From 3fca3cab9b49a2ae4934ddc738839fb8c4534e62 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 02:19:53 +0100 Subject: [PATCH 001/340] add framework and basic application files --- .htaccess | 30 + agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 382 +++++++++++++ agents/bottomlevel/epmty | 0 agents/intermediate/ErrorAgent.inc | 35 ++ agents/toplevel/FaultAgent.inc | 35 ++ agents/toplevel/HtmlAgent.inc | 35 ++ apis/WebApi.inc | 250 +++++++++ app/empty | 0 bootstrap.inc | 33 ++ configs/AppConfig.inc | 75 +++ configs/CoreConfig.inc | 167 ++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 58 ++ controllers/components/empty | 0 core/Agent.inc | 607 +++++++++++++++++++++ core/Api.inc | 163 ++++++ core/Autoloader.inc | 98 ++++ core/ClassLoader.inc | 129 +++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 424 ++++++++++++++ core/Driver.inc | 96 ++++ core/Exception.inc | 65 +++ core/Linker.inc | 322 +++++++++++ core/Logger.inc | 132 +++++ core/Model.inc | 141 +++++ core/Request.inc | 64 +++ core/Response.inc | 158 ++++++ core/View.inc | 124 +++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 167 ++++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 +++ exceptions/AgentNotValidException.inc | 67 +++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 +++ exceptions/ComponentNotValidException.inc | 67 +++ exceptions/ControllerNotFoundException.inc | 67 +++ exceptions/ControllerNotValidException.inc | 67 +++ exceptions/DatamodelException.inc | 99 ++++ exceptions/DriverNotFoundException.inc | 68 +++ exceptions/DriverNotValidException.inc | 68 +++ exceptions/FatalDatamodelException.inc | 96 ++++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 +++ exceptions/LayoutNotValidException.inc | 67 +++ exceptions/ModelNotFoundException.inc | 67 +++ exceptions/ModelNotValidException.inc | 68 +++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ logs/empty | 0 models/DatabaseModel.inc | 83 +++ requests/WebRequest.inc | 401 ++++++++++++++ responses/WebResponse.inc | 250 +++++++++ views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/error/index.tpl | 2 + views/html/html.tpl | 16 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ 72 files changed, 6716 insertions(+) create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/epmty create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/empty create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/components/empty create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 logs/empty create mode 100644 models/DatabaseModel.inc create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php diff --git a/.htaccess b/.htaccess new file mode 100644 index 00000000..e5c5c191 --- /dev/null +++ b/.htaccess @@ -0,0 +1,30 @@ +Allow From All +Options -Indexes -MultiViews + +ErrorDocument 403 /www/error403.html +ErrorDocument 404 /www/error404.html +ErrorDocument 500 /www/error500.html + + + + 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..c545975a --- /dev/null +++ b/agents/ToplevelAgent.inc @@ -0,0 +1,382 @@ + + * @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); + } + + + + + /** + * 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 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 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/epmty b/agents/bottomlevel/epmty new file mode 100644 index 00000000..e69de29b diff --git a/agents/intermediate/ErrorAgent.inc b/agents/intermediate/ErrorAgent.inc new file mode 100644 index 00000000..1067e8e1 --- /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() + { + } + + } + +?> diff --git a/agents/toplevel/FaultAgent.inc b/agents/toplevel/FaultAgent.inc new file mode 100644 index 00000000..0d098fe4 --- /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() + { + } + + } + +?> diff --git a/agents/toplevel/HtmlAgent.inc b/agents/toplevel/HtmlAgent.inc new file mode 100644 index 00000000..e6bc8d61 --- /dev/null +++ b/agents/toplevel/HtmlAgent.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 HTML-page. + * + * @author Oliver Hanraths + */ + class HtmlAgent extends \nre\agents\ToplevelAgent + { + + + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> 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/empty b/app/empty new file mode 100644 index 00000000..e69de29b 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..7524f7b2 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 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( + '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' + ); + + + /** + * Routes + * + * @static + * @var array + */ + public static $routes = array( + //array('', '', '') + ); + + + /** + * Reverse routes + * + * @static + * @var array + */ + public static $reverseRoutes = array( + //array('', '', '') + ); + + } + +?> diff --git a/configs/CoreConfig.inc b/configs/CoreConfig.inc new file mode 100644 index 00000000..a28b6dbc --- /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' => 50, + '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/ErrorController.inc b/controllers/ErrorController.inc new file mode 100644 index 00000000..8cb308b5 --- /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 \nre\core\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..3c33d732 --- /dev/null +++ b/controllers/HtmlController.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\controllers; + + + /** + * Controller of the HtmlAgent to display a HTML-page. + * + * @author Oliver Hanraths + */ + class HtmlController extends \nre\core\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 content-type + $this->response->addHeader("Content-type: text/html; charset=utf-8"); + + // Start session + session_start(); + } + + + /** + * 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')); + } + + } + +?> diff --git a/controllers/components/empty b/controllers/components/empty new file mode 100644 index 00000000..e69de29b 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..55a7675a --- /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 + */ + private $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 + */ + private 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 + */ + private 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..5496d2b9 --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,322 @@ + + * @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; + } + + + + + /** + * Process a title and optional a date to create link parameters. + * + * @param string $title Titel + * @param string $date Date + * @return string Created link parameters + */ + public static function getLinkParams($title, $date=null) + { + // Parameters + $param = ''; + + // Mask special sign seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $title = str_replace($special, rawurlencode(rawurlencode($special)), $title); + } + + // Process title + $param .= str_replace( + ' ', + '-', + substr( + $title, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Process date + if(!empty($date)) { + $param = substr($date, 0, 10).\nre\configs\CoreConfig::$classes['linker']['url']['delimiter'].$param; + } + + + // Mask and return parameters + return array(rawurlencode($param)); + } + + + /** + * Extract date and title from a parameter string. + * + * @param string $dateTitle Parameter string with date and title + * @return array Extracted date and title as associative array + */ + public static function extractDateTitle($dateTitle) + { + // Get delimiter + $delimiter = \nre\configs\CoreConfig::$classes[strtolower(get_class())]['url']['delimiter']; + + // Split + $dateTitle = explode($delimiter, $dateTitle); + if(count($dateTitle) < 4) { + throw new IdNotFoundException(implode($delimiter, $dateTitle)); + } + + // Get parts + $date = urldecode(implode($delimiter, array_slice($dateTitle, 0, 3))); + $title = urldecode(implode($delimiter, array_slice($dateTitle, 3))); + + + // Return date and title + return array( + 'date' => $date, + 'title' => $title + ); + } + + + + + /** + * 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..513bd953 --- /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 + */ + private $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 + */ + private 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..9520fa1e --- /dev/null +++ b/drivers/MysqliDriver.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\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; + } + } + + + return $data; + } + finally { + $stmt->close(); + } + } + + + /** + * 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/logs/empty b/logs/empty new file mode 100644 index 00000000..e69de29b 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/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/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/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..f7e42500 --- /dev/null +++ b/views/html/error/index.tpl @@ -0,0 +1,2 @@ +

Fehler

+

:

diff --git a/views/html/html.tpl b/views/html/html.tpl new file mode 100644 index 00000000..307b772d --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,16 @@ + + + + + + The Legend of Z + + + +

The Legend of Z

+
+ +
+ + + 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/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..2faa32fd --- /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') { + 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'; + +?> From 24f97d7c94e7ce86ce3c210da65347ce22098d04 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 02:34:29 +0100 Subject: [PATCH 002/340] add application Controller --- app/Controller.inc | 85 +++++++++++++++++++++++++++++++++ app/empty | 0 controllers/ErrorController.inc | 2 +- 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 app/Controller.inc delete mode 100644 app/empty diff --git a/app/Controller.inc b/app/Controller.inc new file mode 100644 index 00000000..55509fbb --- /dev/null +++ b/app/Controller.inc @@ -0,0 +1,85 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * 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); + + // Set timezone + date_default_timezone_set(\nre\configs\AppConfig::$app['timeZone']); + } + + + + /** + * 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); + } + + + /** + * Prefilter 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); + + // Set title + $this->set('title', $this->request->getParam(1, 'intermediate')); + } + + } + +?> diff --git a/app/empty b/app/empty deleted file mode 100644 index e69de29b..00000000 diff --git a/controllers/ErrorController.inc b/controllers/ErrorController.inc index 8cb308b5..efdae4f4 100644 --- a/controllers/ErrorController.inc +++ b/controllers/ErrorController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - class ErrorController extends \nre\core\Controller + class ErrorController extends \hhu\z\Controller { From b0a83f4cc9de981f79cd3ff6d0eec515a63e6252 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 02:37:08 +0100 Subject: [PATCH 003/340] add Stylesheet- and CssAgent and add it to HTML-layout --- agents/intermediate/CssAgent.inc | 35 +++++++++ agents/toplevel/StylesheetAgent.inc | 35 +++++++++ configs/AppConfig.inc | 2 +- controllers/CssController.inc | 103 +++++++++++++++++++++++++++ controllers/StylesheetController.inc | 37 ++++++++++ views/html/html.tpl | 2 + views/stylesheet/css/desktop.tpl | 0 views/stylesheet/error/index.tpl | 1 + views/stylesheet/stylesheet.tpl | 4 ++ 9 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 agents/intermediate/CssAgent.inc create mode 100644 agents/toplevel/StylesheetAgent.inc create mode 100644 controllers/CssController.inc create mode 100644 controllers/StylesheetController.inc create mode 100644 views/stylesheet/css/desktop.tpl create mode 100644 views/stylesheet/error/index.tpl create mode 100644 views/stylesheet/stylesheet.tpl diff --git a/agents/intermediate/CssAgent.inc b/agents/intermediate/CssAgent.inc new file mode 100644 index 00000000..8d8de32f --- /dev/null +++ b/agents/intermediate/CssAgent.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 CSS-stylesheets. + * + * @author Oliver Hanraths + */ + class CssAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/agents/toplevel/StylesheetAgent.inc b/agents/toplevel/StylesheetAgent.inc new file mode 100644 index 00000000..07eefea0 --- /dev/null +++ b/agents/toplevel/StylesheetAgent.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 stylesheets. + * + * @author Oliver Hanraths + */ + class StylesheetAgent extends \nre\agents\ToplevelAgent + { + + + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 7524f7b2..599b4cac 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -56,7 +56,7 @@ * @var array */ public static $routes = array( - //array('', '', '') + array('css/?(.*)', 'css/$1?layout=stylesheet', false) ); diff --git a/controllers/CssController.inc b/controllers/CssController.inc new file mode 100644 index 00000000..38e4a474 --- /dev/null +++ b/controllers/CssController.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\controllers; + + + /** + * Controller of the CssAgent to show CSS-stylesheets. + * + * @author Oliver Hanraths + */ + class CssController extends \hhu\z\Controller + { + + + + /** + * 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); + + // Set content-type for CSS-stylesheets + $response->addHeader('Content-type: text/css'); + + // Set expires-header for caching + $response->addHeader("Pragma: public"); + $response->addHeader("Expires: ".gmdate('r', time()+(60*60*24))); + $response->addHeader("Date: ".gmdate(\DateTime::RFC822)); + } + + + /** + * Action: desktop. + * + * Show CSS for desktops. + */ + public function desktop() + { + // Set HTTP-header + $this->setHeader(); + } + + + /** + * Action: adds. + * + * Show additional CSS for desktops. + */ + public function desktop_adds() + { + // Set HTTP-header + $this->setHeader(); + } + + + + + /** + * Set HTTP-header for caching. + */ + private function setHeader() + { + // Determine filename of template + $templateFilename = $this->getView()->getTemplateFilename(); + + // Determine date of last change + $templateLastModified = gmdate('r', filemtime($templateFilename)); + + // Create E-tag + $templateEtag = hash('sha256', $templateLastModified.$templateFilename); + + + // Set header + $this->response->addHeader("Last-Modified: ".$templateLastModified); + $this->response->addHeader("Etag: ".$templateEtag); + // HTTP-status + $headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE'); + $headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH'); + if( + !is_null($headerModifiedSince) && $templateLastModified < strtotime($headerModifiedSince) && + !is_null($headerNoneMatch) && $headerNoneMatch == $templateEtag + ) { + $this->response->setExit(true); + $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304)); + } + } + + } + +?> diff --git a/controllers/StylesheetController.inc b/controllers/StylesheetController.inc new file mode 100644 index 00000000..d9bbfc3d --- /dev/null +++ b/controllers/StylesheetController.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 StylesheetAgent to display stylesheets. + * + * @author Oliver Hanraths + */ + class StylesheetController extends \nre\core\Controller + { + + + + + /** + * Action: index. + * + * Show the stylesheet-structure + */ + public function index() + { + } + + } + +?> diff --git a/views/html/html.tpl b/views/html/html.tpl index 307b772d..6adf05e5 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -4,6 +4,8 @@ The Legend of Z + + diff --git a/views/stylesheet/css/desktop.tpl b/views/stylesheet/css/desktop.tpl new file mode 100644 index 00000000..e69de29b diff --git a/views/stylesheet/error/index.tpl b/views/stylesheet/error/index.tpl new file mode 100644 index 00000000..f6ceec9b --- /dev/null +++ b/views/stylesheet/error/index.tpl @@ -0,0 +1 @@ +Fehler: diff --git a/views/stylesheet/stylesheet.tpl b/views/stylesheet/stylesheet.tpl new file mode 100644 index 00000000..c796c127 --- /dev/null +++ b/views/stylesheet/stylesheet.tpl @@ -0,0 +1,4 @@ +@charset "UTF-8"; + + + From d205864dd4010ba3602310c4f0fe115390b1ef37 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 03:28:18 +0100 Subject: [PATCH 004/340] add IntroductionAgent --- agents/intermediate/IntroductionAgent.inc | 35 +++++++++++++++++++++++ controllers/IntroductionController.inc | 35 +++++++++++++++++++++++ views/html/introduction/index.tpl | 14 +++++++++ 3 files changed, 84 insertions(+) create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 views/html/introduction/index.tpl diff --git a/agents/intermediate/IntroductionAgent.inc b/agents/intermediate/IntroductionAgent.inc new file mode 100644 index 00000000..0ec5e706 --- /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() + { + } + + } + +?> 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/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..2f36cff2 --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,14 @@ +
    +
  • + Oliver Hanraths <oliver.hanraths@uni-duesseldorf.de> +
  • +
  • + Daniel Miskovic <daniel.miskovic@uni-duesseldorf.de> +
  • +
  • + Leitung: Kathrin Knautz, B.A., M.A. <kathrin.knautz@uni-duesseldorf.de> +
  • +
+

+ Heinrich-Heine-Universität Düsseldorf +

From 99a1d525a7994ac12d11e5501c265f7b8683ce31 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 13:12:02 +0100 Subject: [PATCH 005/340] add MenuAgent --- agents/bottomlevel/MenuAgent.inc | 35 ++++++++++++++++++++++++++++++++ agents/bottomlevel/epmty | 0 agents/toplevel/HtmlAgent.inc | 2 ++ controllers/MenuController.inc | 35 ++++++++++++++++++++++++++++++++ views/html/html.tpl | 5 ++++- views/html/menu/index.tpl | 2 ++ 6 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 agents/bottomlevel/MenuAgent.inc delete mode 100644 agents/bottomlevel/epmty create mode 100644 controllers/MenuController.inc create mode 100644 views/html/menu/index.tpl diff --git a/agents/bottomlevel/MenuAgent.inc b/agents/bottomlevel/MenuAgent.inc new file mode 100644 index 00000000..835a8831 --- /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() + { + } + + } + +?> diff --git a/agents/bottomlevel/epmty b/agents/bottomlevel/epmty deleted file mode 100644 index e69de29b..00000000 diff --git a/agents/toplevel/HtmlAgent.inc b/agents/toplevel/HtmlAgent.inc index e6bc8d61..43090112 100644 --- a/agents/toplevel/HtmlAgent.inc +++ b/agents/toplevel/HtmlAgent.inc @@ -28,6 +28,8 @@ */ public function index() { + // Add menu + $this->addSubAgent('Menu'); } } diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc new file mode 100644 index 00000000..51aedd25 --- /dev/null +++ b/controllers/MenuController.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 display a menu. + * + * @author Oliver Hanraths + */ + class MenuController extends \hhu\z\Controller + { + + + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/views/html/html.tpl b/views/html/html.tpl index 6adf05e5..63c9e041 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -9,7 +9,10 @@ -

The Legend of Z

+
+

The Legend of Z

+ +
diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl new file mode 100644 index 00000000..88d03d48 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,2 @@ + From 978ce5156d6e66210ddf0bf39be0a5a8ab4b8ee9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 13:13:32 +0100 Subject: [PATCH 006/340] correct parameters for Agent-actions --- agents/bottomlevel/MenuAgent.inc | 2 +- agents/intermediate/CssAgent.inc | 2 +- agents/intermediate/ErrorAgent.inc | 2 +- agents/intermediate/IntroductionAgent.inc | 2 +- agents/toplevel/FaultAgent.inc | 2 +- agents/toplevel/HtmlAgent.inc | 2 +- agents/toplevel/StylesheetAgent.inc | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/agents/bottomlevel/MenuAgent.inc b/agents/bottomlevel/MenuAgent.inc index 835a8831..77d60d8d 100644 --- a/agents/bottomlevel/MenuAgent.inc +++ b/agents/bottomlevel/MenuAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { } diff --git a/agents/intermediate/CssAgent.inc b/agents/intermediate/CssAgent.inc index 8d8de32f..860a75a5 100644 --- a/agents/intermediate/CssAgent.inc +++ b/agents/intermediate/CssAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { } diff --git a/agents/intermediate/ErrorAgent.inc b/agents/intermediate/ErrorAgent.inc index 1067e8e1..85bb6f95 100644 --- a/agents/intermediate/ErrorAgent.inc +++ b/agents/intermediate/ErrorAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { } diff --git a/agents/intermediate/IntroductionAgent.inc b/agents/intermediate/IntroductionAgent.inc index 0ec5e706..3a06fdff 100644 --- a/agents/intermediate/IntroductionAgent.inc +++ b/agents/intermediate/IntroductionAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { } diff --git a/agents/toplevel/FaultAgent.inc b/agents/toplevel/FaultAgent.inc index 0d098fe4..1ceb682e 100644 --- a/agents/toplevel/FaultAgent.inc +++ b/agents/toplevel/FaultAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { } diff --git a/agents/toplevel/HtmlAgent.inc b/agents/toplevel/HtmlAgent.inc index 43090112..e131fdf2 100644 --- a/agents/toplevel/HtmlAgent.inc +++ b/agents/toplevel/HtmlAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { // Add menu $this->addSubAgent('Menu'); diff --git a/agents/toplevel/StylesheetAgent.inc b/agents/toplevel/StylesheetAgent.inc index 07eefea0..6231b066 100644 --- a/agents/toplevel/StylesheetAgent.inc +++ b/agents/toplevel/StylesheetAgent.inc @@ -26,7 +26,7 @@ /** * Action: index. */ - public function index() + public function index(\nre\core\Request $request, \nre\core\Response $response) { } From f618a2869a6df006912ec4a16a848b6f565ada11 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 16:22:43 +0100 Subject: [PATCH 007/340] create application model --- app/Model.inc | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 app/Model.inc 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); + } + + } + +?> From 1e426803673240009c725f3cd8cb680748a349a0 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 16:22:56 +0100 Subject: [PATCH 008/340] create application Utils --- app/Utils.inc | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 app/Utils.inc diff --git a/app/Utils.inc b/app/Utils.inc new file mode 100644 index 00000000..3ef93670 --- /dev/null +++ b/app/Utils.inc @@ -0,0 +1,32 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + + /** + * Format for printing the date + * + * @var string + */ + const DATEFORMAT = 'm.d.Y'; + + } + +?> From fed2fec0c9ad0cb88dda7ed01ce91b9225e1940c Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 16:25:26 +0100 Subject: [PATCH 009/340] change link parameter masking of framework --- configs/CoreConfig.inc | 2 +- core/Linker.inc | 89 ++++++++++++++++++------------------------ 2 files changed, 40 insertions(+), 51 deletions(-) diff --git a/configs/CoreConfig.inc b/configs/CoreConfig.inc index a28b6dbc..45bc7e4a 100644 --- a/configs/CoreConfig.inc +++ b/configs/CoreConfig.inc @@ -103,7 +103,7 @@ public static $classes = array( 'linker' => array( 'url' => array( - 'length' => 50, + 'length' => 128, 'delimiter' => '-' ) ) diff --git a/core/Linker.inc b/core/Linker.inc index 5496d2b9..f5b30b13 100644 --- a/core/Linker.inc +++ b/core/Linker.inc @@ -43,72 +43,61 @@ /** - * Process a title and optional a date to create link parameters. + * Mask parameters to be used in an URL. * - * @param string $title Titel - * @param string $date Date - * @return string Created link parameters + * @param string $param1 First parameter + * @return string Masked parameters as string */ - public static function getLinkParams($title, $date=null) + public static function createLinkParam($param1) { - // Parameters - $param = ''; - - // Mask special sign seperately - $specials = array('/', '?', '&'); - foreach($specials as &$special) { - $title = str_replace($special, rawurlencode(rawurlencode($special)), $title); - } - - // Process title - $param .= str_replace( - ' ', - '-', - substr( - $title, - 0, - \nre\configs\CoreConfig::$classes['linker']['url']['length'] + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + Utils::createLinkParams, + func_get_args() ) ); - - // Process date - if(!empty($date)) { - $param = substr($date, 0, 10).\nre\configs\CoreConfig::$classes['linker']['url']['delimiter'].$param; - } - - - // Mask and return parameters - return array(rawurlencode($param)); } /** - * Extract date and title from a parameter string. + * Mask parameters to be used in an URL. * - * @param string $dateTitle Parameter string with date and title - * @return array Extracted date and title as associative array + * @param string $param1 First parameter + * @return string Masked parameters as array */ - public static function extractDateTitle($dateTitle) + public static function createLinkParams($param1) { - // Get delimiter - $delimiter = \nre\configs\CoreConfig::$classes[strtolower(get_class())]['url']['delimiter']; + // Parameters + $linkParams = array(); + $params = func_get_args(); - // Split - $dateTitle = explode($delimiter, $dateTitle); - if(count($dateTitle) < 4) { - throw new IdNotFoundException(implode($delimiter, $dateTitle)); + 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( + $title, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); } - - // Get parts - $date = urldecode(implode($delimiter, array_slice($dateTitle, 0, 3))); - $title = urldecode(implode($delimiter, array_slice($dateTitle, 3))); - // Return date and title - return array( - 'date' => $date, - 'title' => $title - ); + // Return link parameters + return $linkParams; } From 150aa2d1b559df2b24c1f9e6d6ba7a38e862e3fd Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 16:26:49 +0100 Subject: [PATCH 010/340] add basic Users- and SeminariesAgent --- agents/intermediate/SeminariesAgent.inc | 35 ++++++++ agents/intermediate/UsersAgent.inc | 35 ++++++++ configs/AppConfig.inc | 21 ++++- controllers/SeminariesController.inc | 75 +++++++++++++++++ controllers/UsersController.inc | 61 ++++++++++++++ models/SeminariesModel.inc | 104 +++++++++++++++++++++++ models/UsersModel.inc | 105 ++++++++++++++++++++++++ views/html/html.tpl | 5 +- views/html/menu/index.tpl | 6 +- views/html/seminaries/index.tpl | 10 +++ views/html/seminaries/seminary.tpl | 4 + views/html/users/index.tpl | 10 +++ views/html/users/user.tpl | 4 + 13 files changed, 470 insertions(+), 5 deletions(-) create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/user.tpl 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..7b929586 --- /dev/null +++ b/agents/intermediate/UsersAgent.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 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) + { + } + + } + +?> diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 599b4cac..8f5ab347 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -56,7 +56,9 @@ * @var array */ public static $routes = array( - array('css/?(.*)', 'css/$1?layout=stylesheet', false) + array('css/?(.*)', 'css/$1?layout=stylesheet', false), + array('users/(.+)', 'users/user/$1', false), + array('seminaries/(.+)', 'seminaries/seminary/$1', false) ); @@ -67,7 +69,22 @@ * @var array */ public static $reverseRoutes = array( - //array('', '', '') + array('users/user/(.*)', 'users/$1', false), + array('seminaries/seminary/(.*)', 'seminaries/$1', false) + ); + + + /** + * Database connection settings + * + * @static + * @var array + */ + public static $database = array( + 'user' => 'z', + 'host' => 'localhost', + 'password' => 'legendofZ', + 'db' => 'z' ); } diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc new file mode 100644 index 00000000..85867cbb --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'users'); + + + + + /** + * 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); + + + // Pass data to view + $this->set('seminary', $seminary); + } + + } + +?> diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc new file mode 100644 index 00000000..0a3ce645 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,61 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + + + + + /** + * 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); + + + // Pass data to view + $this->set('user', $user); + } + + + } + +?> diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc new file mode 100644 index 00000000..c4ae7954 --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,104 @@ + + * @copyright 2014 Heinrich-Heine-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 '. + '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 '. + '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 '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + } + +?> diff --git a/models/UsersModel.inc b/models/UsersModel.inc new file mode 100644 index 00000000..2a9bb6a3 --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,105 @@ + + * @copyright 2014 Heinrich-Heine-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]; + } + + } + +?> diff --git a/views/html/html.tpl b/views/html/html.tpl index 63c9e041..404d0edc 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -11,9 +11,12 @@

The Legend of Z

- +
+

diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 88d03d48..d7c91883 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,2 +1,4 @@ - + +
  • ">Users
  • +
  • ">Seminaries
  • +
    diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..267a561c --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,10 @@ +
      + +
    • +

      +
      + erstellt von +
      +
    • + +
    diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl new file mode 100644 index 00000000..5566aba2 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,4 @@ +

    +

    + erstellt am +

    diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..347743bb --- /dev/null +++ b/views/html/users/index.tpl @@ -0,0 +1,10 @@ +
      + +
    • +

      +
      + registriert seit +
      +
    • + +
    diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..f89d38b7 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,4 @@ +

    +

    + registriert seit +

    From 22618f7985ac4ca935ea138bd4bfac0fa0abac59 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 19 Jan 2014 13:38:40 +0100 Subject: [PATCH 011/340] update introduction page a little --- views/html/introduction/index.tpl | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index 2f36cff2..b7633b2f 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -1,14 +1,20 @@ +

    Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.

    +

    Entwickler:

    • - Oliver Hanraths <oliver.hanraths@uni-duesseldorf.de> + Oliver Hanraths <oliver(DOT)hanraths(AT)uni-duesseldorf(DOT)de>
      + Programmierung und Datenbank
    • - Daniel Miskovic <daniel.miskovic@uni-duesseldorf.de> + Daniel Miskovic <daniel(DOT)miskovic(AT)uni-duesseldorf(DOT)de>
      + GUI und Webdesign
    • - Leitung: Kathrin Knautz, B.A., M.A. <kathrin.knautz@uni-duesseldorf.de> + Kathrin Knautz, B.A., M.A. <kathrin(DOT)knautz(AT)uni-duesseldorf(DOT)de>
      + Leitung
    +

    Heinrich-Heine-Universität Düsseldorf

    From df074f5a01edc6f65d4dc7a70ecf2a740f1a52cf Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 19 Jan 2014 13:38:59 +0100 Subject: [PATCH 012/340] link css-file and headline correctly --- views/html/html.tpl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 404d0edc..9401a5e5 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -5,12 +5,12 @@ The Legend of Z - +
    -

    The Legend of Z

    +

    The Legend of Z

    From da7cda02f95893e33f46e131f74494bd43f53fa1 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 19 Jan 2014 23:03:55 +0100 Subject: [PATCH 013/340] add localization and move timezone selection to the right place --- agents/toplevel/HtmlAgent.inc | 32 +++++++++++++- app/Controller.inc | 3 -- app/ToplevelAgent.inc | 36 ++++++++++++++++ configs/AppConfig.inc | 15 ++++++- controllers/SeminariesController.inc | 3 ++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 744 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 45 ++++++++++++++++++++ views/html/error/index.tpl | 2 +- views/html/html.tpl | 1 - views/html/introduction/index.tpl | 1 + views/html/menu/index.tpl | 4 +- views/html/seminaries/index.tpl | 3 +- views/html/seminaries/seminary.tpl | 3 +- views/html/users/index.tpl | 3 +- views/html/users/user.tpl | 3 +- 15 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 app/ToplevelAgent.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po diff --git a/agents/toplevel/HtmlAgent.inc b/agents/toplevel/HtmlAgent.inc index e131fdf2..63ed4525 100644 --- a/agents/toplevel/HtmlAgent.inc +++ b/agents/toplevel/HtmlAgent.inc @@ -17,12 +17,21 @@ * * @author Oliver Hanraths */ - class HtmlAgent extends \nre\agents\ToplevelAgent + 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. */ @@ -32,6 +41,27 @@ $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/app/Controller.inc b/app/Controller.inc index 55509fbb..4403407b 100644 --- a/app/Controller.inc +++ b/app/Controller.inc @@ -44,9 +44,6 @@ public function __construct($layoutName, $action, $agent) { parent::__construct($layoutName, $action, $agent); - - // Set timezone - date_default_timezone_set(\nre\configs\AppConfig::$app['timeZone']); } 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/configs/AppConfig.inc b/configs/AppConfig.inc index 8f5ab347..3467c3be 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -30,6 +30,7 @@ * @var array */ public static $app = array( + 'name' => 'The Legend of Z', 'namespace' => 'hhu\\z\\', 'timeZone' => 'Europe/Berlin' ); @@ -45,7 +46,19 @@ 'toplevel' => 'html', 'toplevel-error' => 'fault', 'intermediate' => 'introduction', - 'intermediate-error' => 'error' + 'intermediate-error' => 'error', + 'language' => 'de_DE.utf8' + ); + + + /** + * Directories + * + * @static + * @var array + */ + public static $dirs = array( + 'locale' => 'locale' ); diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 85867cbb..4f026f41 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -65,6 +65,9 @@ // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + // Pass data to view $this->set('seminary', $seminary); diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..ec51389a69bdbf4ffb9b427614d53912f88b5c08 GIT binary patch literal 744 zcmZvaPm9w)7{*6k{}2^K@wSKYASg23r0UX5>1DgE6uVe#R>X_UCU29Gbf(Nqs_SR) z>e-tgz|#tXh*$4>((m9W&?gOA55DA=Xa2nJGjEvuxVrI{A=_s}~31M~v)7O+KD_=IS)>t;u-kPjRy)v3jX|A-iiX1y|WF1Q_NlzI+JmNPUHyUqdDOb*u z#WG4J*1Vw9i%}+?paL#2PYU8=%1I+*#-9a~M~UdyPS_QDFpPOSjP8jr644HCx8wWU zcfu$PktiP1Qq?a1&+9aKW9hs|tkkZQ-q@H2W2y5><|Go5^MB?s&*=HyAlM)657rGv ztuW{t?McJtXn}q_z4BX&QYzi$X(6p6-#bW#qO;DckDHMd1D%>o={)A0L*;{~;sI&0 zO&=3OnesepZMW`Xr1ZK+5%X3sDJv^WF*J7WVy+jfsq3}7yczaxX?~-}qubqe z{nS)8rGC>^%$vUcx=%^kw3xtOggBwK)jUf@FI~WfRFq`dKFehler +

    :

    diff --git a/views/html/html.tpl b/views/html/html.tpl index 9401a5e5..0f46493c 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -16,7 +16,6 @@
    -

    diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index b7633b2f..1d3fa615 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -1,3 +1,4 @@ +

    Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.

    Entwickler:

      diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index d7c91883..ad0fbe39 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,4 +1,4 @@ -
    • ">Users
    • -
    • ">Seminaries
    • +
    • ">
    • +
    • ">
    • diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index 267a561c..037f718e 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -1,9 +1,10 @@ +

      • - erstellt von +
      • diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 5566aba2..094fbf3b 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,4 +1,5 @@ +

        - erstellt am +

        diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl index 347743bb..3b94e6a0 100644 --- a/views/html/users/index.tpl +++ b/views/html/users/index.tpl @@ -1,9 +1,10 @@ +

        • - registriert seit +
        • diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index f89d38b7..91591dd3 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -1,4 +1,5 @@ +

          - registriert seit +

          From 63766773a062a8371775b0237a899c5db0486b0a Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 22 Jan 2014 16:28:32 +0100 Subject: [PATCH 014/340] create AuthComponent with basic functionality --- controllers/components/AuthComponent.inc | 80 ++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 controllers/components/AuthComponent.inc diff --git a/controllers/components/AuthComponent.inc b/controllers/components/AuthComponent.inc new file mode 100644 index 00000000..fe0cb139 --- /dev/null +++ b/controllers/components/AuthComponent.inc @@ -0,0 +1,80 @@ + + * @copyright 2014 Heinrich-Heine-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(); + session_regenerate_id(true); + } + } + + + + + /** + * 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; + } + + } + +?> From 011c9d8ed5653308c1e76a9ff4f8ed5fdd5e10a4 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 22 Jan 2014 16:31:25 +0100 Subject: [PATCH 015/340] integrate AuthComponent and implement CRUD for users --- agents/ToplevelAgent.inc | 2 + app/Controller.inc | 65 ++++++- configs/AppConfig.inc | 10 +- controllers/HtmlController.inc | 8 +- controllers/SeminariesController.inc | 9 + controllers/UsersController.inc | 160 ++++++++++++++++++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 744 -> 1407 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 75 +++++++- models/UsersModel.inc | 132 +++++++++++++++ views/html/html.tpl | 5 + views/html/menu/index.tpl | 5 + views/html/users/create.tpl | 14 ++ views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 14 ++ views/html/users/index.tpl | 3 + views/html/users/login.tpl | 12 ++ .../empty => views/html/users/logout.tpl | 0 views/html/users/user.tpl | 3 + 18 files changed, 509 insertions(+), 16 deletions(-) create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/login.tpl rename controllers/components/empty => views/html/users/logout.tpl (100%) diff --git a/agents/ToplevelAgent.inc b/agents/ToplevelAgent.inc index c545975a..eee33450 100644 --- a/agents/ToplevelAgent.inc +++ b/agents/ToplevelAgent.inc @@ -265,6 +265,7 @@ /** * Run the Controller of this Agent and its SubAgents. * + * @throws AccessDeniedException * @throws IdNotFoundException * @throws ServiceUnavailableException * @throws DatamodelException @@ -292,6 +293,7 @@ /** * Run IntermediateAgent. * + * @throws AccessDeniedException * @throws ParamsNotValidException * @throws IdNotFoundException * @throws ServiceUnavailableException diff --git a/app/Controller.inc b/app/Controller.inc index 4403407b..24301a88 100644 --- a/app/Controller.inc +++ b/app/Controller.inc @@ -19,12 +19,30 @@ */ abstract class Controller extends \nre\core\Controller { + /** + * Required components + * + * @var array + */ + public $components = array('auth'); + /** + * Required models + * + * @var array + */ + public $models = array('users'); /** * Linker instance * * @var Linker */ protected $linker = null; + /** + * Data of currently logged in user if any + * + * @var array + */ + protected static $user = null; @@ -58,13 +76,19 @@ { parent::preFilter($request, $response); + // Check rights + $this->checkPermission(); + // Create linker $this->linker = new \nre\core\Linker($this->request); + + // Set userdata + $this->set('loggedUser', static::$user); } /** - * Prefilter that is executed after running the Controller. + * Postfilter that is executed after running the Controller. * * @param Request $request Current request * @param Response $response Current response @@ -72,9 +96,44 @@ public function postFilter(\nre\core\Request $request, \nre\core\Response $response) { parent::postFilter($request, $response); + } + + + + + /** + * Check user permissions. + * + * @throws AccessDeniedException + */ + private function checkPermission() + { + // Determine user + try { + $userId = $this->Auth->getUserId(); + if(!is_null($userId)) { + static::$user = $this->Users->getUserById($this->Auth->getUserId()); + } + } + catch(\nre\exceptions\IdNotFoundException $e) { + } - // Set title - $this->set('title', $this->request->getParam(1, 'intermediate')); + + // Determine permissions + $action = $this->request->getParam(2, 'action'); + if(!property_exists($this, 'permissions')) { + return; // Allow if nothing is specified + } + if(!array_key_exists($action, $this->permissions)) { + return; // Allow if Action is not specified + } + $permissions = $this->permissions[$action]; + + + // Check permissions + if(is_null(static::$user)) { + throw new \nre\exceptions\AccessDeniedException(); + } } } diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 3467c3be..c4ef5929 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -69,9 +69,10 @@ * @var array */ public static $routes = array( - array('css/?(.*)', 'css/$1?layout=stylesheet', false), - array('users/(.+)', 'users/user/$1', false), - array('seminaries/(.+)', 'seminaries/seminary/$1', false) + array('css/?(.*)', 'css/$1?layout=stylesheet', false), + array('users/([^/]+)/(edit|delete)', 'users/$2/$1', true), + array('users/(?!(index|login|logout|create|edit|delete))', 'users/user/$1', true), + array('seminaries/(.+)', 'seminaries/seminary/$1', false) ); @@ -82,7 +83,8 @@ * @var array */ public static $reverseRoutes = array( - array('users/user/(.*)', 'users/$1', false), + array('users/user/(.*)', 'users/$1', true), + array('users/([^/]+)/(.*)', 'users/$2/$1', true), array('seminaries/seminary/(.*)', 'seminaries/$1', false) ); diff --git a/controllers/HtmlController.inc b/controllers/HtmlController.inc index 3c33d732..a2659898 100644 --- a/controllers/HtmlController.inc +++ b/controllers/HtmlController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - class HtmlController extends \nre\core\Controller + class HtmlController extends \hhu\z\Controller { @@ -36,9 +36,6 @@ // Set content-type $this->response->addHeader("Content-type: text/html; charset=utf-8"); - - // Start session - session_start(); } @@ -51,6 +48,9 @@ { // 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); } } diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 4f026f41..4a9581bf 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -25,6 +25,15 @@ * @var array */ public $models = array('seminaries', 'users'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array(), + 'seminary' => array() + ); diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 0a3ce645..aea5ffbb 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -19,6 +19,19 @@ */ class UsersController extends \hhu\z\Controller { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array(), + 'user' => array(), + 'create' => array(), + 'edit' => array(), + 'delete' => array() + ); + @@ -56,6 +69,153 @@ } + /** + * Action: login. + * + * Log in a user. + */ + public function login() + { + $username = ''; + + // Log the user in + if($this->request->getRequestMethod() == 'POST' && !empty($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(!empty($this->request->getPostParam('save'))) + { + // Edit user + $this->Users->editUser( + $user['id'], + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + } + + + // Redirect to user + $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($this->request->getPostParam('delete') == '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/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index ec51389a69bdbf4ffb9b427614d53912f88b5c08..a8dd7d2c500d170581bcd7b63409bb2c2e4438c8 100644 GIT binary patch literal 1407 zcmZvbJ!lj`6vxMJJ-@#|P+=?r5!v(Z6p0&xXmXKYqK9{ih#+&jFSo06vted7XKYL( zHZ~$weu0ghjbIh95IYO41;Jn?SX&AHXLoZK#NlRt^XB8d|C@RDZO{5A3~e9!A@n8m z!{|5G;Dh!V+zEaGw}RinE#NP31Na-<1^xlIfjidb^9R9=u#bS7!Qp~cumsx&D`35t zp9h`4d!Rdazu-e~FYL#K{Tkd3`wjRKF_*vtuy3qqY&V#KZvO+&`FRRDeXl^L|1Id^ zyf4;2f;(V;0-esU#rhA>hy4?Dc@5!^Td#pG|IvbVa6jxz#X5m5o&q5+YtD9UQ;tlR z^Wpr#eWustw9?#t<9PNaZqCJD=LSV{_vdm!>REI8UG96FHLgim;);4aHKf@DNrIh? z>tSq*k=BW3_2tD!Ga{DW*)Y^(44YC`Ct(`cI8kgm>BQMLNiDm$;;$i$=_NX3P3p!< z=$H&^MoB6|ZlgR7UtWARWR@47^Wh z1w!6l;6sKd3SX9=WSTm$v7{-)JZtFW76sOu3cahO4K(|_1r5{GA%z{Y{CcTzspVx? zf>mzsG&40*9|)|LE2Z&7SyFJbFb6%B?%3mVQp9SM2a(W*?AVpo zq&G6y=fcgB_UbA~!dP{DKGKeD>6&+*6zNKkuhF9IyyMtOi~@tcrMH5xvswVCJ7xx4fyveh)_PB z|K?-rnG$LyCB@H?O6^_J98Y9PN>Z#pB|p3~L9}|N4UcV#aXoC3B8dWCRiv&)iPd8@ z`(Y{4sp|Y&qbb6yIA=wrT6Q7Dow8;kC0=B(0cHBamFcs*Onb4uE#n~K9m-`{-Xk57 qI@8yim2BU?XtyIaz>Bg^Nry5*wls<)qJUcdOyXv+<IEh#Qz!{_}zaQcII{veg&P+nc@whMqvStU>vT&R}TVsgb8?o5Z=M} z`!EY1FbUsqn1HRA}3!_a;S5-MqmZLa{{_a&CN`)l1xW(-=7OH7;`u5zG%(Q4H a6U(&0CUaLT-Q8e*diXayNu08*9sK~{9Zhxs diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index e216dbb5..0f2a598c 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-01-19 22:56+0100\n" -"PO-Revision-Date: 2014-01-19 22:58+0100\n" +"POT-Creation-Date: 2014-01-22 16:28+0100\n" +"PO-Revision-Date: 2014-01-22 16:29+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -15,8 +15,10 @@ msgstr "" "X-Poedit-SourceCharset: UTF-8\n" "X-Poedit-SearchPath-0: ../../../views\n" -#: ../../../views/html/menu/index.tpl:2 ../../../views/html/users/user.tpl:1 -#: ../../../views/html/users/index.tpl:1 +#: ../../../views/html/menu/index.tpl:2 ../../../views/html/users/login.tpl:1 +#: ../../../views/html/users/user.tpl:1 ../../../views/html/users/index.tpl:1 +#: ../../../views/html/users/edit.tpl:1 ../../../views/html/users/delete.tpl:1 +#: ../../../views/html/users/create.tpl:1 msgid "Users" msgstr "Benutzer" @@ -26,10 +28,73 @@ msgstr "Benutzer" msgid "Seminaries" msgstr "Kurse" -#: ../../../views/html/users/user.tpl:4 ../../../views/html/users/index.tpl:7 +#: ../../../views/html/menu/index.tpl:5 ../../../views/html/users/login.tpl:2 +#: ../../../views/html/users/login.tpl:11 +msgid "Login" +msgstr "Login" + +#: ../../../views/html/menu/index.tpl:7 +msgid "Logout" +msgstr "Logout" + +#: ../../../views/html/users/login.tpl:6 ../../../views/html/users/login.tpl:7 +#: ../../../views/html/users/edit.tpl:6 ../../../views/html/users/edit.tpl:7 +#: ../../../views/html/users/create.tpl:6 +#: ../../../views/html/users/create.tpl:7 +msgid "Username" +msgstr "Benutzername" + +#: ../../../views/html/users/login.tpl:8 ../../../views/html/users/login.tpl:9 +#: ../../../views/html/users/edit.tpl:10 ../../../views/html/users/edit.tpl:11 +#: ../../../views/html/users/create.tpl:10 +#: ../../../views/html/users/create.tpl:11 +msgid "Password" +msgstr "Passwort" + +#: ../../../views/html/users/user.tpl:4 ../../../views/html/users/delete.tpl:2 +msgid "Delete user" +msgstr "Benutzer löschen" + +#: ../../../views/html/users/user.tpl:7 ../../../views/html/users/index.tpl:10 msgid "registered on" msgstr "registriert seit" +#: ../../../views/html/users/index.tpl:3 +msgid "Create new user" +msgstr "Neuen Benutzer erstellen" + +#: ../../../views/html/users/edit.tpl:2 +msgid "Edit user" +msgstr "Benutzer bearbeiten" + +#: ../../../views/html/users/edit.tpl:8 ../../../views/html/users/edit.tpl:9 +#: ../../../views/html/users/create.tpl:8 +#: ../../../views/html/users/create.tpl:9 +msgid "E‑Mail-Address" +msgstr "E‑Mail-Adresse" + +#: ../../../views/html/users/edit.tpl:13 +#: ../../../views/html/users/create.tpl:13 +msgid "create" +msgstr "erstellen" + +#: ../../../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/delete.tpl:6 +msgid "delete" +msgstr "löschen" + +#: ../../../views/html/users/delete.tpl:7 +msgid "cancel" +msgstr "abbrechen" + +#: ../../../views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + #: ../../../views/html/error/index.tpl:1 msgid "Error" msgstr "Fehler" diff --git a/models/UsersModel.inc b/models/UsersModel.inc index 2a9bb6a3..576bc1a2 100644 --- a/models/UsersModel.inc +++ b/models/UsersModel.inc @@ -100,6 +100,138 @@ 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 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 = ?, email = ? '. + 'WHERE id = ?', + 'ssi', + $sername, $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(); + throw $e; + } + finally { + $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 + */ + private function hash($password) + { + 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) + { + return password_verify($password, $hash); + } + } ?> diff --git a/views/html/html.tpl b/views/html/html.tpl index 0f46493c..a9f5c842 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -14,6 +14,11 @@ + +
          + +
          +
          diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index ad0fbe39..f9f8246c 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,4 +1,9 @@
        • ">
        • ">
        • + +
        • + +
        • +
          diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl new file mode 100644 index 00000000..caffac41 --- /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..10f280ab --- /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..d3d1c2e0 --- /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 index 3b94e6a0..9a5b525e 100644 --- a/views/html/users/index.tpl +++ b/views/html/users/index.tpl @@ -1,4 +1,7 @@

          +
          • diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl new file mode 100644 index 00000000..36c13da4 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,12 @@ +

            +

            + +
            +
            + +
            + +
            +
            + +
            diff --git a/controllers/components/empty b/views/html/users/logout.tpl similarity index 100% rename from controllers/components/empty rename to views/html/users/logout.tpl diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 91591dd3..1e8466b5 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -1,5 +1,8 @@

            +

            From 34e304ded1815ef0be70bddfed735e5a4e538d78 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 22 Jan 2014 16:31:49 +0100 Subject: [PATCH 016/340] correct little mistakes in Linker-class --- core/Linker.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Linker.inc b/core/Linker.inc index f5b30b13..00c5846b 100644 --- a/core/Linker.inc +++ b/core/Linker.inc @@ -53,7 +53,7 @@ return implode( \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], call_user_func_array( - Utils::createLinkParams, + '\nre\core\Linker::createLinkParams', func_get_args() ) ); @@ -85,7 +85,7 @@ ' ', \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], substr( - $title, + $param, 0, \nre\configs\CoreConfig::$classes['linker']['url']['length'] ) From 3bebc9a35fba318c3543dca0c3387569d8893226 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 25 Jan 2014 20:22:54 +0100 Subject: [PATCH 017/340] add IntlDateFormatter for formatting locale-dependent date and time --- app/Controller.inc | 14 +++++++++ app/Utils.inc | 8 ----- configs/AppConfig.inc | 3 +- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 1407 -> 1544 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 32 +++++++++++++++++--- views/html/seminaries/index.tpl | 2 +- views/html/seminaries/seminary.tpl | 2 +- views/html/users/index.tpl | 2 +- views/html/users/user.tpl | 2 +- 9 files changed, 47 insertions(+), 18 deletions(-) diff --git a/app/Controller.inc b/app/Controller.inc index 24301a88..5594ac31 100644 --- a/app/Controller.inc +++ b/app/Controller.inc @@ -82,6 +82,20 @@ // 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 + )); + // Set userdata $this->set('loggedUser', static::$user); } diff --git a/app/Utils.inc b/app/Utils.inc index 3ef93670..1e3527c1 100644 --- a/app/Utils.inc +++ b/app/Utils.inc @@ -19,14 +19,6 @@ */ class Utils { - - /** - * Format for printing the date - * - * @var string - */ - const DATEFORMAT = 'm.d.Y'; - } ?> diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index c4ef5929..12624ab0 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -47,7 +47,8 @@ 'toplevel-error' => 'fault', 'intermediate' => 'introduction', 'intermediate-error' => 'error', - 'language' => 'de_DE.utf8' + 'language' => 'de_DE.utf8', + 'locale' => 'de-DE' ); diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index a8dd7d2c500d170581bcd7b63409bb2c2e4438c8..cb455ec186be0d5ee691003275f56b08f9b65462 100644 GIT binary patch delta 654 zcmYk3J4gdT5QaCo%hM!ke1ne@6hRR}K*eAq0YL<9#3E{uVwJ!pMIHpJU?*aC*lKBI zWv5_cDWafamUEG*S;l8ZRp?6tx%P>g1-(*c4G6}Qbzyv&ja^ZdRfO>Z0 zI+TlEUA{pP_UXocqbVQ?PZ2*1yV<`smFkC&uoJ#R`R^P0RZXcl*+lsOl*%Td2$*G% zgbPpvreO@OLaAUI@~It$6tvy=3`z&ip`5#f(y1Glcd&=}MfS=hn=~HQIK@d>j63o{Q8k!JA11 delta 531 zcmXxhD@X)E9LMpweR$`2o_Z<>-hmecqqo7wnhaYkhSPR+gGB@%bpaCta_jPI93{WS zOE&G}40$IoW&#gU-yfsaxj-H83ROT4wa;BXo*8pbr^<^5)Jb362Ok)df1)azXO_kz z)D5gT6PzaBbmJ*%!(HTjc_>#D2jmCS8l^T5*3VC@s{TV)&iwqh=$#JWtK&jfsA?!a z4;7&5YcyN;I=1U=*k>=X4S&5D&9#=|XvL;})Ad(-XZ~`*z65)AD{R}Fa68;jcMi-S Di

            - + format(new \DateTime($seminary['created'])))?>
          • diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 094fbf3b..3c5f1aba 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,5 +1,5 @@

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

            diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl index 9a5b525e..5d2883af 100644 --- a/views/html/users/index.tpl +++ b/views/html/users/index.tpl @@ -7,7 +7,7 @@
          • - + format(new \DateTime($user['created'])))?>
          • diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 1e8466b5..e3a037af 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -4,5 +4,5 @@
          • - + format(new \DateTime($user['created'])))?>

            From 297c56e29d75506ef00b4bc01641cfed102f1d60 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 25 Jan 2014 20:55:10 +0100 Subject: [PATCH 018/340] implement CRUD for seminaries and correct typos for users --- configs/AppConfig.inc | 3 +- controllers/SeminariesController.inc | 97 ++++++++++++++++++++ controllers/UsersController.inc | 5 +- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 1544 -> 1805 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 57 ++++++++---- models/SeminariesModel.inc | 57 ++++++++++++ models/UsersModel.inc | 11 ++- views/html/seminaries/create.tpl | 10 ++ views/html/seminaries/delete.tpl | 8 ++ views/html/seminaries/edit.tpl | 10 ++ views/html/seminaries/index.tpl | 3 + views/html/seminaries/seminary.tpl | 4 + views/html/users/create.tpl | 2 +- views/html/users/edit.tpl | 6 +- views/html/users/user.tpl | 1 + 15 files changed, 247 insertions(+), 27 deletions(-) create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 12624ab0..aeafd57e 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -73,7 +73,8 @@ array('css/?(.*)', 'css/$1?layout=stylesheet', false), array('users/([^/]+)/(edit|delete)', 'users/$2/$1', true), array('users/(?!(index|login|logout|create|edit|delete))', 'users/user/$1', true), - array('seminaries/(.+)', 'seminaries/seminary/$1', false) + array('seminaries/([^/]+)/(edit|delete)', 'seminaries/$2/$1', true), + array('seminaries/(?!(index|create|edit|delete))', 'seminaries/seminary/$1', true) ); diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 4a9581bf..7e307087 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -82,6 +82,103 @@ $this->set('seminary', $seminary); } + + /** + * 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(!empty($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(!empty($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/UsersController.inc b/controllers/UsersController.inc index aea5ffbb..d8b44ccd 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -167,10 +167,11 @@ $this->request->getPostParam('email'), $this->request->getPostParam('password') ); + $user = $this->Users->getUserById($user['id']); } - // Redirect to user + // Redirect to entry $this->redirect($this->linker->link(array($user['url']), 1)); } @@ -197,7 +198,7 @@ if($this->request->getRequestMethod() == 'POST') { // Check confirmation - if($this->request->getPostParam('delete') == 'delete') + if(!empty($this->request->getPostParam('delete'))) { // Delete user $this->Users->deleteUser($user['id']); diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index cb455ec186be0d5ee691003275f56b08f9b65462..621bc112b8c5dacdd562cb787b33290e193e8a09 100644 GIT binary patch delta 801 zcmYMx&r1|x7{KvoXLc=hwX#+FVK78`3(XM1iy-EuzaeV2eW`(QshOQXgLZ`iDLlj= zbyyVzp+nF?EV|U8pn?w3HR`ZkqFWSn@L=C(dY4}Ic|ZHU@67YQ&pS)S<=*<&zWj>N zjuS(~Z(^AEkfWnrc0>-Nj|VWZ-ob zpZ!eyv5G6w#kdnkv4mqdgPJ%&eg2jm-$6}y&-wuMz>no#NhON$Mg2 zS&y)A8?};$cnG&pD}Iie@Ga^_pHUOGP;cND@{>I}T`$mS0Y&V^LDU0}pguQ>XK(^1 z*7i@?}@0o3|Uev#JN|gjS-5)Jps8TpKMy57wRLoUvN>KCKE9p7s#;NweYc-m~ajUWFMaB=qd9Q4|O7>=DDqVFJ^Ra)^q$k{2 zXJR}x?g(rzyO(mOFC9_+uN(9U+O#YH5nw5hC~pP#$aJ45rY&%32pDvrLl=Homd%7EE0pk zQj0+hh^>!U_>2bIxvPQ z9Kbdl!x&Cu1ap|i1&rfLaDEd#`t9I+8Kd<3O{?aRKdqdwn8j1n3m=;|n3tfhqh9nD z_>Kzp74%-X86bgYgdfCCuHU-G^x_k?;|J=wAM{Msm<+Qic^_(Jqo@Fr95mr9Dqs$i zxQJT8D)KWM9A>Z(^yjD@xIo=^gW9QD-~)Eizv?>kn@|2I**9v}6O@8zvcI9kdg%XX zq3NbF6r0c(EV>a|jXo)hY=qYWwHpeixfF=$Ohn%_YL}e}*DvLZ_R`t2OJU#Mhv%K~ jtWCQka5d$wg;I0*9lu=37l#YA>(cs`y^B=ru4jJ%kFGXK diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index c42c969d..8d264180 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-01-25 20:20+0100\n" -"PO-Revision-Date: 2014-01-25 20:20+0100\n" +"POT-Creation-Date: 2014-01-25 20:52+0100\n" +"PO-Revision-Date: 2014-01-25 20:52+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -25,6 +25,8 @@ msgstr "Benutzer" #: ../../../views/html/menu/index.tpl:3 #: ../../../views/html/seminaries/seminary.tpl:1 #: ../../../views/html/seminaries/index.tpl:1 +#: ../../../views/html/seminaries/edit.tpl:1 +#: ../../../views/html/seminaries/delete.tpl:1 #: ../../../views/html/seminaries/create.tpl:1 msgid "Seminaries" msgstr "Kurse" @@ -52,11 +54,15 @@ msgstr "Benutzername" msgid "Password" msgstr "Passwort" -#: ../../../views/html/users/user.tpl:4 ../../../views/html/users/delete.tpl:2 +#: ../../../views/html/users/user.tpl:4 ../../../views/html/users/edit.tpl:2 +msgid "Edit user" +msgstr "Benutzer bearbeiten" + +#: ../../../views/html/users/user.tpl:5 ../../../views/html/users/delete.tpl:2 msgid "Delete user" msgstr "Benutzer löschen" -#: ../../../views/html/users/user.tpl:7 ../../../views/html/users/index.tpl:10 +#: ../../../views/html/users/user.tpl:8 ../../../views/html/users/index.tpl:10 #, php-format msgid "registered on %s" msgstr "registriert am %s" @@ -65,10 +71,6 @@ msgstr "registriert am %s" msgid "Create new user" msgstr "Neuen Benutzer erstellen" -#: ../../../views/html/users/edit.tpl:2 -msgid "Edit user" -msgstr "Benutzer bearbeiten" - #: ../../../views/html/users/edit.tpl:8 ../../../views/html/users/edit.tpl:9 #: ../../../views/html/users/create.tpl:8 #: ../../../views/html/users/create.tpl:9 @@ -76,10 +78,9 @@ msgid "E‑Mail-Address" msgstr "E‑Mail-Adresse" #: ../../../views/html/users/edit.tpl:13 -#: ../../../views/html/users/create.tpl:13 -#: ../../../views/html/seminaries/create.tpl:9 -msgid "create" -msgstr "erstellen" +#: ../../../views/html/seminaries/edit.tpl:9 +msgid "save" +msgstr "speichern" #: ../../../views/html/users/delete.tpl:4 #, php-format @@ -87,10 +88,12 @@ msgid "Should the user “%s” (%s) really be deleted?" msgstr "Soll der Benutzer „%s“ (%s) wirklich gelöscht werden?" #: ../../../views/html/users/delete.tpl:6 +#: ../../../views/html/seminaries/delete.tpl:6 msgid "delete" msgstr "löschen" #: ../../../views/html/users/delete.tpl:7 +#: ../../../views/html/seminaries/delete.tpl:7 msgid "cancel" msgstr "abbrechen" @@ -98,11 +101,26 @@ msgstr "abbrechen" msgid "New user" msgstr "Neuer Benutzer" +#: ../../../views/html/users/create.tpl:13 +#: ../../../views/html/seminaries/create.tpl:9 +msgid "create" +msgstr "erstellen" + #: ../../../views/html/error/index.tpl:1 msgid "Error" msgstr "Fehler" #: ../../../views/html/seminaries/seminary.tpl:4 +#: ../../../views/html/seminaries/edit.tpl:2 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: ../../../views/html/seminaries/seminary.tpl:5 +#: ../../../views/html/seminaries/delete.tpl:2 +msgid "Delete seminary" +msgstr "Kurs löschen" + +#: ../../../views/html/seminaries/seminary.tpl:8 #: ../../../views/html/seminaries/index.tpl:10 #, php-format msgid "created by %s on %s" @@ -112,15 +130,22 @@ msgstr "erstellt von %s am %s" msgid "Create new seminary" msgstr "Neuen Kurs erstellen" -#: ../../../views/html/seminaries/create.tpl:2 -msgid "New seminary" -msgstr "Neuer Kurs" - +#: ../../../views/html/seminaries/edit.tpl:6 +#: ../../../views/html/seminaries/edit.tpl:7 #: ../../../views/html/seminaries/create.tpl:6 #: ../../../views/html/seminaries/create.tpl:7 msgid "Title" msgstr "Titel" +#: ../../../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/create.tpl:2 +msgid "New seminary" +msgstr "Neuer Kurs" + #: ../../../views/html/introduction/index.tpl:1 msgid "Introduction" msgstr "Einführung" diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index c4ae7954..03ccc2f0 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -99,6 +99,63 @@ 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/UsersModel.inc b/models/UsersModel.inc index 576bc1a2..0456a7ec 100644 --- a/models/UsersModel.inc +++ b/models/UsersModel.inc @@ -155,6 +155,7 @@ * 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 @@ -165,10 +166,12 @@ // Update user data $this->db->query( 'UPDATE users '. - 'SET username = ?, email = ? '. + 'SET username = ?, url = ?, email = ? '. 'WHERE id = ?', - 'ssi', - $sername, $email, + 'sssi', + $username, + \nre\core\Linker::createLinkParam($username), + $email, $userId ); @@ -214,7 +217,7 @@ * @param string $password Plaintext password * @return string Hashed password */ - private function hash($password) + public function hash($password) { return password_hash($password, PASSWORD_DEFAULT); } diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..823eec70 --- /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..d2253f14 --- /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..007acf15 --- /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 index 6e40934a..8ac8c8b0 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -1,4 +1,7 @@

            +
            • diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 3c5f1aba..d033df1c 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,5 +1,9 @@

              +

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

              diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl index caffac41..8536804d 100644 --- a/views/html/users/create.tpl +++ b/views/html/users/create.tpl @@ -2,7 +2,7 @@

              - +

              diff --git a/views/html/users/edit.tpl b/views/html/users/edit.tpl index d3d1c2e0..ef47268c 100644 --- a/views/html/users/edit.tpl +++ b/views/html/users/edit.tpl @@ -1,8 +1,8 @@

              - - + +

              @@ -10,5 +10,5 @@
              - + diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index e3a037af..2b2ea884 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -1,6 +1,7 @@

              From 59d7857ffbfb5ff73c7a6d4681e2f82499f4a2ef Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 29 Jan 2014 12:50:04 +0100 Subject: [PATCH 019/340] do not use ?finally?-statement (not available for PHP <= 5.4) --- drivers/MysqliDriver.inc | 6 ++++-- models/UsersModel.inc | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc index 9520fa1e..fe4fa81a 100644 --- a/drivers/MysqliDriver.inc +++ b/drivers/MysqliDriver.inc @@ -82,10 +82,12 @@ } + $stmt->close(); return $data; } - finally { - $stmt->close(); + catch(Exception $e) { + $stmt->close(); + throw $e; } } diff --git a/models/UsersModel.inc b/models/UsersModel.inc index 0456a7ec..e8b01a52 100644 --- a/models/UsersModel.inc +++ b/models/UsersModel.inc @@ -190,11 +190,10 @@ } catch(Exception $e) { $this->db->rollback(); + $this->db->setAutocommit(true); throw $e; } - finally { - $this->db->setAutocommit(true); - } + $this->db->setAutocommit(true); } From 5d86b5527b48e7994df6c4a19f193838ef04959b Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 29 Jan 2014 15:40:49 +0100 Subject: [PATCH 020/340] use ?is_null? function instead of ?empty? due to backwards compatibility --- controllers/SeminariesController.inc | 4 ++-- controllers/UsersController.inc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 7e307087..632a3378 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -123,7 +123,7 @@ if($this->request->getRequestMethod() == 'POST') { // Save changes - if(!empty($this->request->getPostParam('save'))) + if(!is_null($this->request->getPostParam('save'))) { // Edit seminary $this->Seminaries->editSeminary( @@ -161,7 +161,7 @@ if($this->request->getRequestMethod() == 'POST') { // Check confirmation - if(!empty($this->request->getPostParam('delete'))) + if(!is_null($this->request->getPostParam('delete'))) { // Delete seminary $this->Seminaries->deleteSeminary($seminary['id']); diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index d8b44ccd..7fc8dcf5 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -79,7 +79,7 @@ $username = ''; // Log the user in - if($this->request->getRequestMethod() == 'POST' && !empty($this->request->getPostParam('login'))) + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('login'))) { $username = $this->request->getPostParam('username'); $userId = $this->Users->login( @@ -158,7 +158,7 @@ if($this->request->getRequestMethod() == 'POST') { // Save changes - if(!empty($this->request->getPostParam('save'))) + if(!is_null($this->request->getPostParam('save'))) { // Edit user $this->Users->editUser( @@ -198,7 +198,7 @@ if($this->request->getRequestMethod() == 'POST') { // Check confirmation - if(!empty($this->request->getPostParam('delete'))) + if(!is_null($this->request->getPostParam('delete'))) { // Delete user $this->Users->deleteUser($user['id']); From 0e7ea50bd5043ee57a0969a767710cb2ea6532be Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 29 Jan 2014 16:45:45 +0100 Subject: [PATCH 021/340] add compatibility library for password functions (PHP<=5.5) --- app/lib/Password.inc | 316 ++++++++++++++++++++++++++++++++++++++++++ models/UsersModel.inc | 10 ++ 2 files changed, 326 insertions(+) create mode 100644 app/lib/Password.inc 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/models/UsersModel.inc b/models/UsersModel.inc index e8b01a52..d42db7c7 100644 --- a/models/UsersModel.inc +++ b/models/UsersModel.inc @@ -218,6 +218,11 @@ */ public function hash($password) { + if(!function_exists('password_hash')) { + \hhu\z\lib\Password::load(); + } + + return password_hash($password, PASSWORD_DEFAULT); } @@ -231,6 +236,11 @@ */ private function verify($password, $hash) { + if(!function_exists('password_verify')) { + \hhu\z\lib\Password::load(); + } + + return password_verify($password, $hash); } From fcc7e89fcd66a73d8fa6b5cc459caa72da8b2796 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 30 Jan 2014 00:59:02 +0100 Subject: [PATCH 022/340] implement user roles and user seminary roles as basic ACL --- agents/ToplevelAgent.inc | 11 ++ app/Controller.inc | 56 -------- app/controllers/SeminaryRoleController.inc | 129 ++++++++++++++++++ app/controllers/ToplevelController.inc | 149 +++++++++++++++++++++ controllers/HtmlController.inc | 3 +- controllers/MenuController.inc | 15 +++ controllers/SeminariesController.inc | 19 ++- controllers/UsersController.inc | 11 +- models/UserrolesModel.inc | 77 +++++++++++ models/UserseminaryrolesModel.inc | 78 +++++++++++ 10 files changed, 481 insertions(+), 67 deletions(-) create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/controllers/ToplevelController.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UserseminaryrolesModel.inc diff --git a/agents/ToplevelAgent.inc b/agents/ToplevelAgent.inc index eee33450..9d6d6f8a 100644 --- a/agents/ToplevelAgent.inc +++ b/agents/ToplevelAgent.inc @@ -153,6 +153,17 @@ } + /** + * Return the IntermediateAgent. + * + * @return IntermediateAgent IntermediateAgent + */ + public function getIntermediateAgent() + { + return $this->intermediateAgent; + } + + /** diff --git a/app/Controller.inc b/app/Controller.inc index 5594ac31..42881662 100644 --- a/app/Controller.inc +++ b/app/Controller.inc @@ -25,24 +25,12 @@ * @var array */ public $components = array('auth'); - /** - * Required models - * - * @var array - */ - public $models = array('users'); /** * Linker instance * * @var Linker */ protected $linker = null; - /** - * Data of currently logged in user if any - * - * @var array - */ - protected static $user = null; @@ -76,9 +64,6 @@ { parent::preFilter($request, $response); - // Check rights - $this->checkPermission(); - // Create linker $this->linker = new \nre\core\Linker($this->request); @@ -95,9 +80,6 @@ \IntlDateFormatter::SHORT, NULL )); - - // Set userdata - $this->set('loggedUser', static::$user); } @@ -112,44 +94,6 @@ parent::postFilter($request, $response); } - - - - /** - * Check user permissions. - * - * @throws AccessDeniedException - */ - private function checkPermission() - { - // Determine user - try { - $userId = $this->Auth->getUserId(); - if(!is_null($userId)) { - static::$user = $this->Users->getUserById($this->Auth->getUserId()); - } - } - catch(\nre\exceptions\IdNotFoundException $e) { - } - - - // Determine permissions - $action = $this->request->getParam(2, 'action'); - if(!property_exists($this, 'permissions')) { - return; // Allow if nothing is specified - } - if(!array_key_exists($action, $this->permissions)) { - return; // Allow if Action is not specified - } - $permissions = $this->permissions[$action]; - - - // Check permissions - if(is_null(static::$user)) { - throw new \nre\exceptions\AccessDeniedException(); - } - } - } ?> diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..fadf1249 --- /dev/null +++ b/app/controllers/SeminaryRoleController.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\controllers; + + + /** + * Abstract class for implementing a Controller of a ToplevelAgent. + * + * @author Oliver Hanraths + */ + abstract class SeminaryroleController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('userseminaryroles'); + /** + * 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..74f63667 --- /dev/null +++ b/app/controllers/ToplevelController.inc @@ -0,0 +1,149 @@ + + * @copyright 2014 Heinrich-Heine-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'); + /** + * Current user + * + * @var array + */ + public static $user = 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()); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', static::$user); + } + + + /** + * 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/controllers/HtmlController.inc b/controllers/HtmlController.inc index a2659898..d7660f5a 100644 --- a/controllers/HtmlController.inc +++ b/controllers/HtmlController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - class HtmlController extends \hhu\z\Controller + class HtmlController extends \hhu\z\controllers\ToplevelController { @@ -33,7 +33,6 @@ { parent::preFilter($request, $response); - // Set content-type $this->response->addHeader("Content-type: text/html; charset=utf-8"); } diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc index 51aedd25..3d9d5551 100644 --- a/controllers/MenuController.inc +++ b/controllers/MenuController.inc @@ -23,6 +23,21 @@ + /** + * 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. */ diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 632a3378..1df4d5a4 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - class SeminariesController extends \hhu\z\Controller + class SeminariesController extends \hhu\z\controllers\SeminaryRoleController { /** * Required models @@ -31,8 +31,21 @@ * @var array */ public $permissions = array( - 'index' => array(), - 'seminary' => 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') ); diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 7fc8dcf5..fb6c8d16 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -25,17 +25,16 @@ * @var array */ public $permissions = array( - 'index' => array(), - 'user' => array(), - 'create' => array(), - 'edit' => array(), - 'delete' => array() + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') ); - /** * Action: index. */ 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/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 + ); + } + + } + +?> From 6a52ded20e93778bdb29ba40926ae418e7e540c7 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 7 Feb 2014 14:10:00 +0100 Subject: [PATCH 023/340] correct name of SeminaryRoleController --- app/controllers/SeminaryRoleController.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index fadf1249..ed6c8d04 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - abstract class SeminaryroleController extends \hhu\z\Controller + abstract class SeminaryRoleController extends \hhu\z\Controller { /** * Required models From a0f85a976a88892cbbd1933ba791b99fa8e5842f Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Feb 2014 13:33:38 +0100 Subject: [PATCH 024/340] show Characters and Roles of an user --- agents/bottomlevel/UserrolesAgent.inc | 35 ++++++++++++++++++++ agents/intermediate/UsersAgent.inc | 9 +++++ controllers/UserrolesController.inc | 47 +++++++++++++++++++++++++++ controllers/UsersController.inc | 10 ++++++ views/html/userroles/user.tpl | 5 +++ views/html/users/user.tpl | 10 ++++++ 6 files changed, 116 insertions(+) create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 views/html/userroles/user.tpl 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/UsersAgent.inc b/agents/intermediate/UsersAgent.inc index 7b929586..a9094490 100644 --- a/agents/intermediate/UsersAgent.inc +++ b/agents/intermediate/UsersAgent.inc @@ -30,6 +30,15 @@ { } + + /** + * Action: user. + */ + public function user(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Userroles', 'user'); + } + } ?> 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 index fb6c8d16..a9eb9f91 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -31,6 +31,12 @@ 'edit' => array('admin', 'moderator'), 'delete' => array('admin') ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters'); @@ -62,9 +68,13 @@ // Get user $user = $this->Users->getUserByUrl($userUrl); + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + // Pass data to view $this->set('user', $user); + $this->set('characters', $characters); } 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/user.tpl b/views/html/users/user.tpl index 2b2ea884..2c081f2d 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -7,3 +7,13 @@

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

              + +

              +
                + +
              • ()
              • + +
              + +

              + From ff8500207079420dad7a10ce278da57cb0cdc06e Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Feb 2014 13:35:39 +0100 Subject: [PATCH 025/340] correct components and models of SeminaryRoleController --- app/controllers/SeminaryRoleController.inc | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index ed6c8d04..e06a9a03 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -13,18 +13,25 @@ /** - * Abstract class for implementing a Controller of a ToplevelAgent. + * 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('userseminaryroles'); + public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements'); /** * Data of currently logged in user if any * From efd4f990fa7c2a9fb398bf3e40bc1f5a54074804 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Feb 2014 13:37:40 +0100 Subject: [PATCH 026/340] implement basic Questgroups and Quests structure --- .../QuestgroupshierarchypathAgent.inc | 35 +++++ agents/intermediate/QuestgroupsAgent.inc | 36 +++++ agents/intermediate/QuestsAgent.inc | 36 +++++ configs/AppConfig.inc | 11 +- controllers/QuestgroupsController.inc | 95 ++++++++++++ .../QuestgroupshierarchypathController.inc | 76 ++++++++++ controllers/QuestsController.inc | 78 ++++++++++ controllers/SeminariesController.inc | 9 +- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 1805 -> 1888 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 14 +- models/QuestgroupsModel.inc | 143 ++++++++++++++++++ models/QuestgroupshierarchyModel.inc | 103 +++++++++++++ models/QuestsModel.inc | 85 +++++++++++ models/SeminariesModel.inc | 6 +- views/html/questgroups/questgroup.tpl | 29 ++++ views/html/questgroupshierarchypath/index.tpl | 8 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 7 + views/html/seminaries/seminary.tpl | 11 ++ 19 files changed, 774 insertions(+), 8 deletions(-) create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl 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/intermediate/QuestgroupsAgent.inc b/agents/intermediate/QuestgroupsAgent.inc new file mode 100644 index 00000000..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..e58e5092 --- /dev/null +++ b/agents/intermediate/QuestsAgent.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\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); + } + + } + +?> diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index aeafd57e..82134781 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -70,11 +70,17 @@ * @var array */ public static $routes = array( - array('css/?(.*)', 'css/$1?layout=stylesheet', false), + 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) + 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)*/ ); @@ -88,6 +94,7 @@ array('users/user/(.*)', 'users/$1', true), array('users/([^/]+)/(.*)', 'users/$2/$1', true), array('seminaries/seminary/(.*)', 'seminaries/$1', false) + //array('seminaries/seminary/(.*)', '$1', false) ); diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..2ab68d9e --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,95 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the 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 child Questgroupshierarchy + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupshierarchy['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) { + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Quests + $quests = null; + if(count($childQuestgroupshierarchy) == 0) { + $quests = $this->Quests->getQuestsForQuestgroup($questgroup['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/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..f8b41332 --- /dev/null +++ b/controllers/QuestsController.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\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'); + /** + * 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 quest and its task. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param string $questUrl URL-Title of a Quest + */ + public function quest($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); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + } + + } + +?> diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 1df4d5a4..94303d88 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'users'); + public $models = array('seminaries', 'users', 'questgroupshierarchy', 'questgroups'); /** * User permissions * @@ -90,9 +90,16 @@ // Created user $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + // Questgrouphierarchy and Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyForSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) { + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + } + // Pass data to view $this->set('seminary', $seminary); + $this->set('questgroupshierarchy', $questgroupshierarchy); } diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 621bc112b8c5dacdd562cb787b33290e193e8a09..ab0f294c9cf370b826dbce9096447d57728a950e 100644 GIT binary patch delta 748 zcmYk)PbfrD6vy$yycuIK{*3?svXLzOnaN_XuwccCB1_VgB5K%KOetlf=w+v@St+rQ zg^d&&*(ssy#6q#5=-@4h?to_o)G8}Y|@?!Lm?Hlm5vNb~Z{+VKSyoB3uj z+``Y6HXA zh2z+bYi|4)HSg5*&yj`g%6Ws@_^s=|qBiu79{DZoneB4g7%I^|Su61fYw;3wvL`IZ zH&g{eWYNM2RKhkcTDTKckpbkdHiGCiF^Km zs?-GfRFn^c_oeboaI-Q++*Yzszg?*F8*o$-G!FZ;p8xe>Qv(VcvXZ!S9Sb`~an@ BLZJWv delta 664 zcmYk(J4*vW6o%oGWD`w{QDZdT5F70z7f6%Z%FchFAc#L8g$M>j8^J=BpkN^if`VWX zg4k#yUcf?Z#UR>#nccH<&ORm96Xkl+s~BUD7$QE2QQ|hhg)tp8 z>&6-Ez>;$n+n86d9d|K`#~8*_?8I}7L6A0@B()z?gmpAhn2lBHYF4_-sQ2}vGU>bGcG1Pq-%wYkGyx->B#vZCw2dD>XsFt2Nud#>u zz1x399rWGphg!_^)zXB9J}FJL0YV4Spy)<1DonX%3F{|Pgu0*(kgw4yy}UWd-Ke_P zEvOP5Qk5p%S`!tagQ+_j{&w)tpA3D+a^v~JNIqLE + * @copyright 2014 Heinrich-Heine-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 + { + + + + + /** + * 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) + { + if(is_null($parentQuestgroupId)) + { + return $this->db->query( + 'SELECT id, questgroupshierarchy_id, pos, title, url '. + 'FROM questgroups '. + 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id IS NULL '. + 'ORDER BY questgroups.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + return $this->db->query( + 'SELECT id, questgroupshierarchy_id, pos, title, url '. + 'FROM questgroups '. + 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id = ? '. + 'ORDER BY questgroups.pos ASC', + 'ii', + $hierarchyId, $parentQuestgroupId + ); + } + } + + + /** + * 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 '. + '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 '. + '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 + ); + } + + } + +?> 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..c4770488 --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,85 @@ + + * @copyright 2014 Heinrich-Heine-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 getQuestsForQuestgroup($questgroupId) + { + return $this->db->query( + 'SELECT id, questtype_id, title, url, xps, text '. + 'FROM quests '. + '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.text '. + '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); + } + + + return $data[0]; + } + + } + +?> diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index 03ccc2f0..15963271 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -43,7 +43,7 @@ { // Get seminaries return $this->db->query( - 'SELECT id, created, created_user_id, title, url '. + 'SELECT id, created, created_user_id, title, url, description '. 'FROM seminaries '. 'ORDER BY created DESC' ); @@ -60,7 +60,7 @@ public function getSeminaryById($seminaryId) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url '. + 'SELECT id, created, created_user_id, title, url, description '. 'FROM seminaries '. 'WHERE id = ?', 'i', @@ -85,7 +85,7 @@ public function getSeminaryByUrl($seminaryUrl) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url '. + 'SELECT id, created, created_user_id, title, url, description '. 'FROM seminaries '. 'WHERE url = ?', 's', diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..3cca41f4 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,29 @@ +

              +

              + + + +

              :

              + +

              + + + + 0) : ?> +

              +
                + +
              • :
              • + +
              + + + + +

              Quests

              +
                + +
              • + +
              + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..2c583bb6 --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,8 @@ + 0) : ?> +Pfad: + + 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..1dc1fc0d --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,7 @@ +

              +

              + + + +

              +

              diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index d033df1c..2f63cdfa 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -7,3 +7,14 @@

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

              +

              Beschreibung

              +

              + + +

              +
                + +
              • :
              • + +
              + From 858c503add7026d76ec7b5a7fb10d9c38832c363 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Feb 2014 13:37:56 +0100 Subject: [PATCH 027/340] add CharactersModel --- models/CharactersModel.inc | 85 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 models/CharactersModel.inc diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc new file mode 100644 index 00000000..c62f294c --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,85 @@ + + * @copyright 2014 Heinrich-Heine-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, 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 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 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, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM 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]; + } + + } + +?> From e08508270e4432c7ee3446db4e61639430717141 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Feb 2014 13:38:25 +0100 Subject: [PATCH 028/340] add logging method detection for IPv6 --- www/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/index.php b/www/index.php index 2faa32fd..46301a31 100644 --- a/www/index.php +++ b/www/index.php @@ -25,7 +25,7 @@ /** * De-/Activate error messages */ - if($_SERVER['SERVER_ADDR'] == '127.0.0.1') { + 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); From ad2ac73f01ed88065b3edff16664639846084dfa Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Feb 2014 13:55:45 +0100 Subject: [PATCH 029/340] add empty AchievementsComponent and AchievementsModel --- .../components/AchievementComponent.inc | 41 +++++++++++++++++++ models/AchievementsModel.inc | 36 ++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 models/AchievementsModel.inc 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/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(); + } + + } + +?> From 108ca6547059ba2fd11018882c1317f8c2a03e35 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 12 Feb 2014 10:31:05 +0100 Subject: [PATCH 030/340] show Questtexts for Quests --- controllers/QuestsController.inc | 25 +++++++++- models/QuestsModel.inc | 4 +- models/QuesttextsModel.inc | 80 ++++++++++++++++++++++++++++++++ views/html/quests/quest.tpl | 13 +++++- 4 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 models/QuesttextsModel.inc diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index f8b41332..cccd1581 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroups', 'quests'); + public $models = array('seminaries', 'questgroups', 'quests', 'questtexts'); /** * User permissions * @@ -55,7 +55,7 @@ * @param string $questgroupUrl URL-Title of a Questgroup * @param string $questUrl URL-Title of a Quest */ - public function quest($seminaryUrl, $questgroupUrl, $questUrl) + public function quest($seminaryUrl, $questgroupUrl, $questUrl, $questtexttypeUrl=null, $questtextPos=1) { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); @@ -66,11 +66,32 @@ // Get Quest $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + // Get Questtext + $questtext = null; + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + if(in_array($questtexttypeUrl, $questtexttypes)) + { + $questtextPos = max(intval($questtextPos), 1); + $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); + } + + // Show task only for Prologes + $showTask = false; + if($questtext['type'] == 'Prolog') { + $showTask = true; + } + // Pass data to view $this->set('seminary', $seminary); $this->set('questgroup', $questgroup); + $this->set('questtext', $questtext); $this->set('quest', $quest); + $this->set('showtask', $showTask); } } diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index c4770488..2f991143 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -43,7 +43,7 @@ public function getQuestsForQuestgroup($questgroupId) { return $this->db->query( - 'SELECT id, questtype_id, title, url, xps, text '. + 'SELECT id, questtype_id, title, url, xps, task '. 'FROM quests '. 'WHERE questgroup_id = ?', 'i', @@ -64,7 +64,7 @@ public function getQuestByUrl($seminaryId, $questgroupId, $questUrl) { $data = $this->db->query( - 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.text '. + 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task '. 'FROM quests '. 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '. diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..7387b3a2 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,80 @@ + + * @copyright 2014 Heinrich-Heine-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.out_text, questtexttypes.type '. + '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 all registered Questtexttypes. + * + * @return array Registered Questtexttypes + */ + public function getQuesttexttypes() + { + return $this->db->query( + 'SELECT id, type, url '. + 'FROM questtexttypes' + ); + } + + } + +?> diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 1dc1fc0d..836ce775 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -4,4 +4,15 @@

              -

              +
              +

              + + + +
              + + +
              +

              +
              + From 46364ac6f21bef85ea2e07e6c08f5c7abb12835a Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 12 Feb 2014 23:39:15 +0100 Subject: [PATCH 031/340] implement Questtexts and Sidequests --- agents/intermediate/QuestsAgent.inc | 9 ++ controllers/QuestgroupsController.inc | 8 +- controllers/QuestsController.inc | 85 +++++++++++++-- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 1888 -> 2106 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 22 +++- models/QuestsModel.inc | 73 +++++++++++++ models/QuesttextsModel.inc | 113 +++++++++++++++++++- views/html/questgroups/questgroup.tpl | 15 ++- views/html/quests/quest.tpl | 22 +++- views/html/quests/sidequest.tpl | 23 ++++ 10 files changed, 355 insertions(+), 15 deletions(-) create mode 100644 views/html/quests/sidequest.tpl diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc index e58e5092..faed65a5 100644 --- a/agents/intermediate/QuestsAgent.inc +++ b/agents/intermediate/QuestsAgent.inc @@ -31,6 +31,15 @@ $this->addSubAgent('Questgroupshierarchypath', 'index', $request->getParam(3), $request->getParam(4), true); } + + /** + * Action: quest. + */ + public function sidequest(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', 'index', $request->getParam(3), $request->getParam(4), true); + } + } ?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 2ab68d9e..a0639592 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -76,8 +76,14 @@ // Get Quests $quests = null; - if(count($childQuestgroupshierarchy) == 0) { + if(count($childQuestgroupshierarchy) == 0) + { $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); + + // Attach sidequests + foreach($quests as &$quest) { + $quest['sidequests'] = $this->Quests->getSidequestsForQuest($quest['id']); + } } diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index cccd1581..ed6a1f3c 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -51,9 +51,11 @@ * Show a quest and its task. * * @throws IdNotFoundException - * @param string $seminaryUrl URL-Title of a Seminary - * @param string $questgroupUrl URL-Title of a Questgroup - * @param string $questUrl URL-Title of a Quest + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param string $questUrl URL-Title of a Quest + * @param string $questtexttypeUrl URL-Title of a Questtexttype + * @param int $questtextPos Position of Questtext */ public function quest($seminaryUrl, $questgroupUrl, $questUrl, $questtexttypeUrl=null, $questtextPos=1) { @@ -73,10 +75,16 @@ if(is_null($questtexttypeUrl)) { $questtexttypeUrl = 'Prolog'; } - if(in_array($questtexttypeUrl, $questtexttypes)) + $questtextCount = $this->Questtexts->getQuesttextsCountForQuest($quest['id'], $questtexttypeUrl); + if($questtextCount > 0) { - $questtextPos = max(intval($questtextPos), 1); - $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); + if(in_array($questtexttypeUrl, $questtexttypes)) + { + $questtextPos = max(intval($questtextPos), 1); + $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); + $questtext['count'] = $questtextCount; + $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']); + } } // Show task only for Prologes @@ -94,6 +102,71 @@ $this->set('showtask', $showTask); } + /** + * Action: sidequest. + * + * Show a sidequest and its task. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param string $questUrl URL-Title of a Quest + * @param string $sidequestUrl URL-Title of a Sidequest + * @param string $questtexttypeUrl URL-Title of a Questtexttype + * @param int $questtextPos Position of Questtext + */ + public function sidequest($seminaryUrl, $questgroupUrl, $questUrl, $sidequestUrl, $sidequesttexttypeUrl=null, $sidequesttextPos=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 Sidequest + $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $sidequestUrl); + + // Get Questtext + $questtext = $this->Questtexts->getQuesttextForSidequest($sidequest['id']); + + // Get Sidequesttext + $sidequesttext = null; + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(is_null($sidequesttexttypeUrl)) { + $sidequesttexttypeUrl = 'Prolog'; + } + $sidequesttextCount = $this->Questtexts->getQuesttextsCountForSidequest($sidequest['id'], $sidequesttexttypeUrl); + if($sidequesttextCount > 0) + { + if(in_array($sidequesttexttypeUrl, $questtexttypes)) + { + $sidequesttextPos = max(intval($sidequesttextPos), 1); + $sidequesttext = $this->Questtexts->getSidequesttextByUrl($sidequest['id'], $sidequesttexttypeUrl, $sidequesttextPos); + $sidequesttext['count'] = $sidequesttextCount; + } + } + + // Show task only for Prologes + $showTask = false; + if($sidequesttext['type'] == 'Prolog') { + $showTask = true; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('sidequesttext', $sidequesttext); + $this->set('quest', $quest); + $this->set('questtext', $questtext); + $this->set('sidequest', $sidequest); + $this->set('showtask', $showTask); + } + } ?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index ab0f294c9cf370b826dbce9096447d57728a950e..8680ecbba6a35368600de60d9e20a7e18e7ec4ce 100644 GIT binary patch delta 959 zcmZ9~KWGzS7{~F)Bu(PK+E$}6)%FS%9R!mqh@I-cF6t1exO#@OIgOX%UBxPr!9ggv zcsTe66r4I&5Cu1J6$b|wQ4rL{sSZy1{pBthe7X1iyzkw6@AJOT-Iu}FL!Hkfxd%p> zpzfkht}{D;`!h6@AGiU3;#OS4Em+JZmT@EFeYhD9;d(rV19%#T@GNe^MtXk%^JX2p zo<6vZMJ6n##>oVGf?DV~Y9lMC4ZgrCe#0D=XtrYoHNS>h?{GSvK?2$=R`5)B+%ap? z(aGBBggdAmFC&*drg=(Mo}o4{!z$bG6qfM<>f}o}jCWBPdWc$g1-a}Ejn;jO%GgI7 zAijO2!)4!T6yO&Q;vduriiPC)aB3NK!f~wO9-PMW$Yu9ww6RC1d8??@ze26|4)uxN zV`r4kr}V~eEHVCznpmRIiN;VbRFOp32vq@;M!o-kQBVcc|D;UlvuItNTECV4wX|IB zs`M$|E$w3*4(oC$xqeG`yBI+Ck*{fgjBG=O-(K z@rUeMHsu=xJ!5+}Xy^X_|n_ndp)tHd9orTe6}YeX+2&G72X2JsaY+X1si z+`(4d!x$bp&#{U5HMZa#HsA|}u!?c~#Aa*?{#;LCy;;fnIMIzPMzG+VKrK*26)=s% zIEOjhboWnC&z-sX1+uVRId4#f-@5r5s-P-**g2?sc6;UQE^4l^e{A`_r9@xMaDU*>tyIPm3S@I#Zb_ZTByoyhIXM)QWd69 z<>ar0x}y##i4y-t4<|{6z9)4=#i)b-qk{?cBx|i!I~Do}&3_7<`Uk-kzu=|)C2uv7 g8_VPi<0GT~vzPgslw+Ylz)ywO{BpS8{|HZg12BX-2LJ#7 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index b9117fec..e5954918 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-02-09 12:48+0100\n" -"PO-Revision-Date: 2014-02-09 12:48+0100\n" +"POT-Creation-Date: 2014-02-12 23:09+0100\n" +"PO-Revision-Date: 2014-02-12 23:10+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -30,6 +30,7 @@ msgstr "Benutzer" #: ../../../views/html/seminaries/create.tpl:1 #: ../../../views/html/questgroups/questgroup.tpl:1 #: ../../../views/html/quests/quest.tpl:1 +#: ../../../views/html/quests/sidequest.tpl:1 msgid "Seminaries" msgstr "Kurse" @@ -156,10 +157,27 @@ msgstr "Soll der Kurs „%s“ wirklich gelöscht werden?" msgid "New seminary" msgstr "Neuer Kurs" +#: ../../../views/html/questgroups/questgroup.tpl:23 +msgid "Quests" +msgstr "Quests" + +#: ../../../views/html/questgroups/questgroup.tpl:29 +msgid "containing optional Quests" +msgstr "Enthaltene optionale Quests" + #: ../../../views/html/introduction/index.tpl:1 msgid "Introduction" msgstr "Einführung" +#: ../../../views/html/quests/quest.tpl:33 +#: ../../../views/html/quests/sidequest.tpl:20 +msgid "Task" +msgstr "Aufgabe" + +#: ../../../views/html/quests/sidequest.tpl:8 +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" diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 2f991143..dd30a5dd 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -80,6 +80,79 @@ 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 sidequests.id, sidequests.questtype_id, sidequests.title, sidequests.url, sidequests.xps, sidequests.task '. + 'FROM sidequests '. + 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '. + 'LEFT JOIN quests ON quests.id = questtexts.quest_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 sidequests.url = ? AND quests.id = ? AND questgroups.id = ? AND seminaries.id = ?', + 'siii', + $sidequestUrl, + $questId, + $questgroupId, + $seminaryId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException(); + } + + + 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 sidequests.id, sidequests.questtext_id, sidequests.title, sidequests.url, sidequests.entry_text '. + 'FROM sidequests '. + '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 sidequests '. + 'WHERE questtext_id = ?', + 'i', + $questtextId + ); + } + } ?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index 7387b3a2..aa1a054c 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -46,10 +46,10 @@ public function getQuesttextByUrl($questId, $questtexttypeUrl, $pos) { $data = $this->db->query( - 'SELECT questtexts.id, questtexts.text, questtexts.out_text, questtexttypes.type '. + 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexts.out_text, 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 = ?', + 'WHERE questtexts.quest_id = ? AND questtexttypes.url = ? AND questtexts.pos = ?', 'isi', $questId, $questtexttypeUrl, $pos ); @@ -62,6 +62,115 @@ } + /** + * Get a Questtext for a Sidequest by its URL. + * + * @throws IdNotFoundException + * @param int $sidequestId ID of the Sidequest to get text for + * @param string $questtexttypeUrl URL of the Questtexttype + * @param int $pos Position of Questtexttype + * @return array Questtexttype data + */ + public function getSidequesttextByUrl($sidequestId, $questtexttypeUrl, $pos) + { + $data = $this->db->query( + 'SELECT sidequesttexts.id, sidequesttexts.text, sidequesttexts.pos, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. + 'FROM sidequesttexts '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = sidequesttexts.questtexttype_id '. + 'WHERE sidequesttexts.sidequest_id = ? AND questtexttypes.url = ? AND sidequesttexts.pos = ?', + 'isi', + $sidequestId, $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 count of Questtexts for a Sidequest. + * + * @param int $sidequestId ID of the Sidequest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return int Conut of Questtexts for Sideuest + */ + public function getQuesttextsCountForSidequest($questId, $questtexttypUrl) + { + $count = 0; + $data = $this->db->query( + 'SELECT COUNT(sidequesttexts.id) AS c '. + 'FROM sidequesttexts '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = sidequesttexts.questtexttype_id '. + 'WHERE sidequesttexts.sidequest_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, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. + 'FROM sidequests '. + 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE sidequests.id = ?', + 'i', + $sidequestId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException(); + } + + + return $data[0]; + } + + /** * Get all registered Questtexttypes. * diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 3cca41f4..231f663e 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -20,10 +20,21 @@ -

              Quests

              +

                -
              • +
              • + +
                + : + 0) : ?> +
                  + +
                • + +
                + +
              diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 836ce775..598342da 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -5,14 +5,32 @@

              +

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

              diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl new file mode 100644 index 00000000..7b1dc3db --- /dev/null +++ b/views/html/quests/sidequest.tpl @@ -0,0 +1,23 @@ +

              +

              + + + +

              +

              +

              .

              +
              +

              +

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

              +

              +
              + From 0d474e2fbacf1b548420aa351d5c6db2df692b5f Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:53:19 +0100 Subject: [PATCH 032/340] implement BinaryAgent for displaying binary data --- agents/toplevel/BinaryAgent.inc | 41 ++++++++++++++++++++++++++++++++ controllers/BinaryController.inc | 37 ++++++++++++++++++++++++++++ views/binary/binary.tpl | 1 + 3 files changed, 79 insertions(+) create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 controllers/BinaryController.inc create mode 100644 views/binary/binary.tpl 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/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/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 @@ + From e11b07ead6ec2aaaaf546b27400fbb1b59eff172 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:56:12 +0100 Subject: [PATCH 033/340] implement MediaAgent for displaying media --- agents/intermediate/MediaAgent.inc | 35 ++++++++++++ controllers/MediaController.inc | 72 ++++++++++++++++++++++++ media/empty | 0 models/MediaModel.inc | 90 ++++++++++++++++++++++++++++++ views/binary/media/index.tpl | 1 + 5 files changed, 198 insertions(+) create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 controllers/MediaController.inc create mode 100644 media/empty create mode 100644 models/MediaModel.inc create mode 100644 views/binary/media/index.tpl 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/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..60ad460d --- /dev/null +++ b/controllers/MediaController.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\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( + 'quest' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'quest' => array('admin', 'moderator', 'user') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'media'); + + + + + /** + * TODO Action: index. + */ + public function index($seminaryUrl, $mediaUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getMediaByUrl($seminary['id'], $mediaUrl); + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.'media'.DS.$media['id']; + + + // Pass data to view + $this->set('media', $media); + } + + } + +?> diff --git a/media/empty b/media/empty new file mode 100644 index 00000000..e69de29b 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/views/binary/media/index.tpl b/views/binary/media/index.tpl new file mode 100644 index 00000000..d0f3e0cd --- /dev/null +++ b/views/binary/media/index.tpl @@ -0,0 +1 @@ + From 1d273badbe4d20ec1904e0ac4891a0bd702e4b6a Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:57:05 +0100 Subject: [PATCH 034/340] do not upload media files --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .hgignore diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..5c118c1e --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +syntax: glob +media/* From f1917de143bc23aa6583c5f553b002de8d82f541 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:57:29 +0100 Subject: [PATCH 035/340] rewrite media URLs --- configs/AppConfig.inc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 82134781..d5523216 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -81,6 +81,8 @@ array('^([^/]+)/([^/]+)/?$', 'questgropus/questgroup/$1/$2', true), // z/// ⇒ z/quests/quest/// array('^([^/]+)/([^/]+)/([^/]+)/?$', 'quests/quest/$1/$2/3', true)*/ + array('media/(.*)', 'media/$1?layout=binary', false), + array('media/(.*)', 'media/index/$1', true) ); @@ -95,6 +97,7 @@ array('users/([^/]+)/(.*)', 'users/$2/$1', true), array('seminaries/seminary/(.*)', 'seminaries/$1', false) //array('seminaries/seminary/(.*)', '$1', false) + array('media/index/(.*)', 'media/$1', true) ); From 857aeb37af5ebec7a9e81b955aab3a656c8b3601 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:58:47 +0100 Subject: [PATCH 036/340] implement QuestgroupspictureAgent to include the picture of a Questgroup --- .../bottomlevel/QuestgroupspictureAgent.inc | 35 +++++++++++ controllers/QuestgroupspictureController.inc | 60 +++++++++++++++++++ views/html/questgroupspicture/index.tpl | 3 + 3 files changed, 98 insertions(+) create mode 100644 agents/bottomlevel/QuestgroupspictureAgent.inc create mode 100644 controllers/QuestgroupspictureController.inc create mode 100644 views/html/questgroupspicture/index.tpl 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/controllers/QuestgroupspictureController.inc b/controllers/QuestgroupspictureController.inc new file mode 100644 index 00000000..182c2b50 --- /dev/null +++ b/controllers/QuestgroupspictureController.inc @@ -0,0 +1,60 @@ + + * @copyright 2014 Heinrich-Heine-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 = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('picture', $picture); + } + + } + +?> 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 @@ + + + From fc814a515ee7203172df46156b26c2b1bf3ae514 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:59:39 +0100 Subject: [PATCH 037/340] include pictures for Questgroups and Quests --- agents/intermediate/QuestgroupsAgent.inc | 1 + agents/intermediate/QuestsAgent.inc | 2 ++ controllers/QuestsController.inc | 17 ++++++++++++++++- models/QuestgroupsModel.inc | 4 ++-- models/QuestsModel.inc | 4 ++-- views/html/questgroups/questgroup.tpl | 1 + views/html/quests/quest.tpl | 4 ++++ views/html/quests/sidequest.tpl | 4 ++++ 8 files changed, 32 insertions(+), 5 deletions(-) diff --git a/agents/intermediate/QuestgroupsAgent.inc b/agents/intermediate/QuestgroupsAgent.inc index 52ecd4e1..860fd177 100644 --- a/agents/intermediate/QuestgroupsAgent.inc +++ b/agents/intermediate/QuestgroupsAgent.inc @@ -29,6 +29,7 @@ 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 index faed65a5..8de47e79 100644 --- a/agents/intermediate/QuestsAgent.inc +++ b/agents/intermediate/QuestsAgent.inc @@ -29,6 +29,7 @@ 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); } @@ -38,6 +39,7 @@ public function sidequest(\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/controllers/QuestsController.inc b/controllers/QuestsController.inc index ed6a1f3c..dda3e718 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroups', 'quests', 'questtexts'); + public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media'); /** * User permissions * @@ -93,6 +93,13 @@ $showTask = true; } + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + } + + // Pass data to view $this->set('seminary', $seminary); @@ -100,6 +107,7 @@ $this->set('questtext', $questtext); $this->set('quest', $quest); $this->set('showtask', $showTask); + $this->set('media', $questmedia); } /** @@ -156,6 +164,12 @@ $showTask = true; } + // Media + $sidequestmedia = null; + if(!is_null($sidequest['questsmedia_id'])) { + $sidequestmedia = $this->Media->getMediaById($sidequest['questsmedia_id']); + } + // Pass data to view $this->set('seminary', $seminary); @@ -165,6 +179,7 @@ $this->set('questtext', $questtext); $this->set('sidequest', $sidequest); $this->set('showtask', $showTask); + $this->set('media', $sidequestmedia); } } diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index d4b624bf..ac820bf1 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -78,7 +78,7 @@ public function getQuestgroupById($questgroupId) { $data = $this->db->query( - 'SELECT id, questgroupshierarchy_id, parent_questgroup_id, pos, title, url '. + 'SELECT id, questgroupshierarchy_id, parent_questgroup_id, pos, title, url, questgroupspicture_id '. 'FROM questgroups '. 'WHERE questgroups.id = ?', 'i', @@ -104,7 +104,7 @@ 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 '. + '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 = ?', diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index dd30a5dd..0873c6cd 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -64,7 +64,7 @@ public function getQuestByUrl($seminaryId, $questgroupId, $questUrl) { $data = $this->db->query( - 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task '. + 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.questsmedia_id '. 'FROM quests '. 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '. @@ -94,7 +94,7 @@ public function getSidequestByUrl($seminaryId, $questgroupId, $questId, $sidequestUrl) { $data = $this->db->query( - 'SELECT sidequests.id, sidequests.questtype_id, sidequests.title, sidequests.url, sidequests.xps, sidequests.task '. + 'SELECT sidequests.id, sidequests.questtype_id, sidequests.title, sidequests.url, sidequests.xps, sidequests.task, sidequests.questsmedia_id '. 'FROM sidequests '. 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '. 'LEFT JOIN quests ON quests.id = questtexts.quest_id '. diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 231f663e..b78d1fe4 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -2,6 +2,7 @@

              +

              :

              diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 598342da..330cb70e 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -2,8 +2,12 @@

              +

              + + +

              diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl index 7b1dc3db..ff3c09ad 100644 --- a/views/html/quests/sidequest.tpl +++ b/views/html/quests/sidequest.tpl @@ -2,10 +2,14 @@

              +

              .

              + + +

              From 0cba7dcc90ba0fbfdb144ca572201ebb57a5c566 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 18:59:55 +0100 Subject: [PATCH 038/340] correct typo in Questgroup hierarchy path --- views/html/questgroupshierarchypath/index.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl index 2c583bb6..9bfe0099 100644 --- a/views/html/questgroupshierarchypath/index.tpl +++ b/views/html/questgroupshierarchypath/index.tpl @@ -2,7 +2,7 @@ Pfad: From 74abd9a7ff2d6b6774a4057d1a9836a62dfa6d2b Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 19:04:24 +0100 Subject: [PATCH 039/340] correct typo in AppConfig --- configs/AppConfig.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index d5523216..5f0e0485 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -95,7 +95,7 @@ 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/(.*)', 'seminaries/$1', false), //array('seminaries/seminary/(.*)', '$1', false) array('media/index/(.*)', 'media/$1', true) ); From 3f686005e3c5ff6993c044f6ce699c7c980bd317 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 19:25:29 +0100 Subject: [PATCH 040/340] make media directory configurable and check media file for existence --- configs/AppConfig.inc | 3 ++- controllers/MediaController.inc | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 5f0e0485..3e8d92da 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -59,7 +59,8 @@ * @var array */ public static $dirs = array( - 'locale' => 'locale' + 'locale' => 'locale', + 'media' => 'media' ); diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 60ad460d..7d80a897 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -46,7 +46,13 @@ /** - * TODO Action: index. + * 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) { @@ -60,7 +66,10 @@ $this->response->addHeader("Content-type: ".$media['mimetype'].""); // Set filename - $media['filename'] = ROOT.DS.'media'.DS.$media['id']; + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['media'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } // Pass data to view From 2873f9325f1f05ac22e6cc96a708a6931c5b6140 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 19:30:51 +0100 Subject: [PATCH 041/340] add error template for binary layout --- views/binary/error/index.tpl | 1 + 1 file changed, 1 insertion(+) create mode 100644 views/binary/error/index.tpl 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 @@ +: : From 18dbf8d83a996bb3918f9eab4d8faa8980044138 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 19:52:05 +0100 Subject: [PATCH 042/340] do not regenerate session id --- controllers/components/AuthComponent.inc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/controllers/components/AuthComponent.inc b/controllers/components/AuthComponent.inc index fe0cb139..0db6c69d 100644 --- a/controllers/components/AuthComponent.inc +++ b/controllers/components/AuthComponent.inc @@ -13,7 +13,7 @@ /** - * Component to handle authentication and authorization + * Component to handle authentication and authorization. * * @author Oliver Hanraths */ @@ -37,7 +37,6 @@ // Start session if(session_id() === '') { session_start(); - session_regenerate_id(true); } } From 5ceb82ae6921aa0f6a92483214e42237c8b3c790 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 19:57:24 +0100 Subject: [PATCH 043/340] correct permissions of MediaController --- controllers/MediaController.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 7d80a897..bb3feede 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -25,7 +25,7 @@ * @var array */ public $permissions = array( - 'quest' => array('admin', 'moderator', 'user') + 'index' => array('admin', 'moderator', 'user') ); /** * User seminary permissions @@ -33,7 +33,7 @@ * @var array */ public $seminaryPermissions = array( - 'quest' => array('admin', 'moderator', 'user') + 'index' => array('admin', 'moderator', 'user') ); /** * Required models From 0a09700828a6fe745e0dbd9b9f2d2a63751dfb8e Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 14 Feb 2014 20:45:10 +0100 Subject: [PATCH 044/340] set caching headers for media --- controllers/MediaController.inc | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index bb3feede..aa69e704 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -45,6 +45,24 @@ + /** + * 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. * @@ -71,11 +89,55 @@ throw new \nre\exceptions\IdNotFoundException($mediaUrl); } + // Cache + if($this->setCacheHeaders($media['filename'])) { + return; + } + // Pass data to view $this->set('media', $media); } + + + + /** + * 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; + } + } ?> From 6791074027e9825c935ad44926c74b41360ea4d0 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 15 Feb 2014 11:27:24 +0100 Subject: [PATCH 045/340] delete Stylesheet- and CssAgent and use static CSS-file instead --- agents/intermediate/CssAgent.inc | 35 --------- agents/toplevel/StylesheetAgent.inc | 35 --------- controllers/CssController.inc | 103 --------------------------- controllers/StylesheetController.inc | 37 ---------- views/html/html.tpl | 2 +- views/stylesheet/css/desktop.tpl | 0 views/stylesheet/error/index.tpl | 1 - views/stylesheet/stylesheet.tpl | 4 -- www/css/desktop.css | 1 + 9 files changed, 2 insertions(+), 216 deletions(-) delete mode 100644 agents/intermediate/CssAgent.inc delete mode 100644 agents/toplevel/StylesheetAgent.inc delete mode 100644 controllers/CssController.inc delete mode 100644 controllers/StylesheetController.inc delete mode 100644 views/stylesheet/css/desktop.tpl delete mode 100644 views/stylesheet/error/index.tpl delete mode 100644 views/stylesheet/stylesheet.tpl create mode 100644 www/css/desktop.css diff --git a/agents/intermediate/CssAgent.inc b/agents/intermediate/CssAgent.inc deleted file mode 100644 index 860a75a5..00000000 --- a/agents/intermediate/CssAgent.inc +++ /dev/null @@ -1,35 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 CSS-stylesheets. - * - * @author Oliver Hanraths - */ - class CssAgent extends \nre\agents\IntermediateAgent - { - - - - - /** - * Action: index. - */ - public function index(\nre\core\Request $request, \nre\core\Response $response) - { - } - - } - -?> diff --git a/agents/toplevel/StylesheetAgent.inc b/agents/toplevel/StylesheetAgent.inc deleted file mode 100644 index 6231b066..00000000 --- a/agents/toplevel/StylesheetAgent.inc +++ /dev/null @@ -1,35 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 stylesheets. - * - * @author Oliver Hanraths - */ - class StylesheetAgent extends \nre\agents\ToplevelAgent - { - - - - - /** - * Action: index. - */ - public function index(\nre\core\Request $request, \nre\core\Response $response) - { - } - - } - -?> diff --git a/controllers/CssController.inc b/controllers/CssController.inc deleted file mode 100644 index 38e4a474..00000000 --- a/controllers/CssController.inc +++ /dev/null @@ -1,103 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 CssAgent to show CSS-stylesheets. - * - * @author Oliver Hanraths - */ - class CssController extends \hhu\z\Controller - { - - - - /** - * 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); - - // Set content-type for CSS-stylesheets - $response->addHeader('Content-type: text/css'); - - // Set expires-header for caching - $response->addHeader("Pragma: public"); - $response->addHeader("Expires: ".gmdate('r', time()+(60*60*24))); - $response->addHeader("Date: ".gmdate(\DateTime::RFC822)); - } - - - /** - * Action: desktop. - * - * Show CSS for desktops. - */ - public function desktop() - { - // Set HTTP-header - $this->setHeader(); - } - - - /** - * Action: adds. - * - * Show additional CSS for desktops. - */ - public function desktop_adds() - { - // Set HTTP-header - $this->setHeader(); - } - - - - - /** - * Set HTTP-header for caching. - */ - private function setHeader() - { - // Determine filename of template - $templateFilename = $this->getView()->getTemplateFilename(); - - // Determine date of last change - $templateLastModified = gmdate('r', filemtime($templateFilename)); - - // Create E-tag - $templateEtag = hash('sha256', $templateLastModified.$templateFilename); - - - // Set header - $this->response->addHeader("Last-Modified: ".$templateLastModified); - $this->response->addHeader("Etag: ".$templateEtag); - // HTTP-status - $headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE'); - $headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH'); - if( - !is_null($headerModifiedSince) && $templateLastModified < strtotime($headerModifiedSince) && - !is_null($headerNoneMatch) && $headerNoneMatch == $templateEtag - ) { - $this->response->setExit(true); - $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304)); - } - } - - } - -?> diff --git a/controllers/StylesheetController.inc b/controllers/StylesheetController.inc deleted file mode 100644 index d9bbfc3d..00000000 --- a/controllers/StylesheetController.inc +++ /dev/null @@ -1,37 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 StylesheetAgent to display stylesheets. - * - * @author Oliver Hanraths - */ - class StylesheetController extends \nre\core\Controller - { - - - - - /** - * Action: index. - * - * Show the stylesheet-structure - */ - public function index() - { - } - - } - -?> diff --git a/views/html/html.tpl b/views/html/html.tpl index a9f5c842..0eadc348 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -5,7 +5,7 @@ The Legend of Z - + diff --git a/views/stylesheet/css/desktop.tpl b/views/stylesheet/css/desktop.tpl deleted file mode 100644 index e69de29b..00000000 diff --git a/views/stylesheet/error/index.tpl b/views/stylesheet/error/index.tpl deleted file mode 100644 index f6ceec9b..00000000 --- a/views/stylesheet/error/index.tpl +++ /dev/null @@ -1 +0,0 @@ -Fehler: diff --git a/views/stylesheet/stylesheet.tpl b/views/stylesheet/stylesheet.tpl deleted file mode 100644 index c796c127..00000000 --- a/views/stylesheet/stylesheet.tpl +++ /dev/null @@ -1,4 +0,0 @@ -@charset "UTF-8"; - - - diff --git a/www/css/desktop.css b/www/css/desktop.css new file mode 100644 index 00000000..9f44090c --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1 @@ +@charset "UTF-8"; From 6f61da1b025f8e9fdff98753f385bb887969823b Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 15 Feb 2014 11:29:10 +0100 Subject: [PATCH 046/340] remove e?mail-addresses from introduction page --- views/html/introduction/index.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index 1d3fa615..aad666f6 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -3,15 +3,15 @@

              Entwickler:

              • - Oliver Hanraths <oliver(DOT)hanraths(AT)uni-duesseldorf(DOT)de>
                + Oliver Hanraths
                Programmierung und Datenbank
              • - Daniel Miskovic <daniel(DOT)miskovic(AT)uni-duesseldorf(DOT)de>
                + Daniel
                GUI und Webdesign
              • - Kathrin Knautz, B.A., M.A. <kathrin(DOT)knautz(AT)uni-duesseldorf(DOT)de>
                + Kathrin Knautz, B.A., M.A.
                Leitung
              From d8b3903cb884a3e11cafb1a7d2e364a0aaf2f6d9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 01:55:52 +0100 Subject: [PATCH 047/340] implement out- and abort-text for sidequests --- models/QuesttextsModel.inc | 2 +- views/html/quests/sidequest.tpl | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index aa1a054c..50a9f85a 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -74,7 +74,7 @@ public function getSidequesttextByUrl($sidequestId, $questtexttypeUrl, $pos) { $data = $this->db->query( - 'SELECT sidequesttexts.id, sidequesttexts.text, sidequesttexts.pos, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. + 'SELECT sidequesttexts.id, sidequesttexts.text, sidequesttexts.pos, sidequesttexts.out_text, sidequesttexts.abort_text, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. 'FROM sidequesttexts '. 'LEFT JOIN questtexttypes ON questtexttypes.id = sidequesttexts.questtexttype_id '. 'WHERE sidequesttexts.sidequest_id = ? AND questtexttypes.url = ? AND sidequesttexts.pos = ?', diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl index ff3c09ad..4527b3c8 100644 --- a/views/html/quests/sidequest.tpl +++ b/views/html/quests/sidequest.tpl @@ -14,6 +14,17 @@

              + + +
                +
              • +
              • +
              + +
              + + + 1) : ?>< / > From 3f30fad8f2a7e953738decc2a40fda192f4da61b Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 01:56:27 +0100 Subject: [PATCH 048/340] correct missing closing-tag --- views/html/quests/quest.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 330cb70e..afccd176 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -18,7 +18,7 @@
            • -
            • +
            From 305a086ae64c3ab8fe010a0f22e0c0a94e7d2ad9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 02:48:29 +0100 Subject: [PATCH 049/340] implement CharactergroupsAgent and include it in Seminary page --- agents/intermediate/CharactergroupsAgent.inc | 35 +++++ configs/AppConfig.inc | 2 + controllers/CharactergroupsController.inc | 131 +++++++++++++++++++ models/CharactergroupsModel.inc | 127 ++++++++++++++++++ views/html/charactergroups/group.tpl | 5 + views/html/charactergroups/groupsgroup.tpl | 10 ++ views/html/charactergroups/index.tpl | 9 ++ views/html/seminaries/seminary.tpl | 3 + 8 files changed, 322 insertions(+) create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl 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/configs/AppConfig.inc b/configs/AppConfig.inc index 3e8d92da..97c0a080 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -82,6 +82,7 @@ array('^([^/]+)/([^/]+)/?$', 'questgropus/questgroup/$1/$2', true), // z/// ⇒ z/quests/quest/// array('^([^/]+)/([^/]+)/([^/]+)/?$', 'quests/quest/$1/$2/3', true)*/ + array('charactergroups/(?!(index|groupsgroup|group))', 'charactergroups/index/$1', true), array('media/(.*)', 'media/$1?layout=binary', false), array('media/(.*)', 'media/index/$1', true) ); @@ -98,6 +99,7 @@ array('users/([^/]+)/(.*)', 'users/$2/$1', true), array('seminaries/seminary/(.*)', 'seminaries/$1', false), //array('seminaries/seminary/(.*)', '$1', false) + array('charactergroup/index/(.*)', 'charactergroup/$1', true), array('media/index/(.*)', 'media/$1', true) ); diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..1402e140 --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,131 @@ + + * @copyright 2014 Heinrich-Heine-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'); + /** + * 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']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('groups', $groups); + } + + + /** + * 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); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..4fc32b80 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,127 @@ + + * @copyright 2014 Heinrich-Heine-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 '. + 'FROM charactergroups '. + 'WHERE charactergroupsgroup_id = ?', + 'i', + $groupsgroupId + ); + } + + + /** + * 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 '. + 'FROM charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + } + +?> diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..6b50852c --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,5 @@ +

            +

            +

            +

            +
            diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..ebdd8973 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,10 @@ +

            +

            +

            +

            + +
              + +
            • + +
            diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..584428fb --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,9 @@ +

            +

            +

            + +
              + +
            • + +
            diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 2f63cdfa..89378d65 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -4,6 +4,9 @@
          • +

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

            From 3d82dd7297c239ecce8a3c11f5bd311d6b2f2588 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 13:17:59 +0100 Subject: [PATCH 050/340] create method for savely printing text --- app/Utils.inc | 27 +++++++++++++++++++++++++++ views/html/questgroups/questgroup.tpl | 2 +- views/html/quests/quest.tpl | 4 ++-- views/html/quests/sidequest.tpl | 4 ++-- views/html/seminaries/seminary.tpl | 2 +- 5 files changed, 33 insertions(+), 6 deletions(-) diff --git a/app/Utils.inc b/app/Utils.inc index 1e3527c1..76ccc7fe 100644 --- a/app/Utils.inc +++ b/app/Utils.inc @@ -19,6 +19,33 @@ */ 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'); + } + } ?> diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index b78d1fe4..5699bfa7 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -6,7 +6,7 @@

            :

            -

            +

            diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index afccd176..9a8b7376 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -10,7 +10,7 @@

            -

            +

              @@ -35,6 +35,6 @@

              -

              +

              diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl index 4527b3c8..b1bff276 100644 --- a/views/html/quests/sidequest.tpl +++ b/views/html/quests/sidequest.tpl @@ -12,7 +12,7 @@

              -

              +

              @@ -33,6 +33,6 @@

              -

              +

              diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 89378d65..3e93b154 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -11,7 +11,7 @@ format(new \DateTime($seminary['created'])))?>

              Beschreibung

              -

              +

              From 3517d6d0ed09f3417e35fee34eb76fff9cb35e51 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 13:20:58 +0100 Subject: [PATCH 051/340] implement Quests for Character groups (-groups) --- .../CharactergroupsquestsAgent.inc | 35 +++++ configs/AppConfig.inc | 40 +++--- controllers/CharactergroupsController.inc | 10 +- .../CharactergroupsquestsController.inc | 91 ++++++++++++ models/CharactergroupsquestsModel.inc | 136 ++++++++++++++++++ views/html/charactergroups/group.tpl | 19 ++- views/html/charactergroups/groupsgroup.tpl | 10 +- views/html/charactergroupsquests/quest.tpl | 46 ++++++ 8 files changed, 364 insertions(+), 23 deletions(-) create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 views/html/charactergroupsquests/quest.tpl 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/configs/AppConfig.inc b/configs/AppConfig.inc index 97c0a080..4dd71853 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -71,20 +71,21 @@ * @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), + 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('charactergroups/(?!(index|groupsgroup|group))', 'charactergroups/index/$1', true), - array('media/(.*)', 'media/$1?layout=binary', false), - array('media/(.*)', 'media/index/$1', true) + 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('charactergroups/(?!(index|groupsgroup|group))', 'charactergroups/index/$1', true), + array('charactergroupsquests/(?!(quest))', 'charactergroupsquests/quest/$1', true), + array('media/(.*)', 'media/$1?layout=binary', false), + array('media/(.*)', 'media/index/$1', true) ); @@ -95,12 +96,13 @@ * @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('charactergroup/index/(.*)', 'charactergroup/$1', true), - array('media/index/(.*)', 'media/$1', true) + array('users/user/(.*)', 'users/$1', true), + array('users/([^/]+)/(.*)', 'users/$2/$1', true), + array('seminaries/seminary/(.*)', 'seminaries/$1', false), + //array('seminaries/seminary/(.*)', '$1', false) + array('charactergroup/index/(.*)', 'charactergroup/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('media/index/(.*)', 'media/$1', true) ); diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc index 1402e140..e98e3dd1 100644 --- a/controllers/CharactergroupsController.inc +++ b/controllers/CharactergroupsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'charactergroups'); + public $models = array('seminaries', 'charactergroups', 'charactergroupsquests'); /** * User permissions * @@ -89,11 +89,15 @@ // 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); } @@ -119,11 +123,15 @@ // Get Character group $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl); + // 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('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/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/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 6b50852c..dc3932ef 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -1,5 +1,20 @@

              -

              -

              +

              +

              + +
              +

              + + + + + + + + + + +
              format(new \DateTime($quest['created']))?>/ XPs
              +
              diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl index ebdd8973..9a24a91b 100644 --- a/views/html/charactergroups/groupsgroup.tpl +++ b/views/html/charactergroups/groupsgroup.tpl @@ -1,6 +1,6 @@

              -

              +

                @@ -8,3 +8,11 @@
              + + +

              +
                + +
              • + +
              diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..cf0e9923 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,46 @@ +

              +

              +

              +

              + + + + + +
              +

              XPs:

              +

              +

              + +

              +

              + +
              + + +
              +

              +

              +
              + + +
              +

              +

              +
              + + +
              +

              + + + + + + + + + + +
              format(new \DateTime($group['created']))?>/ XPs
              +
              From 2f65a89ef4bff920104910d51b828b11dd37c3c5 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 15:31:08 +0100 Subject: [PATCH 052/340] 1) implement basic CharactersAgent 2) use view for Character groups --- agents/intermediate/CharactersAgent.inc | 43 ++++++++++ app/controllers/ToplevelController.inc | 23 ++++- configs/AppConfig.inc | 2 + controllers/CharactergroupsController.inc | 4 + controllers/CharactersController.inc | 97 ++++++++++++++++++++++ controllers/HtmlController.inc | 2 + models/CharactergroupsModel.inc | 28 ++++++- models/CharactersModel.inc | 75 ++++++++++++++++- views/html/charactergroups/group.tpl | 13 +++ views/html/charactergroups/groupsgroup.tpl | 2 +- views/html/characters/character.tpl | 19 +++++ views/html/characters/index.tpl | 8 ++ views/html/html.tpl | 3 + views/html/seminaries/seminary.tpl | 1 + views/html/users/user.tpl | 2 +- 15 files changed, 311 insertions(+), 11 deletions(-) create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 controllers/CharactersController.inc create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl 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/app/controllers/ToplevelController.inc b/app/controllers/ToplevelController.inc index 74f63667..544748a9 100644 --- a/app/controllers/ToplevelController.inc +++ b/app/controllers/ToplevelController.inc @@ -24,13 +24,23 @@ * * @var array */ - public $models = array('users', 'userroles'); + 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; @@ -67,6 +77,15 @@ // Get userdata try { static::$user = $this->Users->getUserById($this->Auth->getUserId()); + + // Character + $controller = $this->agent->getIntermediateAgent()->controller; + if(in_array(\hhu\z\controllers\SeminaryRoleController::class, class_parents($controller))) + { + $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) { } @@ -76,6 +95,8 @@ // Set userdata $this->set('loggedUser', static::$user); + $this->set('loggedSeminary', static::$seminary); + $this->set('loggedCharacter', static::$character); } diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 4dd71853..8c388dce 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -82,6 +82,7 @@ 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), @@ -100,6 +101,7 @@ 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), array('media/index/(.*)', 'media/$1', true) diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc index e98e3dd1..1028d28c 100644 --- a/controllers/CharactergroupsController.inc +++ b/controllers/CharactergroupsController.inc @@ -123,6 +123,9 @@ // 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']); @@ -131,6 +134,7 @@ $this->set('seminary', $seminary); $this->set('groupsgroup', $groupsgroup); $this->set('group', $group); + $this->set('characters', $characters); $this->set('quests', $quests); } diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc new file mode 100644 index 00000000..27c4edcb --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,97 @@ + + * @copyright 2014 Heinrich-Heine-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\Controller + { + /** + * 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'); + + + + + /** + * 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']); + + + // 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); + + // 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('user', $user); + $this->set('groups', $groups); + } + + } + +?> diff --git a/controllers/HtmlController.inc b/controllers/HtmlController.inc index d7660f5a..20795d5b 100644 --- a/controllers/HtmlController.inc +++ b/controllers/HtmlController.inc @@ -50,6 +50,8 @@ // Set userdata $this->set('loggedUser', static::$user); + $this->set('loggedSeminary', static::$seminary); + $this->set('loggedCharacter', static::$character); } } diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc index 4fc32b80..ce6e8b61 100644 --- a/models/CharactergroupsModel.inc +++ b/models/CharactergroupsModel.inc @@ -88,8 +88,8 @@ public function getGroupsForGroupsgroup($groupsgroupId) { return $this->db->query( - 'SELECT id, name, url '. - 'FROM charactergroups '. + 'SELECT id, name, url, xps '. + 'FROM v_charactergroups '. 'WHERE charactergroupsgroup_id = ?', 'i', $groupsgroupId @@ -97,6 +97,26 @@ } + /** + * 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. * @@ -108,8 +128,8 @@ public function getGroupByUrl($groupsgroupId, $groupUrl) { $data = $this->db->query( - 'SELECT id, name, url '. - 'FROM charactergroups '. + 'SELECT id, name, url, xps '. + 'FROM v_charactergroups '. 'WHERE charactergroupsgroup_id = ? AND url = ?', 'is', $groupsgroupId, $groupUrl diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index c62f294c..0f637d19 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -43,8 +43,8 @@ public function getCharactersForUser($userId) { return $this->db->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, 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 characters '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, 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 = ?', @@ -54,6 +54,46 @@ } + /** + * 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, 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, 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. * @@ -65,8 +105,8 @@ public function getCharacterForUserAndSeminary($userId, $seminaryId) { $data = $this->db->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. - 'FROM characters '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, 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', @@ -80,6 +120,33 @@ 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, 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 charactertypes.seminary_id = ? AND characters.url = ?', + 'is', + $seminaryId, $characterUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + } ?> diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index dc3932ef..f46b24b4 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -4,6 +4,19 @@

              +
              + XPs: +
              + +
              +

              +
                + +
              • ( XPs)
              • + +
              +
              +

              diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl index 9a24a91b..3108b483 100644 --- a/views/html/charactergroups/groupsgroup.tpl +++ b/views/html/charactergroups/groupsgroup.tpl @@ -5,7 +5,7 @@ diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl new file mode 100644 index 00000000..aa8bdd78 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,19 @@ +

              +

              +

              + +
              +

              + XPs:
              + :
              +

              +
              + +
              +

              +
                + +
              • ( XPs)
              • + +
              +
              diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..362d60c1 --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,8 @@ +

              +

              + +
                + +
              • ( XPs)
              • + +
              diff --git a/views/html/html.tpl b/views/html/html.tpl index 0eadc348..ad8f4648 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -17,6 +17,9 @@
              + + ( XPs) +
              diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 3e93b154..3ff9b9c0 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -5,6 +5,7 @@
            • diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 2c081f2d..9be97544 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -11,7 +11,7 @@

                -
              • ()
              • +
              • ( XPs) ()
              From 0ef30ac1ca12d0c9f4c8e819ef7bae430f2ef667 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 16 Feb 2014 15:46:18 +0100 Subject: [PATCH 053/340] correct classname determination for older PHP-versions --- app/controllers/ToplevelController.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/ToplevelController.inc b/app/controllers/ToplevelController.inc index 544748a9..30558c91 100644 --- a/app/controllers/ToplevelController.inc +++ b/app/controllers/ToplevelController.inc @@ -80,7 +80,7 @@ // Character $controller = $this->agent->getIntermediateAgent()->controller; - if(in_array(\hhu\z\controllers\SeminaryRoleController::class, class_parents($controller))) + if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) { $seminaryUrl = $this->request->getParam(3); static::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); From 16189f5d122d59be7e4ceed66dde8821b9d6669f Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 17 Feb 2014 02:18:01 +0100 Subject: [PATCH 054/340] correct typo in template for Sidequests --- views/html/quests/sidequest.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl index b1bff276..1cb888ce 100644 --- a/views/html/quests/sidequest.tpl +++ b/views/html/quests/sidequest.tpl @@ -33,6 +33,6 @@

              -

              +

              From e356b71b175909ff8c79010d501166d516a1dbfb Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 17 Feb 2014 02:19:00 +0100 Subject: [PATCH 055/340] implement different media for Quest- and Sidequesttexts (Ticket #26) --- controllers/QuestsController.inc | 11 ++++++++--- models/QuesttextsModel.inc | 4 ++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index dda3e718..b5df177c 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -95,12 +95,14 @@ // Media $questmedia = null; - if(!is_null($quest['questsmedia_id'])) { + if(!is_null($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']); } - // Pass data to view $this->set('seminary', $seminary); $this->set('questgroup', $questgroup); @@ -166,7 +168,10 @@ // Media $sidequestmedia = null; - if(!is_null($sidequest['questsmedia_id'])) { + if(!is_null($sidequesttext) && !empty($sidequesttext['questsmedia_id'])) { + $sidequestmedia = $this->Media->getMediaById($sidequesttext['questsmedia_id']); + } + elseif(!is_null($sidequest['questsmedia_id'])) { $sidequestmedia = $this->Media->getMediaById($sidequest['questsmedia_id']); } diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index 50a9f85a..3a7d8526 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -46,7 +46,7 @@ public function getQuesttextByUrl($questId, $questtexttypeUrl, $pos) { $data = $this->db->query( - 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexts.out_text, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. + 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexts.out_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 = ?', @@ -74,7 +74,7 @@ public function getSidequesttextByUrl($sidequestId, $questtexttypeUrl, $pos) { $data = $this->db->query( - 'SELECT sidequesttexts.id, sidequesttexts.text, sidequesttexts.pos, sidequesttexts.out_text, sidequesttexts.abort_text, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. + 'SELECT sidequesttexts.id, sidequesttexts.text, sidequesttexts.pos, sidequesttexts.out_text, sidequesttexts.abort_text, sidequesttexts.questsmedia_id, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. 'FROM sidequesttexts '. 'LEFT JOIN questtexttypes ON questtexttypes.id = sidequesttexts.questtexttype_id '. 'WHERE sidequesttexts.sidequest_id = ? AND questtexttypes.url = ? AND sidequesttexts.pos = ?', From 59d358005fc01f77e69cedc568980a7975b3b0b7 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 17 Feb 2014 02:30:14 +0100 Subject: [PATCH 056/340] implement group leaders for Character groups (Ticket #23) --- models/CharactersModel.inc | 2 +- views/html/charactergroups/group.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 0f637d19..2bbe7744 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -83,7 +83,7 @@ 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, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + '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 '. diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index f46b24b4..96146f31 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -12,7 +12,7 @@

                -
              • ( XPs)
              • +
              • ( XPs) 0) : ?>()
              From 82f75fff4790e4928a03bacf0bef32fd7f0f5ed4 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 17 Feb 2014 02:30:21 +0100 Subject: [PATCH 057/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2106 -> 2519 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 301 ++++++++++++-------- 2 files changed, 176 insertions(+), 125 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 8680ecbba6a35368600de60d9e20a7e18e7ec4ce..09213b4bc7d8d63e6ef8815628bcb22d800dd269 100644 GIT binary patch literal 2519 zcmaKsO>7)B6vtgCU(099S1FXbKp-iabT?GA-6){8*+wlXS&}V{IDou6FFQjV&&u{B z4HwF_5=ccLKq{0AQhNc!p$F&zsX{$*K|-A1kPBxdB(D75jI+BH71r24Kl}Ol`Ppy& z*uM2+fwC9%2~@pBh)MA3Eoe|K-73Tb;AQYp@O!WV{sKM-{tn&`{uQ`os}OfX-WIqE zychBqco%p8ybU}IvfUZ*9&j;`!n_4<$M_kL_j@Dy&_UFR*BFKKf335mhkoQ{#cY-#^?}HqVkAi#& zsoyl)r0540dY@g5r1I~RC9@KccOTm)<2S706d z4TLLVJA%Z1?*v)z1rSr>5XgQX3Gy7s`!9lcG9m%-3617)a5cyuf)7K!0J5F$K#s!? zVg6?j;VkZe&%uTF<(Tj~E_|*%MNfzkkmJj-;lg(^R`kj^?GAm$Q>ag(K88Aq%7xFy z`N2MNe0HI7yq`g3zu->cCvZdIoRO$|QThEmjmq)hm~9q*hr?w%1dh$Ks2r>3P{q+i zS=I4m<&4epp8L;$d@U#EhsM#hq&!JOYtqq5YLs0c9?KnBF-@AF)1MD=m~$PQ_I#Qd z(TGz&&?=-tr(tboMPuXa`I9Qu(cw6@~Y+9o5gvjFf^NWQzOu3xd)st z9T%?-d&xp!zNwsB%WN#d>E<&{PR!@rE|h0VxtnCUj-^iqCy^UxFO0a2v!6>uLF;wd zCK(rpjb9$t=WPbMqa$wYW(!)%EsIu?y3$*TPF4g)Y0*l(CUJ_R$&FDfrR2ov%!Jb^ zCpyY>NQ+Kpyh=@Kx_t|!4m67U3W#NUU5;QIgI1KD3rpS9;So?A#+_Ov(X`py)bY{r zIC`C|!*$hV3!#{$E*WSp$u}y^xmHxX2h2v(couazQLXKdsuNM|fUHi`Ck{SetyQZ~ zjOJ-IWh*z=J5=h;D(9n?RmN%MGh3IHS!KGp>QdAqwK62v5%;&I80~iNSeFOesjn>T0p#rE6Cc%T{m5Ydk}pS=>o@{_MCdPE(x0%1swV zNN_I(3C<^-$J5oOaFxVDrZwJBX#lzU1?Tkzxt7{xopurl)#B)0u91z&Aip9@?T(6vOY|?9w8j~1BrhDZ<%Hz delta 867 zcmZ9~Pe_zO7{~F)e0TTVGXHO`R{jA6gUDSKLWvL$fwh4I($!G0Mcr3S1rM?Z4~Cb( zAtDuwI@z@@N!`M0co6YY1VVZ7q=!!R{q4ScX?SNo^SGwZ^e7icKI@gkmJ9iCwo`sGEFc!~Q?yo|jV$3cu>2CMNAR^V9pd={f-d0Px$ zEThkWwNO_aU|Xn(-k~t}d)Q;DX%Qk7Y$;unl1_qd=9v@&5M^TY4VGXXLGV~fX?+$X=7aGm`6_v3AjIh4_ zq{C&uXtcm7R^lI21b(dez9uw@im(OK*n$1{7`benMjP8ejr)K~{U_9Xd#GRZ4f9v& z9EA_gF~R*`)W8IdB5Fo`kU|n+wNx#jH0t~Rix$;_I#0@kev9T+)H+r=YhjrwsPrk- zie3x0Qng?URpmNWDKC`?5)RhPi7`;(<#>*eP7n& genelmSFvGt7Q5r_`i*YVPrGgZt-Bwubkp&vf1@{3i~s-t diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index e5954918..086c0741 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,39 +1,106 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-02-12 23:09+0100\n" -"PO-Revision-Date: 2014-02-12 23:10+0100\n" +"POT-Creation-Date: 2014-02-17 02:28+0100\n" +"PO-Revision-Date: 2014-02-17 02:29+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.3\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/html/menu/index.tpl:2 ../../../views/html/users/login.tpl:1 -#: ../../../views/html/users/user.tpl:1 ../../../views/html/users/index.tpl:1 -#: ../../../views/html/users/edit.tpl:1 ../../../views/html/users/delete.tpl:1 -#: ../../../views/html/users/create.tpl:1 -msgid "Users" -msgstr "Benutzer" +#: ../../../views/binary/error/index.tpl:1 +#: ../../../views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" +#: ../../../views/html/charactergroups/group.tpl:1 +#: ../../../views/html/charactergroups/groupsgroup.tpl:1 +#: ../../../views/html/charactergroups/index.tpl:1 +#: ../../../views/html/charactergroupsquests/quest.tpl:1 #: ../../../views/html/menu/index.tpl:3 -#: ../../../views/html/seminaries/seminary.tpl:1 -#: ../../../views/html/seminaries/index.tpl:1 -#: ../../../views/html/seminaries/edit.tpl:1 -#: ../../../views/html/seminaries/delete.tpl:1 -#: ../../../views/html/seminaries/create.tpl:1 #: ../../../views/html/questgroups/questgroup.tpl:1 #: ../../../views/html/quests/quest.tpl:1 #: ../../../views/html/quests/sidequest.tpl:1 +#: ../../../views/html/seminaries/create.tpl:1 +#: ../../../views/html/seminaries/delete.tpl:1 +#: ../../../views/html/seminaries/edit.tpl:1 +#: ../../../views/html/seminaries/index.tpl:1 +#: ../../../views/html/seminaries/seminary.tpl:1 msgid "Seminaries" msgstr "Kurse" +#: ../../../views/html/charactergroups/group.tpl:3 +#: ../../../views/html/charactergroups/groupsgroup.tpl:3 +#: ../../../views/html/charactergroups/index.tpl:3 +#: ../../../views/html/characters/character.tpl:13 +#: ../../../views/html/seminaries/seminary.tpl:9 +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:8 +#: ../../../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:24 +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 +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:8 +msgid "User" +msgstr "Benutzer" + +#: ../../../views/html/html.tpl:21 +msgid "as" +msgstr "als" + +#: ../../../views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: ../../../views/html/menu/index.tpl:2 ../../../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:5 ../../../views/html/users/login.tpl:2 #: ../../../views/html/users/login.tpl:11 msgid "Login" @@ -43,140 +110,124 @@ msgstr "Login" msgid "Logout" msgstr "Logout" -#: ../../../views/html/users/login.tpl:6 ../../../views/html/users/login.tpl:7 -#: ../../../views/html/users/edit.tpl:6 ../../../views/html/users/edit.tpl:7 -#: ../../../views/html/users/create.tpl:6 -#: ../../../views/html/users/create.tpl:7 -msgid "Username" -msgstr "Benutzername" +#: ../../../views/html/questgroups/questgroup.tpl:30 +msgid "containing optional Quests" +msgstr "Enthaltene optionale Quests" -#: ../../../views/html/users/login.tpl:8 ../../../views/html/users/login.tpl:9 -#: ../../../views/html/users/edit.tpl:10 ../../../views/html/users/edit.tpl:11 -#: ../../../views/html/users/create.tpl:10 -#: ../../../views/html/users/create.tpl:11 -msgid "Password" -msgstr "Passwort" +#: ../../../views/html/quests/quest.tpl:37 +#: ../../../views/html/quests/sidequest.tpl:35 +msgid "Task" +msgstr "Aufgabe" -#: ../../../views/html/users/user.tpl:4 ../../../views/html/users/edit.tpl:2 -msgid "Edit user" -msgstr "Benutzer bearbeiten" +#: ../../../views/html/quests/sidequest.tpl:9 +msgid "This Quest is optional" +msgstr "Diese Quest ist optional" -#: ../../../views/html/users/user.tpl:5 ../../../views/html/users/delete.tpl:2 -msgid "Delete user" -msgstr "Benutzer löschen" +#: ../../../views/html/seminaries/create.tpl:2 +msgid "New seminary" +msgstr "Neuer Kurs" -#: ../../../views/html/users/user.tpl:8 ../../../views/html/users/index.tpl:10 -#, php-format -msgid "registered on %s" -msgstr "registriert am %s" +#: ../../../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/users/user.tpl:11 -msgid "Characters" -msgstr "Charaktere" - -#: ../../../views/html/users/user.tpl:18 -msgid "Roles" -msgstr "Rollen" - -#: ../../../views/html/users/index.tpl:3 -msgid "Create new user" -msgstr "Neuen Benutzer erstellen" - -#: ../../../views/html/users/edit.tpl:8 ../../../views/html/users/edit.tpl:9 -#: ../../../views/html/users/create.tpl:8 -#: ../../../views/html/users/create.tpl:9 -msgid "E‑Mail-Address" -msgstr "E‑Mail-Adresse" - -#: ../../../views/html/users/edit.tpl:13 -#: ../../../views/html/seminaries/edit.tpl:9 -msgid "save" -msgstr "speichern" - -#: ../../../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/delete.tpl:6 -#: ../../../views/html/seminaries/delete.tpl:6 -msgid "delete" -msgstr "löschen" - -#: ../../../views/html/users/delete.tpl:7 -#: ../../../views/html/seminaries/delete.tpl:7 -msgid "cancel" -msgstr "abbrechen" - -#: ../../../views/html/users/create.tpl:2 -msgid "New user" -msgstr "Neuer Benutzer" - -#: ../../../views/html/users/create.tpl:13 #: ../../../views/html/seminaries/create.tpl:9 +#: ../../../views/html/users/create.tpl:13 msgid "create" msgstr "erstellen" -#: ../../../views/html/error/index.tpl:1 -msgid "Error" -msgstr "Fehler" - -#: ../../../views/html/seminaries/seminary.tpl:4 -#: ../../../views/html/seminaries/edit.tpl:2 -msgid "Edit seminary" -msgstr "Kurs bearbeiten" - -#: ../../../views/html/seminaries/seminary.tpl:5 #: ../../../views/html/seminaries/delete.tpl:2 +#: ../../../views/html/seminaries/seminary.tpl:5 msgid "Delete seminary" msgstr "Kurs löschen" -#: ../../../views/html/seminaries/seminary.tpl:8 -#: ../../../views/html/seminaries/index.tpl:10 -#, php-format -msgid "created by %s on %s" -msgstr "erstellt von %s am %s" - -#: ../../../views/html/seminaries/index.tpl:3 -msgid "Create new seminary" -msgstr "Neuen Kurs erstellen" - -#: ../../../views/html/seminaries/edit.tpl:6 -#: ../../../views/html/seminaries/edit.tpl:7 -#: ../../../views/html/seminaries/create.tpl:6 -#: ../../../views/html/seminaries/create.tpl:7 -msgid "Title" -msgstr "Titel" - #: ../../../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/create.tpl:2 -msgid "New seminary" -msgstr "Neuer Kurs" +#: ../../../views/html/seminaries/delete.tpl:6 +#: ../../../views/html/users/delete.tpl:6 +msgid "delete" +msgstr "löschen" -#: ../../../views/html/questgroups/questgroup.tpl:23 -msgid "Quests" -msgstr "Quests" +#: ../../../views/html/seminaries/delete.tpl:7 +#: ../../../views/html/users/delete.tpl:7 +msgid "cancel" +msgstr "abbrechen" -#: ../../../views/html/questgroups/questgroup.tpl:29 -msgid "containing optional Quests" -msgstr "Enthaltene optionale Quests" +#: ../../../views/html/seminaries/edit.tpl:2 +#: ../../../views/html/seminaries/seminary.tpl:4 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" -#: ../../../views/html/introduction/index.tpl:1 -msgid "Introduction" -msgstr "Einführung" +#: ../../../views/html/seminaries/edit.tpl:9 +#: ../../../views/html/users/edit.tpl:13 +msgid "save" +msgstr "speichern" -#: ../../../views/html/quests/quest.tpl:33 -#: ../../../views/html/quests/sidequest.tpl:20 -msgid "Task" -msgstr "Aufgabe" +#: ../../../views/html/seminaries/index.tpl:3 +msgid "Create new seminary" +msgstr "Neuen Kurs erstellen" -#: ../../../views/html/quests/sidequest.tpl:8 -msgid "This Quest is optional" -msgstr "Diese Quest ist optional" +#: ../../../views/html/seminaries/index.tpl:10 +#: ../../../views/html/seminaries/seminary.tpl:12 +#, 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 "created by %s on %s at %s" #~ msgstr "erstellt von %s am %s um %s Uhr" From 3af0e9d5706e3b1e43e74fbe772a596da4d5cf24 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 17 Feb 2014 02:54:49 +0100 Subject: [PATCH 058/340] implement fields for Characters for Seminaries (Ticket #23) --- controllers/CharactersController.inc | 6 ++- models/SeminarycharacterfieldsModel.inc | 58 +++++++++++++++++++++++++ views/html/characters/character.tpl | 3 ++ 3 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 models/SeminarycharacterfieldsModel.inc diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 27c4edcb..4e65ed40 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -33,7 +33,7 @@ * * @var array */ - public $models = array('seminaries', 'characters', 'users', 'charactergroups'); + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'seminarycharacterfields'); @@ -78,6 +78,9 @@ // Get Character $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + // Get Seminarycharacterfields + $characterfields = $this->Seminarycharacterfields->getFieldsForCharacter($character['id']); + // Get User $user = $this->Users->getUserById($character['user_id']); @@ -88,6 +91,7 @@ // 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/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/views/html/characters/character.tpl b/views/html/characters/character.tpl index aa8bdd78..dcc9cea3 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -6,6 +6,9 @@

              XPs:
              :
              + + :
              +

              From f1b5affa799c4bdada42155bf90a14fdeaff24d0 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 23 Feb 2014 14:45:07 +0100 Subject: [PATCH 059/340] implementing abstract classes for QuesttypeAgents (-Agent, -Controller, -Model and -View) --- app/QuesttypeAgent.inc | 231 +++++++++++++++++ app/QuesttypeController.inc | 235 ++++++++++++++++++ app/QuesttypeModel.inc | 154 ++++++++++++ app/QuesttypeView.inc | 76 ++++++ .../QuesttypeAgentNotFoundException.inc | 77 ++++++ .../QuesttypeAgentNotValidException.inc | 77 ++++++ .../QuesttypeControllerNotFoundException.inc | 77 ++++++ .../QuesttypeControllerNotValidException.inc | 77 ++++++ .../QuesttypeModelNotFoundException.inc | 77 ++++++ .../QuesttypeModelNotValidException.inc | 77 ++++++ configs/AppConfig.inc | 3 +- core/Controller.inc | 6 +- core/View.inc | 4 +- 13 files changed, 1165 insertions(+), 6 deletions(-) create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc diff --git a/app/QuesttypeAgent.inc b/app/QuesttypeAgent.inc new file mode 100644 index 00000000..9f104be1 --- /dev/null +++ b/app/QuesttypeAgent.inc @@ -0,0 +1,231 @@ + + * @copyright 2014 Heinrich-Heine-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); + } + + + + + /** + * 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 = \nre\configs\CoreConfig::$defaults['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..5c7b9e04 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,235 @@ + + * @copyright 2014 Heinrich-Heine-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 \nre\core\Controller + { + + + + + /** + * 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); + } + + } + +?> 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/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/configs/AppConfig.inc b/configs/AppConfig.inc index 8c388dce..5b5a7558 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -60,7 +60,8 @@ */ public static $dirs = array( 'locale' => 'locale', - 'media' => 'media' + 'media' => 'media', + 'questtypes' => 'questtypes' ); diff --git a/core/Controller.inc b/core/Controller.inc index 55a7675a..c2814e99 100644 --- a/core/Controller.inc +++ b/core/Controller.inc @@ -30,7 +30,7 @@ * * @var View */ - private $view = null; + protected $view = null; /** * Data to pass to the View * @@ -348,7 +348,7 @@ * @throws ModelNotValidException * @throws ModelNotFoundException */ - private function loadModels() + protected function loadModels() { // Determine Models $explicit = false; @@ -403,7 +403,7 @@ * @param string $layoutName Name of the current Layout * @param string $action Current Action */ - private function loadView($layoutName, $action) + protected function loadView($layoutName, $action) { // Check Layout name if(is_null($layoutName)) { diff --git a/core/View.inc b/core/View.inc index 513bd953..546ddb6e 100644 --- a/core/View.inc +++ b/core/View.inc @@ -26,7 +26,7 @@ * * @var string */ - private $templateFilename; + protected $templateFilename; @@ -57,7 +57,7 @@ * @param string $action Current Action * @param bool $isToplevel Agent is a ToplevelAgent */ - private function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) { // Create template filename // LayoutName From 2923bd8421e9fbeef2c91404e638fdfd28d840ff Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 26 Feb 2014 20:20:38 +0100 Subject: [PATCH 060/340] provide methods to mark Quests as solved and unsolved for QuesttypeControllers --- app/QuesttypeController.inc | 73 ++++++++++++++++++++++++++++++++++++- models/QuesttypesModel.inc | 62 +++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 models/QuesttypesModel.inc diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 5c7b9e04..603626ac 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -17,8 +17,14 @@ * * @author Oliver Hanraths */ - abstract class QuesttypeController extends \nre\core\Controller + abstract class QuesttypeController extends \hhu\z\Controller { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'quests', 'characters'); @@ -230,6 +236,71 @@ $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)); + + // Sidequest + $sidequest = null; + if($this->request->getParam(2) == 'sidequest') { + $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $this->request->getParam(6)); + } + + // 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('solved', $sidequest != null ? 6 : 5)); + } + + + /** + * 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)); + + // Sidequest + $sidequest = null; + if($this->request->getParam(2) == 'sidequest') { + $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $this->request->getParam(6)); + } + + // 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('unsolved', $sidequest != null ? 6 : 5)); + } + } ?> 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]; + } + + } + +?> From 1ef9b1d6e64b8e772c12d60cf0085b61975e1ce9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 26 Feb 2014 20:23:15 +0100 Subject: [PATCH 061/340] integrate loading, running and rendering of QuesttypeAgent for a Quest --- controllers/QuestsController.inc | 201 ++++++++++++++++++++++++------- models/QuestsModel.inc | 34 +++++- views/html/quests/quest.tpl | 15 ++- views/html/quests/sidequest.tpl | 15 ++- 4 files changed, 215 insertions(+), 50 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index b5df177c..1fcd3f56 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media'); + public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media', 'questtypes'); /** * User permissions * @@ -51,10 +51,10 @@ * Show a quest and its task. * * @throws IdNotFoundException - * @param string $seminaryUrl URL-Title of a Seminary - * @param string $questgroupUrl URL-Title of a Questgroup - * @param string $questUrl URL-Title of a Quest - * @param string $questtexttypeUrl URL-Title of a Questtexttype + * @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) @@ -70,15 +70,35 @@ // Get Questtext $questtext = null; - $questtexttypes = $this->Questtexts->getQuesttexttypes(); - $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); if(is_null($questtexttypeUrl)) { $questtexttypeUrl = 'Prolog'; } - $questtextCount = $this->Questtexts->getQuesttextsCountForQuest($quest['id'], $questtexttypeUrl); - if($questtextCount > 0) + // Quest solved + if($questtexttypeUrl == 'solved') { - if(in_array($questtexttypeUrl, $questtexttypes)) + $questtext = array( + 'type' => 'solved', + 'text' => $quest['right_text'] + ); + } + // Quest unsolved + elseif($questtexttypeUrl == 'unsolved') + { + $questtext = array( + 'type' => 'unsolved', + 'text' => $quest['wrong_text'] + ); + } + // Text + else + { + // Text type + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); + // Text count + $questtextCount = $this->Questtexts->getQuesttextsCountForQuest($quest['id'], $questtexttypeUrl); + // Get text + if($questtextCount > 0 && in_array($questtexttypeUrl, $questtexttypes)) { $questtextPos = max(intval($questtextPos), 1); $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); @@ -87,43 +107,49 @@ } } - // Show task only for Prologes - $showTask = false; - if($questtext['type'] == 'Prolog') { - $showTask = true; - } - // Media $questmedia = null; - if(!is_null($questtext) && !empty($questtext['questsmedia_id'])) { + 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($questtext['type'] == 'Prolog') + { + // Questtype + $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + + // Task + $task = $this->runAndRenderTask($questtype['classname']); + } + // Pass data to view $this->set('seminary', $seminary); $this->set('questgroup', $questgroup); $this->set('questtext', $questtext); $this->set('quest', $quest); - $this->set('showtask', $showTask); + $this->set('task', $task); $this->set('media', $questmedia); } + /** * Action: sidequest. * * Show a sidequest and its task. * * @throws IdNotFoundException - * @param string $seminaryUrl URL-Title of a Seminary - * @param string $questgroupUrl URL-Title of a Questgroup - * @param string $questUrl URL-Title of a Quest - * @param string $sidequestUrl URL-Title of a Sidequest - * @param string $questtexttypeUrl URL-Title of a Questtexttype - * @param int $questtextPos Position of Questtext + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + * @param string $sidequestUrl URL-Title of Sidequest + * @param string $sidequesttexttypeUrl URL-Title of Sidequesttexttype + * @param int $sidequesttextPos Position of Sidequesttext */ public function sidequest($seminaryUrl, $questgroupUrl, $questUrl, $sidequestUrl, $sidequesttexttypeUrl=null, $sidequesttextPos=1) { @@ -144,26 +170,43 @@ // Get Sidequesttext $sidequesttext = null; - $questtexttypes = $this->Questtexts->getQuesttexttypes(); - $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); if(is_null($sidequesttexttypeUrl)) { $sidequesttexttypeUrl = 'Prolog'; } - $sidequesttextCount = $this->Questtexts->getQuesttextsCountForSidequest($sidequest['id'], $sidequesttexttypeUrl); - if($sidequesttextCount > 0) + // Quest solved + if($sidequesttexttypeUrl == 'solved') { - if(in_array($sidequesttexttypeUrl, $questtexttypes)) - { - $sidequesttextPos = max(intval($sidequesttextPos), 1); - $sidequesttext = $this->Questtexts->getSidequesttextByUrl($sidequest['id'], $sidequesttexttypeUrl, $sidequesttextPos); - $sidequesttext['count'] = $sidequesttextCount; - } + $sidequesttext = array( + 'type' => 'solved', + 'text' => $quest['right_text'] + ); } - - // Show task only for Prologes - $showTask = false; - if($sidequesttext['type'] == 'Prolog') { - $showTask = true; + // Quest unsolved + elseif($sidequesttexttypeUrl == 'unsolved') + { + $sidequesttext = array( + 'type' => 'unsolved', + 'text' => $quest['wrong_text'] + ); + } + // Text + else + { + // Text type + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); + // Text count + $sidequesttextCount = $this->Questtexts->getQuesttextsCountForSidequest($sidequest['id'], $sidequesttexttypeUrl); + // Get text + if($sidequesttextCount > 0 && in_array($sidequesttexttypeUrl, $questtexttypes)) + { + if(in_array($sidequesttexttypeUrl, $questtexttypes)) + { + $sidequesttextPos = max(intval($sidequesttextPos), 1); + $sidequesttext = $this->Questtexts->getSidequesttextByUrl($sidequest['id'], $sidequesttexttypeUrl, $sidequesttextPos); + $sidequesttext['count'] = $sidequesttextCount; + } + } } // Media @@ -175,18 +218,92 @@ $sidequestmedia = $this->Media->getMediaById($sidequest['questsmedia_id']); } + // Task + $task = null; + if($sidequesttext['type'] == 'Prolog') + { + // Questtype + $questtype = $this->Questtypes->getQuesttypeById($sidequest['questtype_id']); + + // Task + $task = $this->runAndRenderTask($questtype['classname']); + } + // Pass data to view $this->set('seminary', $seminary); $this->set('questgroup', $questgroup); $this->set('sidequesttext', $sidequesttext); $this->set('quest', $quest); - $this->set('questtext', $questtext); $this->set('sidequest', $sidequest); - $this->set('showtask', $showTask); + $this->set('task', $task); $this->set('media', $sidequestmedia); } + + + + /** + * Load, construct, run and render the Agent for the given + * classname of a Questtype and return ist output. + * + * @param string $questtypeClassname Classname of Questtype to run and render + * @return string Rendered output of Questtype-Agent + */ + private function runAndRenderTask($questtypeClassname) + { + $task = null; + $questtypeAgent = null; + try { + // Load Agent + \hhu\z\QuesttypeAgent::load($questtypeClassname); + // Construct Agent + $questtypeAgent = \hhu\z\QuesttypeAgent::factory($questtypeClassname, $this->request, $this->response); + + // Generate response + $response = clone $this->response; + $response->clearParams(1); + $response->addParams( + null, + \nre\configs\CoreConfig::$defaults['action'] + ); + // Run Agent + $questtypeAgent->run($this->request, $response); + + // Render output + $task = $questtypeAgent->render(); + + } + 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; + } + } ?> diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 0873c6cd..d8baed5f 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -64,7 +64,7 @@ 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.questsmedia_id '. + '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 '. 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '. @@ -94,7 +94,7 @@ public function getSidequestByUrl($seminaryId, $questgroupId, $questId, $sidequestUrl) { $data = $this->db->query( - 'SELECT sidequests.id, sidequests.questtype_id, sidequests.title, sidequests.url, sidequests.xps, sidequests.task, sidequests.questsmedia_id '. + 'SELECT sidequests.id, sidequests.questtype_id, sidequests.title, sidequests.url, sidequests.xps, sidequests.task, quests.right_text, quests.wrong_text, sidequests.questsmedia_id '. 'FROM sidequests '. 'LEFT JOIN questtexts ON questtexts.id = sidequests.questtext_id '. 'LEFT JOIN quests ON quests.id = questtexts.quest_id '. @@ -153,6 +153,36 @@ ); } + + public function setQuestSolved($questId, $characterId) + { + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?)', + 'iii', + $questId, + $characterId, + 0 + ); + } + + + public function setQuestUnsolved($questId, $characterId) + { + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?)', + 'iii', + $questId, + $characterId, + -1 + ); + } + } ?> diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 9a8b7376..bfbe8a9d 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -9,10 +9,16 @@
              + +

              + +

              +

              +

              - +
              • @@ -23,18 +29,21 @@
              - + + 1) : ?>< / > +
              - +

              +

              diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl index 1cb888ce..3067b914 100644 --- a/views/html/quests/sidequest.tpl +++ b/views/html/quests/sidequest.tpl @@ -11,11 +11,17 @@
              + +

              + +

              +

              +

              - - + +
              • @@ -25,14 +31,17 @@ + 1) : ?>< / > +
              - +

              +

              From 256558e048ceb1a4ca7d36abd811188dd8f4f2f6 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 26 Feb 2014 20:32:02 +0100 Subject: [PATCH 062/340] implement dummy-QuesttypeAgent for testing basic QuesttypeAgent-functionality --- questtypes/dummy/DummyQuesttypeAgent.inc | 24 ++++++++++ questtypes/dummy/DummyQuesttypeController.inc | 48 +++++++++++++++++++ questtypes/dummy/DummyQuesttypeModel.inc | 25 ++++++++++ questtypes/dummy/html/index.tpl | 4 ++ 4 files changed, 101 insertions(+) create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/index.tpl 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..0f3012fa --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.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\questtypes; + + + /** + * Controller of the Dummy-QuesttypeAgent for testing basic + * QuesttypeAgent-functionality. + * + * @author Oliver Hanraths + */ + class DummyQuesttypeController extends \hhu\z\QuesttypeController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Check for submission + if($this->request->getRequestMethod() == 'POST') + { + // Right answer (dummy) + if(!is_null($this->request->getPostParam('submit'))) { + $this->setQuestSolved(); + } + // Wrong answer (dummy) + else { + $this->setQuestUnsolved(); + } + } + } + + } + +?> 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/index.tpl b/questtypes/dummy/html/index.tpl new file mode 100644 index 00000000..f482a07b --- /dev/null +++ b/questtypes/dummy/html/index.tpl @@ -0,0 +1,4 @@ +
              + + + From 34e63718bd3a993999a82a789fd31a1690511b72 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 26 Feb 2014 21:00:22 +0100 Subject: [PATCH 063/340] correct Questtext for Sidequests --- controllers/QuestsController.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 1fcd3f56..87d800d4 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -233,6 +233,7 @@ // Pass data to view $this->set('seminary', $seminary); $this->set('questgroup', $questgroup); + $this->set('questtext', $questtext); $this->set('sidequesttext', $sidequesttext); $this->set('quest', $quest); $this->set('sidequest', $sidequest); From 48f796a0c796d208878ad2849c9bb4b58939abd4 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Feb 2014 19:55:09 +0100 Subject: [PATCH 064/340] change solved/unsolved status of a Quest to GET-parameter --- app/QuesttypeController.inc | 4 +- controllers/QuestsController.inc | 115 +++++++++++++++---------------- models/QuestsModel.inc | 33 +++++++++ views/html/quests/quest.tpl | 27 ++++++-- views/html/quests/sidequest.tpl | 16 +++-- 5 files changed, 126 insertions(+), 69 deletions(-) diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 603626ac..89913fb5 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -265,7 +265,7 @@ // Redirect - $this->redirect($this->linker->link('solved', $sidequest != null ? 6 : 5)); + $this->redirect($this->linker->link('Epilog', $sidequest != null ? 6 : 5, true, array('status'=>'solved'))); } @@ -298,7 +298,7 @@ // Redirect - $this->redirect($this->linker->link('unsolved', $sidequest != null ? 6 : 5)); + $this->redirect($this->linker->link('Prolog', $sidequest != null ? 6 : 5, true, array('status'=>'unsolved'))); } } diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 87d800d4..fd64d771 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -73,37 +73,30 @@ if(is_null($questtexttypeUrl)) { $questtexttypeUrl = 'Prolog'; } - // Quest solved - if($questtexttypeUrl == 'solved') + $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)) { - $questtext = array( - 'type' => 'solved', - 'text' => $quest['right_text'] - ); + $questtextPos = max(intval($questtextPos), 1); + $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); + $questtext['count'] = $questtextCount; + $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']); } - // Quest unsolved - elseif($questtexttypeUrl == 'unsolved') + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) { - $questtext = array( - 'type' => 'unsolved', - 'text' => $quest['wrong_text'] - ); - } - // Text - else - { - // Text type - $questtexttypes = $this->Questtexts->getQuesttexttypes(); - $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); - // Text count - $questtextCount = $this->Questtexts->getQuesttextsCountForQuest($quest['id'], $questtexttypeUrl); - // Get text - if($questtextCount > 0 && in_array($questtexttypeUrl, $questtexttypes)) + switch($questStatus) { - $questtextPos = max(intval($questtextPos), 1); - $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); - $questtext['count'] = $questtextCount; - $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']); + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; } } @@ -127,12 +120,21 @@ $task = $this->runAndRenderTask($questtype['classname']); } + // Next Quest + $nextQuests = null; + if($questtexttypeUrl == 'Epilog') { + $nextQuests = $this->Quests->getNextQuests($quest['id']); + } + // 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('nextquests', $nextQuests); $this->set('task', $task); $this->set('media', $questmedia); } @@ -173,39 +175,32 @@ if(is_null($sidequesttexttypeUrl)) { $sidequesttexttypeUrl = 'Prolog'; } - // Quest solved - if($sidequesttexttypeUrl == 'solved') + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); + $sidequesttextCount = $this->Questtexts->getQuesttextsCountForSidequest($sidequest['id'], $sidequesttexttypeUrl); + if($sidequesttextCount > 0 && in_array($sidequesttexttypeUrl, $questtexttypes)) { - $sidequesttext = array( - 'type' => 'solved', - 'text' => $quest['right_text'] - ); - } - // Quest unsolved - elseif($sidequesttexttypeUrl == 'unsolved') - { - $sidequesttext = array( - 'type' => 'unsolved', - 'text' => $quest['wrong_text'] - ); - } - // Text - else - { - // Text type - $questtexttypes = $this->Questtexts->getQuesttexttypes(); - $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); - // Text count - $sidequesttextCount = $this->Questtexts->getQuesttextsCountForSidequest($sidequest['id'], $sidequesttexttypeUrl); - // Get text - if($sidequesttextCount > 0 && in_array($sidequesttexttypeUrl, $questtexttypes)) + if(in_array($sidequesttexttypeUrl, $questtexttypes)) { - if(in_array($sidequesttexttypeUrl, $questtexttypes)) - { - $sidequesttextPos = max(intval($sidequesttextPos), 1); - $sidequesttext = $this->Questtexts->getSidequesttextByUrl($sidequest['id'], $sidequesttexttypeUrl, $sidequesttextPos); - $sidequesttext['count'] = $sidequesttextCount; - } + $sidequesttextPos = max(intval($sidequesttextPos), 1); + $sidequesttext = $this->Questtexts->getSidequesttextByUrl($sidequest['id'], $sidequesttexttypeUrl, $sidequesttextPos); + $sidequesttext['count'] = $sidequesttextCount; + } + } + + // Sidequest status + $sidequestStatus = $this->request->getGetParam('status'); + $sidequestStatusText = null; + if(!is_null($sidequestStatus)) + { + switch($sidequestStatus) + { + case 'solved': + $sidequestStatusText = $sidequest['right_text']; + break; + case 'unsolved': + $sidequestStatusText = $sidequest['wrong_text']; + break; } } @@ -220,7 +215,7 @@ // Task $task = null; - if($sidequesttext['type'] == 'Prolog') + if(!is_null($sidequesttext) && $sidequesttext['type'] == 'Prolog') { // Questtype $questtype = $this->Questtypes->getQuesttypeById($sidequest['questtype_id']); @@ -237,6 +232,8 @@ $this->set('sidequesttext', $sidequesttext); $this->set('quest', $quest); $this->set('sidequest', $sidequest); + $this->set('sidequeststatus', $sidequestStatus); + $this->set('sidequeststatustext', $sidequestStatusText); $this->set('task', $task); $this->set('media', $sidequestmedia); } diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index d8baed5f..4ac41986 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -154,6 +154,33 @@ } + /** + * 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 quests_previousquests '. + 'LEFT JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '. + 'WHERE quests_previousquests.previous_quest_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( @@ -169,6 +196,12 @@ } + /** + * 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( diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index bfbe8a9d..2c919a3d 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -8,14 +8,21 @@ + +
              - +

              - +

              - -

              +

              +
              + + + +
              +

              @@ -39,6 +46,18 @@ >
              + + + +
              +

              +
                + +
              • + +
              +
              +
              diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl index 3067b914..191648d1 100644 --- a/views/html/quests/sidequest.tpl +++ b/views/html/quests/sidequest.tpl @@ -10,14 +10,21 @@ + +
              - +

              - +

              - -

              +

              +
              + + + +
              +

              @@ -37,6 +44,7 @@ >
              +
              From e7227fe2c9ce5f5c2e9e0e079a1eb67d342d9cd4 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Feb 2014 19:55:24 +0100 Subject: [PATCH 065/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2519 -> 2585 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 20 +++++++++++++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 09213b4bc7d8d63e6ef8815628bcb22d800dd269..77e8b492bff97ab2c588f07f110cfb874d724ab4 100644 GIT binary patch delta 823 zcmX}qKS*0q6vy$C#3cSt$s1 zO0JJSvsS$DbK^(_%${HxV|b3a_!o=u79$wV%2o1=iygHeie-LhHaX>f1{Kz1Z&_ z45JE+y7?*8&ZkjLnL`zv$EUd9##^Y4?7DFZ)wvT?!C!9v4{H4zT=Qu6KLRB#hBGbR zK<#`VOE8V)c!_G|e~e;~Rn)l{>J!wUHV{W`umiPjH|o7!)TbFiO4#AyKjAT<4a_0` zCtF5NTjf@P8*cm!OBsJhE%Y1JiEH=#7F9URuOPHYZgS86%2$_+2447Tcisfv`6J__ viT4BjHaU_BlE=a4*-gz=O${yaXUY6f)#G3%9$F6+4GtuZzKl{t&du34ol*u;&a_*Q#USI$sDwE;zlI^= zUFRWc-6`te3+FXz-yL@0BgXI-d997bb>z2BI@&l!qlpPrf}|Typ>95lDoO^GZ~>#3 zb@4i?BHJ!LK~?S?b?!YLP~8VNo-U%IEtil&mr9M$5|3)SB zb6Z`g0hO=?wJ(PHDTa`oHi23uLJA77EX zScEd^;!)JPZe+^(P!}I{aT0ZbRM>`%|BYoB;I&1XMqG077Ss-uzEsI27+`jV1aMz4^gl>wnO9H5WYc{Q)tmLk0i< diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 086c0741..45c97262 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-02-17 02:28+0100\n" -"PO-Revision-Date: 2014-02-17 02:29+0100\n" +"POT-Creation-Date: 2014-02-26 20:45+0100\n" +"PO-Revision-Date: 2014-02-26 20:45+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -39,7 +39,7 @@ msgstr "Kurse" #: ../../../views/html/charactergroups/group.tpl:3 #: ../../../views/html/charactergroups/groupsgroup.tpl:3 #: ../../../views/html/charactergroups/index.tpl:3 -#: ../../../views/html/characters/character.tpl:13 +#: ../../../views/html/characters/character.tpl:16 #: ../../../views/html/seminaries/seminary.tpl:9 msgid "Character Groups" msgstr "Charaktergruppen" @@ -114,8 +114,18 @@ msgstr "Logout" msgid "containing optional Quests" msgstr "Enthaltene optionale Quests" -#: ../../../views/html/quests/quest.tpl:37 -#: ../../../views/html/quests/sidequest.tpl:35 +#: ../../../views/html/quests/quest.tpl:13 +#: ../../../views/html/quests/sidequest.tpl:15 +msgid "solved" +msgstr "gelöst" + +#: ../../../views/html/quests/quest.tpl:15 +#: ../../../views/html/quests/sidequest.tpl:17 +msgid "unsolved" +msgstr "ungelöst" + +#: ../../../views/html/quests/quest.tpl:45 +#: ../../../views/html/quests/sidequest.tpl:43 msgid "Task" msgstr "Aufgabe" From 351db837f87c3dc668d701be3f370d24cc756bad Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Feb 2014 20:53:48 +0100 Subject: [PATCH 066/340] show xplevel for characters --- models/CharactersModel.inc | 8 ++++---- views/html/characters/character.tpl | 2 +- views/html/characters/index.tpl | 2 +- views/html/html.tpl | 2 +- views/html/users/user.tpl | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 2bbe7744..c2899d8e 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -43,7 +43,7 @@ public function getCharactersForUser($userId) { return $this->db->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, 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 '. + '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 '. @@ -63,7 +63,7 @@ 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, 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 '. + '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 '. @@ -105,7 +105,7 @@ 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, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + '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 = ?', @@ -132,7 +132,7 @@ 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, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + '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 charactertypes.seminary_id = ? AND characters.url = ?', diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index dcc9cea3..b2130746 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -4,7 +4,7 @@

              - XPs:
              + XPs: ()
              :
              :
              diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl index 362d60c1..f16261cb 100644 --- a/views/html/characters/index.tpl +++ b/views/html/characters/index.tpl @@ -3,6 +3,6 @@

                -
              • ( XPs)
              • +
              • ( XPs, )
              diff --git a/views/html/html.tpl b/views/html/html.tpl index ad8f4648..ab275bdf 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -18,7 +18,7 @@
              - ( XPs) + ( XPs, )
              diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 9be97544..506fdf94 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -11,7 +11,7 @@

                -
              • ( XPs) ()
              • +
              • ( XPs, ) ()
              From b6ea1fafae147b5c4ec0ad541474cd06274f09ff Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 03:03:32 +0100 Subject: [PATCH 067/340] correct typo by marking Quest as unsolved --- app/QuesttypeController.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 89913fb5..132a25e5 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -294,7 +294,7 @@ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Set solved - $this->Quests->setQuestSolved($quest['id'], $character['id']); + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); // Redirect From 42a7e9303c443ca1498341f2ff335ca7465da8fd Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 03:16:02 +0100 Subject: [PATCH 068/340] implement permissions for Questgroups, Quests and Sidequests --- controllers/QuestgroupsController.inc | 33 +++++- controllers/QuestsController.inc | 82 ++++++++++++- controllers/SeminariesController.inc | 12 +- models/QuestgroupsModel.inc | 164 ++++++++++++++++++++++++++ models/QuestsModel.inc | 46 +++++++- views/html/questgroups/questgroup.tpl | 17 ++- views/html/quests/quest.tpl | 12 +- views/html/seminaries/seminary.tpl | 8 +- 8 files changed, 355 insertions(+), 19 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index a0639592..1e7c2a29 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -65,10 +65,28 @@ // 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) { + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Check permission of Questgroups + for($i=1; $iQuestgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + } } // Get texts @@ -79,10 +97,15 @@ if(count($childQuestgroupshierarchy) == 0) { $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); - - // Attach sidequests - foreach($quests as &$quest) { - $quest['sidequests'] = $this->Quests->getSidequestsForQuest($quest['id']); + for($i=0; $i 0) { + $quests[$i]['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']); + } + + // Attach sidequests + $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quests[$i]['id']); } } diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index fd64d771..e6e7ce54 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media', 'questtypes'); + public $models = array('seminaries', 'questgroups', 'quests', 'questtexts', 'media', 'questtypes', 'questgroupshierarchy'); /** * User permissions * @@ -68,6 +68,35 @@ // 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(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + else + { + // Previous Quests + $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 Questtext $questtext = null; if(is_null($questtexttypeUrl)) { @@ -111,7 +140,7 @@ // Task $task = null; - if($questtext['type'] == 'Prolog') + if($questtexttypeUrl == 'Prolog') { // Questtype $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); @@ -120,10 +149,20 @@ $task = $this->runAndRenderTask($questtype['classname']); } - // Next Quest + // Next Quest/Questgroup $nextQuests = null; - if($questtexttypeUrl == 'Epilog') { + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // 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']); + } } @@ -135,6 +174,7 @@ $this->set('queststatus', $questStatus); $this->set('queststatustext', $questStatusText); $this->set('nextquests', $nextQuests); + $this->set('nextquestgroup', $nextQuestgroup); $this->set('task', $task); $this->set('media', $questmedia); } @@ -167,6 +207,38 @@ // Get Sidequest $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $sidequestUrl); + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permission + $previousQuests = $this->Quests->getPreviousQuests($quest['id']); + if(count($previousQuests) == 0) + { + // Previous Questgroup + $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($questgroup['id']); + if(!$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 Questtext $questtext = $this->Questtexts->getQuesttextForSidequest($sidequest['id']); @@ -215,7 +287,7 @@ // Task $task = null; - if(!is_null($sidequesttext) && $sidequesttext['type'] == 'Prolog') + if($sidequesttexttypeUrl == 'Prolog') { // Questtype $questtype = $this->Questtypes->getQuesttypeById($sidequest['questtype_id']); diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 94303d88..bf89b9f2 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -90,10 +90,20 @@ // 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) { + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Check permission of Questgroups + for($i=1; $iQuestgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + } } diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index ac820bf1..471fef71 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -19,6 +19,12 @@ */ class QuestgroupsModel extends \hhu\z\Model { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'quests'); @@ -138,6 +144,164 @@ ); } + + /** + * 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->getQuestsForQuestgroup($currentQuestgroup['id']); + $lastQuest = array_pop($quests); + + + return $this->Quests->hasCharacterSolvedQuest($lastQuest['id'], $characterId); + } + + + + + /** + * 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/QuestsModel.inc b/models/QuestsModel.inc index 4ac41986..e531ac26 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -109,7 +109,7 @@ $seminaryId ); if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException(); + throw new \nre\exceptions\IdNotFoundException($sidequestUrl); } @@ -175,6 +175,27 @@ } + /** + * 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 quests_previousquests '. + 'LEFT JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + /** * Mark a Quest as solved for a Character. * @@ -216,6 +237,29 @@ ); } + + /** + * 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); + } + } ?> diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 5699bfa7..848bf187 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -1,5 +1,5 @@ -

              -

              +

              +

              @@ -14,7 +14,14 @@

                -
              • :
              • +
              • + : + + + + + +
              @@ -25,6 +32,7 @@
              • +
                : @@ -35,6 +43,9 @@
              + + + diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 2c919a3d..6aca7048 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -48,14 +48,20 @@
              - +
              -

              +

              + 0) : ?>
                -
              • +
              • :
              + + : + + Spiel vorbei +
              diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 3ff9b9c0..e84f15f4 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -18,7 +18,13 @@

                -
              • :
              • +
              • + : + +
              • + + +
              From 4085457bb1684a9e72968dc61d02587576dc0a1d Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 11:46:09 +0100 Subject: [PATCH 069/340] correct exceptions for Questgroupspictures and Questtexts --- controllers/QuestgroupspictureController.inc | 7 ++++++- models/QuesttextsModel.inc | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/controllers/QuestgroupspictureController.inc b/controllers/QuestgroupspictureController.inc index 182c2b50..7810869b 100644 --- a/controllers/QuestgroupspictureController.inc +++ b/controllers/QuestgroupspictureController.inc @@ -47,7 +47,12 @@ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); // Get Picture - $picture = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + $picture = null; + try { + $picture = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } // Pass data to view diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index 3a7d8526..5324157f 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -163,7 +163,7 @@ $sidequestId ); if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException(); + throw new \nre\exceptions\IdNotFoundException($sidequestId); } From ef37c1ea15bfb44e1e665d39c824be5b297d9d14 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 12:00:53 +0100 Subject: [PATCH 070/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2585 -> 2719 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 34 ++++++++++++++------ views/html/questgroups/questgroup.tpl | 2 +- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 77e8b492bff97ab2c588f07f110cfb874d724ab4..d2a2559161443dd7666423b67ee58c7d64c09e6e 100644 GIT binary patch delta 1160 zcmXxjPe{{Y9LMoz`PnyfXpT`K^#~?mJ#;uyC zwqX%l=vcBY+xG7mqJ0H5@o(F&QNq+)t=*`B`}orHL$;oCLBfLd%0Q_OG56d2Ro z!)|w=TYMfjVga?2*HK4v8}%Z0ks-}P)VPnas*aZw zG{Ae**?mD4VSb^0@CP-}Khz7fvHUiSBTZ%)^?Vxj{C?|U>v7wE64Ugb!7Sd2u>UO1 zJf}e`d1D{^gc|rOY9&8yeHHa0LEb|5Gln#oIBvlKTR(y^>L*a+Ojrx39hye{?q-zz z*9*+rhB;*C>!ArWsQwj-p$7F8Y7m7>?j{qYVzfbhH&oQ3bf@~sU8F*HglWtO*`o7T z^pSdFg^onW5hry{+sU;Uq@W#0k~>M=1H}%qqh4yP%ofcU1?_;2RhL{bOlr~Fl)AwR z{kL_dYtc(#z<<^B#3{PPtNx60wzlXr2SQhhmo9sGe<_%c?iow%b<^qWzJvauP_M0K xvZ+)pA6gA`j!$_dchW0VT(9iTcvBUxmrU4j{>txxUq{rT5-2o9hNYQpD~TU@BnUME!M}Xw&EVfz3vs<$M_nia2Vrw3t2a` z2|9Z@FzHVFggH!bd;zs#-p{|l8pbc(H>i0f)WRR#HPm$**p1(@2eV9~YXjKC{x(QQ z7moXhJE#rr`SD3q=8sWDnMG~*1P|k!kC#yuS@m%dRk<>1!!LgP8*2TZm=7uUZ#tSd zpRAU636=S4Y{U{C#7$Hw|6o1FSw)r0q8>pzDuF&!f&-{^hfvpzq8`m%WJ`O9p(akz z(E>B5ji2Ive2)BVg+I-E<-T=`sD;Ydi=VI`laz}b?J6qq>!^8S$P~McO8h~J`s+Z* z0VObtyic}({A`iGBe>+_cbH-P0d?J1R3(1+`7PAON$!HK)glG1JSN?kni5f!w2t1t z%AK}W24*KwC0DuB^xQfsy=v)>dtYBm8_`T0BF+$Mx?6okN?H{=M(7c=6G~J`De-^p z2EDU{KCKp_lh8|cf>4QcH#@Cqr{gxDs&$G`^-f2b*lJXc-7Iv*gCNQ!#?rZSxr;5i fp8mcIQ9jZ9->~pL@i&OpYeou}Q(ZxHKYe~1Z{b!e diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 45c97262..80b79587 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-02-26 20:45+0100\n" -"PO-Revision-Date: 2014-02-26 20:45+0100\n" +"POT-Creation-Date: 2014-03-04 11:59+0100\n" +"PO-Revision-Date: 2014-03-04 12:00+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -57,7 +57,7 @@ msgid "Group Leader" msgstr "Gruppenleiter" #: ../../../views/html/charactergroups/group.tpl:21 -#: ../../../views/html/questgroups/questgroup.tpl:24 +#: ../../../views/html/questgroups/questgroup.tpl:31 msgid "Quests" msgstr "Quests" @@ -110,22 +110,36 @@ msgstr "Login" msgid "Logout" msgstr "Logout" -#: ../../../views/html/questgroups/questgroup.tpl:30 +#: ../../../views/html/questgroups/questgroup.tpl:22 +#: ../../../views/html/questgroups/questgroup.tpl:47 +#: ../../../views/html/seminaries/seminary.tpl:26 +msgid "locked" +msgstr "gesperrt" + +#: ../../../views/html/questgroups/questgroup.tpl:38 msgid "containing optional Quests" msgstr "Enthaltene optionale Quests" -#: ../../../views/html/quests/quest.tpl:13 -#: ../../../views/html/quests/sidequest.tpl:15 +#: ../../../views/html/quests/quest.tpl:15 +#: ../../../views/html/quests/sidequest.tpl:17 msgid "solved" msgstr "gelöst" -#: ../../../views/html/quests/quest.tpl:15 -#: ../../../views/html/quests/sidequest.tpl:17 +#: ../../../views/html/quests/quest.tpl:17 +#: ../../../views/html/quests/sidequest.tpl:19 msgid "unsolved" msgstr "ungelöst" -#: ../../../views/html/quests/quest.tpl:45 -#: ../../../views/html/quests/sidequest.tpl:43 +#: ../../../views/html/quests/quest.tpl:53 +msgid "Go on" +msgstr "Hier geht es weiter" + +#: ../../../views/html/quests/quest.tpl:57 +msgid "Quest" +msgstr "Quest" + +#: ../../../views/html/quests/quest.tpl:70 +#: ../../../views/html/quests/sidequest.tpl:51 msgid "Task" msgstr "Aufgabe" diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 848bf187..2e5596d7 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -44,7 +44,7 @@ - + From 5706d6fe14b6e01828c2dc5e199dfa81d25324b8 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 12:04:56 +0100 Subject: [PATCH 071/340] correct permissions checking for previous Questgroups if there is none --- controllers/QuestsController.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index e6e7ce54..8ce5705c 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -77,7 +77,7 @@ { // Previous Questgroup $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($questgroup['id']); - if(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) { + if(!is_null($previousQuestgroup) && !$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) { throw new \nre\exceptions\AccessDeniedException(); } } @@ -216,7 +216,7 @@ { // Previous Questgroup $previousQuestgroup = $this->Questgroups->getPreviousQuestgroup($questgroup['id']); - if(!$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) { + if(!is_null($previousQuestgroup) && !$this->Questgroups->hasCharacterSolvedQuestgroup($previousQuestgroup['id'], $character['id'])) { throw new \nre\exceptions\AccessDeniedException(); } } From 5cd7372ad221f9e393c6bf12f340fbffe7a8b48a Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 12:18:00 +0100 Subject: [PATCH 072/340] correctly set status of Sidequests --- app/QuesttypeController.inc | 15 +++++++++++-- models/QuestsModel.inc | 42 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 132a25e5..31596073 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -261,7 +261,12 @@ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Set solved - $this->Quests->setQuestSolved($quest['id'], $character['id']); + if(is_null($sidequest)) { + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else { + $this->Quests->setSidequestSolved($sidequest['id'], $character['id']); + } // Redirect @@ -294,7 +299,13 @@ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Set solved - $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + if(is_null($sidequest)) { + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + else { + $this->Quests->setSidequestUnsolved($sidequest['id'], $character['id']); + } + // Redirect diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index e531ac26..e49947f8 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -217,6 +217,27 @@ } + /** + * Mark a Sidequest as solved for a Character. + * + * @param int $sidequestId ID of Sidequest to mark as solved + * @param int $characterId ID of Character that solved the Sidequest + */ + public function setSidequestSolved($sidequestId, $characterId) + { + $this->db->query( + 'INSERT INTO sidequests_characters '. + '(sidequest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?)', + 'iii', + $sidequestId, + $characterId, + 0 + ); + } + + /** * Mark a Quest as unsolved for a Character. * @@ -238,6 +259,27 @@ } + /** + * Mark a Sidequest as unsolved for a Character. + * + * @param int $sidequestId ID of Sidequest to mark as unsolved + * @param int $characterId ID of Character that unsolved the Sidequest + */ + public function setSidequestUnsolved($sidequestId, $characterId) + { + $this->db->query( + 'INSERT INTO sidequests_characters '. + '(sidequest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?)', + 'iii', + $sidequestId, + $characterId, + -1 + ); + } + + /** * Determine if the given Character has solved the given Quest. * From 7fec7e6afa0f2804e5108a2a05e778dc204faa6c Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 7 Mar 2014 13:37:59 +0100 Subject: [PATCH 073/340] implement avatar pictures --- controllers/CharactersController.inc | 2 +- models/CharactersModel.inc | 5 ++++- views/html/characters/character.tpl | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 4e65ed40..581b5b5a 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -17,7 +17,7 @@ * * @author Oliver Hanraths */ - class CharactersController extends \hhu\z\Controller + class CharactersController extends \hhu\z\controllers\SeminaryRoleController { /** * User permissions diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index c2899d8e..59125e32 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -132,9 +132,12 @@ 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 '. + '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 diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index b2130746..b4bb166d 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -10,6 +10,9 @@ :

              + + <?=$character['avatar_description']?> +
              From 1cc251038aa38ac980f390faab0c2673c3709f47 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Mar 2014 13:25:55 +0100 Subject: [PATCH 074/340] unify Main- and Sidequests --- agents/intermediate/QuestsAgent.inc | 10 -- app/QuesttypeController.inc | 27 +--- controllers/QuestgroupsController.inc | 2 +- controllers/QuestsController.inc | 182 ++++++-------------------- models/QuestgroupsModel.inc | 2 +- models/QuestsModel.inc | 111 ++++++++-------- models/QuesttextsModel.inc | 64 +-------- views/html/questgroups/questgroup.tpl | 2 +- views/html/quests/quest.tpl | 25 ++-- views/html/quests/sidequest.tpl | 55 -------- 10 files changed, 117 insertions(+), 363 deletions(-) delete mode 100644 views/html/quests/sidequest.tpl diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc index 8de47e79..c26a2f70 100644 --- a/agents/intermediate/QuestsAgent.inc +++ b/agents/intermediate/QuestsAgent.inc @@ -32,16 +32,6 @@ $this->addSubAgent('Questgroupspicture', 'index', $request->getParam(3), $request->getParam(4), true); } - - /** - * Action: quest. - */ - public function sidequest(\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/app/QuesttypeController.inc b/app/QuesttypeController.inc index 31596073..743c5d59 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -251,22 +251,11 @@ // Get Quest $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $this->request->getParam(5)); - // Sidequest - $sidequest = null; - if($this->request->getParam(2) == 'sidequest') { - $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $this->request->getParam(6)); - } - // Character $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Set solved - if(is_null($sidequest)) { - $this->Quests->setQuestSolved($quest['id'], $character['id']); - } - else { - $this->Quests->setSidequestSolved($sidequest['id'], $character['id']); - } + $this->Quests->setQuestSolved($quest['id'], $character['id']); // Redirect @@ -289,23 +278,11 @@ // Get Quest $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $this->request->getParam(5)); - // Sidequest - $sidequest = null; - if($this->request->getParam(2) == 'sidequest') { - $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $this->request->getParam(6)); - } - // Character $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Set solved - if(is_null($sidequest)) { - $this->Quests->setQuestUnsolved($quest['id'], $character['id']); - } - else { - $this->Quests->setSidequestUnsolved($sidequest['id'], $character['id']); - } - + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); // Redirect diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 1e7c2a29..42e19d05 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -96,7 +96,7 @@ $quests = null; if(count($childQuestgroupshierarchy) == 0) { - $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); + $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']); for($i=0; $i 0) { - if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) { - $solved = true; - break; + $solved = false; + foreach($previousQuests as &$previousQuest) + { + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) { + $solved = true; + break; + } + } + if(!$solved) { + throw new \nre\exceptions\AccessDeniedException(); } } - 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']); } } @@ -154,14 +167,23 @@ $nextQuestgroup = null; if($questtexttypeUrl == 'Epilog') { - // Next Quest - $nextQuests = $this->Quests->getNextQuests($quest['id']); - - // Next Questgroup - if(empty($nextQuests)) + if($quest['is_mainquest']) { - $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); - $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($nextQuestgroup['questgroupshierarchy_id']); + // 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); } } @@ -173,6 +195,7 @@ $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); @@ -180,137 +203,6 @@ } - /** - * Action: sidequest. - * - * Show a sidequest 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 $sidequestUrl URL-Title of Sidequest - * @param string $sidequesttexttypeUrl URL-Title of Sidequesttexttype - * @param int $sidequesttextPos Position of Sidequesttext - */ - public function sidequest($seminaryUrl, $questgroupUrl, $questUrl, $sidequestUrl, $sidequesttexttypeUrl=null, $sidequesttextPos=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 Sidequest - $sidequest = $this->Quests->getSidequestByUrl($seminary['id'], $questgroup['id'], $quest['id'], $sidequestUrl); - - // Get Character - $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); - - // Check permission - $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 Questtext - $questtext = $this->Questtexts->getQuesttextForSidequest($sidequest['id']); - - // Get Sidequesttext - $sidequesttext = null; - if(is_null($sidequesttexttypeUrl)) { - $sidequesttexttypeUrl = 'Prolog'; - } - $questtexttypes = $this->Questtexts->getQuesttexttypes(); - $questtexttypes = array_map(function($t) { return $t['url']; }, $questtexttypes); - $sidequesttextCount = $this->Questtexts->getQuesttextsCountForSidequest($sidequest['id'], $sidequesttexttypeUrl); - if($sidequesttextCount > 0 && in_array($sidequesttexttypeUrl, $questtexttypes)) - { - if(in_array($sidequesttexttypeUrl, $questtexttypes)) - { - $sidequesttextPos = max(intval($sidequesttextPos), 1); - $sidequesttext = $this->Questtexts->getSidequesttextByUrl($sidequest['id'], $sidequesttexttypeUrl, $sidequesttextPos); - $sidequesttext['count'] = $sidequesttextCount; - } - } - - // Sidequest status - $sidequestStatus = $this->request->getGetParam('status'); - $sidequestStatusText = null; - if(!is_null($sidequestStatus)) - { - switch($sidequestStatus) - { - case 'solved': - $sidequestStatusText = $sidequest['right_text']; - break; - case 'unsolved': - $sidequestStatusText = $sidequest['wrong_text']; - break; - } - } - - // Media - $sidequestmedia = null; - if(!is_null($sidequesttext) && !empty($sidequesttext['questsmedia_id'])) { - $sidequestmedia = $this->Media->getMediaById($sidequesttext['questsmedia_id']); - } - elseif(!is_null($sidequest['questsmedia_id'])) { - $sidequestmedia = $this->Media->getMediaById($sidequest['questsmedia_id']); - } - - // Task - $task = null; - if($sidequesttexttypeUrl == 'Prolog') - { - // Questtype - $questtype = $this->Questtypes->getQuesttypeById($sidequest['questtype_id']); - - // Task - $task = $this->runAndRenderTask($questtype['classname']); - } - - - // Pass data to view - $this->set('seminary', $seminary); - $this->set('questgroup', $questgroup); - $this->set('questtext', $questtext); - $this->set('sidequesttext', $sidequesttext); - $this->set('quest', $quest); - $this->set('sidequest', $sidequest); - $this->set('sidequeststatus', $sidequestStatus); - $this->set('sidequeststatustext', $sidequestStatusText); - $this->set('task', $task); - $this->set('media', $sidequestmedia); - } - - /** diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index 471fef71..088d54c0 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -217,7 +217,7 @@ $lastChildQuestgroupshierarchy = array_pop($childQuestgroupshierarchy); } - $quests = $this->Quests->getQuestsForQuestgroup($currentQuestgroup['id']); + $quests = $this->Quests->getMainquestsForQuestgroup($currentQuestgroup['id']); $lastQuest = array_pop($quests); diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index e49947f8..09ebc125 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -40,11 +40,12 @@ * @param int $questgroupId ID of a Questgroup * @return array Quests of the given Questgroup */ - public function getQuestsForQuestgroup($questgroupId) + 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 @@ -64,7 +65,8 @@ 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 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 '. @@ -76,6 +78,38 @@ 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]; } @@ -94,14 +128,15 @@ public function getSidequestByUrl($seminaryId, $questgroupId, $questId, $sidequestUrl) { $data = $this->db->query( - 'SELECT sidequests.id, sidequests.questtype_id, sidequests.title, sidequests.url, sidequests.xps, sidequests.task, quests.right_text, quests.wrong_text, sidequests.questsmedia_id '. - 'FROM sidequests '. + '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 quests ON quests.id = questtexts.quest_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 sidequests.url = ? AND quests.id = ? AND questgroups.id = ? AND seminaries.id = ?', + 'WHERE quests.url = ? AND mainquests.id = ? AND questgroups.id = ? AND seminaries.id = ?', 'siii', $sidequestUrl, $questId, @@ -126,8 +161,9 @@ public function getSidequestsForQuest($questId) { return $this->db->query( - 'SELECT sidequests.id, sidequests.questtext_id, sidequests.title, sidequests.url, sidequests.entry_text '. - 'FROM sidequests '. + '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', @@ -146,7 +182,8 @@ { return $this->db->query( 'SELECT id, questtext_id, title, url, entry_text '. - 'FROM sidequests '. + 'FROM quests '. + 'INNER JOIN sidequests ON sidequests.quest_id = quests.id '. 'WHERE questtext_id = ?', 'i', $questtextId @@ -164,11 +201,12 @@ { return $this->db->query( 'SELECT quests.id, quests.title, quests.url, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. - 'FROM quests_previousquests '. - 'LEFT JOIN quests ON quests.id = quests_previousquests.quest_id '. + '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 quests_previousquests.previous_quest_id = ?', + 'WHERE mainquests_previousmainquests.previous_mainquest_id = ?', 'i', $questId ); @@ -185,11 +223,12 @@ { return $this->db->query( 'SELECT quests.id, quests.title, quests.url, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. - 'FROM quests_previousquests '. - 'LEFT JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + '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 quests_previousquests.quest_id = ?', + 'WHERE mainquests_previousmainquests.mainquest_id = ?', 'i', $questId ); @@ -217,27 +256,6 @@ } - /** - * Mark a Sidequest as solved for a Character. - * - * @param int $sidequestId ID of Sidequest to mark as solved - * @param int $characterId ID of Character that solved the Sidequest - */ - public function setSidequestSolved($sidequestId, $characterId) - { - $this->db->query( - 'INSERT INTO sidequests_characters '. - '(sidequest_id, character_id, status) '. - 'VALUES '. - '(?, ?, ?)', - 'iii', - $sidequestId, - $characterId, - 0 - ); - } - - /** * Mark a Quest as unsolved for a Character. * @@ -259,27 +277,6 @@ } - /** - * Mark a Sidequest as unsolved for a Character. - * - * @param int $sidequestId ID of Sidequest to mark as unsolved - * @param int $characterId ID of Character that unsolved the Sidequest - */ - public function setSidequestUnsolved($sidequestId, $characterId) - { - $this->db->query( - 'INSERT INTO sidequests_characters '. - '(sidequest_id, character_id, status) '. - 'VALUES '. - '(?, ?, ?)', - 'iii', - $sidequestId, - $characterId, - -1 - ); - } - - /** * Determine if the given Character has solved the given Quest. * diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index 5324157f..38a1286d 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -46,7 +46,7 @@ public function getQuesttextByUrl($questId, $questtexttypeUrl, $pos) { $data = $this->db->query( - 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexts.out_text, questtexts.questsmedia_id, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. + '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 = ?', @@ -62,34 +62,6 @@ } - /** - * Get a Questtext for a Sidequest by its URL. - * - * @throws IdNotFoundException - * @param int $sidequestId ID of the Sidequest to get text for - * @param string $questtexttypeUrl URL of the Questtexttype - * @param int $pos Position of Questtexttype - * @return array Questtexttype data - */ - public function getSidequesttextByUrl($sidequestId, $questtexttypeUrl, $pos) - { - $data = $this->db->query( - 'SELECT sidequesttexts.id, sidequesttexts.text, sidequesttexts.pos, sidequesttexts.out_text, sidequesttexts.abort_text, sidequesttexts.questsmedia_id, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. - 'FROM sidequesttexts '. - 'LEFT JOIN questtexttypes ON questtexttypes.id = sidequesttexts.questtexttype_id '. - 'WHERE sidequesttexts.sidequest_id = ? AND questtexttypes.url = ? AND sidequesttexts.pos = ?', - 'isi', - $sidequestId, $questtexttypeUrl, $pos - ); - if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException($questtexttypeUrl); - } - - - return $data = $data[0]; - } - - /** * Get count of Questtexts for a Quest. * @@ -117,33 +89,6 @@ } - /** - * Get count of Questtexts for a Sidequest. - * - * @param int $sidequestId ID of the Sidequest - * @param string $questtexttypeUrl URL of the Questtexttype - * @return int Conut of Questtexts for Sideuest - */ - public function getQuesttextsCountForSidequest($questId, $questtexttypUrl) - { - $count = 0; - $data = $this->db->query( - 'SELECT COUNT(sidequesttexts.id) AS c '. - 'FROM sidequesttexts '. - 'LEFT JOIN questtexttypes ON questtexttypes.id = sidequesttexts.questtexttype_id '. - 'WHERE sidequesttexts.sidequest_id = ? AND questtexttypes.url = ?', - 'is', - $questId, $questtexttypUrl - ); - if(!empty($data)) { - $count = $data[0]['c']; - } - - - return $count; - } - - /** * Get corresponding Questtext for a Sidequest. * @@ -154,11 +99,12 @@ public function getQuesttextForSidequest($sidequestId) { $data = $this->db->query( - 'SELECT questtexts.id, questtexts.text, questtexts.pos, questtexttypes.id AS type_id, questtexttypes.type, questtexttypes.url AS type_url '. - 'FROM sidequests '. + '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 sidequests.id = ?', + 'WHERE quests.id = ?', 'i', $sidequestId ); diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 2e5596d7..d8d2ce42 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -34,9 +34,9 @@
            • + 0) : ?>
              : - 0) : ?>
              • diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 6aca7048..66110f7b 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -4,7 +4,13 @@ +

                + +

                +

                + + @@ -25,20 +31,17 @@

                -
                  -
                • +
                • - + +
                • + +
                • -
                - - - - - +
              1) : ?>< @@ -54,7 +57,11 @@ 0) : ?>
                + +
              • :
              • +
              • :
              • +
              diff --git a/views/html/quests/sidequest.tpl b/views/html/quests/sidequest.tpl deleted file mode 100644 index 191648d1..00000000 --- a/views/html/quests/sidequest.tpl +++ /dev/null @@ -1,55 +0,0 @@ -

              -

              - - - - -

              -

              -

              .

              - - - - - -
              - -

              - -

              - -

              -
              - - - -
              -

              -

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

              -

              -

              -
              - From 7ce4cf5292ace836a1e97db2cc8570a45c25445d Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Mar 2014 14:31:40 +0100 Subject: [PATCH 075/340] pass Quest-ID to QuesttypeController --- controllers/QuestsController.inc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 9a5041c3..211c8104 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -159,7 +159,7 @@ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); // Task - $task = $this->runAndRenderTask($questtype['classname']); + $task = $this->runAndRenderTask($quest['id'], $questtype['classname']); } // Next Quest/Questgroup @@ -209,10 +209,11 @@ * Load, construct, run and render the Agent for the given * classname of a Questtype and return ist output. * + * @param int $questId ID of Quest * @param string $questtypeClassname Classname of Questtype to run and render * @return string Rendered output of Questtype-Agent */ - private function runAndRenderTask($questtypeClassname) + private function runAndRenderTask($questId, $questtypeClassname) { $task = null; $questtypeAgent = null; @@ -222,15 +223,18 @@ // Construct Agent $questtypeAgent = \hhu\z\QuesttypeAgent::factory($questtypeClassname, $this->request, $this->response); + // Generate request + $request = clone $this->request; // Generate response $response = clone $this->response; $response->clearParams(1); $response->addParams( null, - \nre\configs\CoreConfig::$defaults['action'] + \nre\configs\CoreConfig::$defaults['action'], + $questId ); // Run Agent - $questtypeAgent->run($this->request, $response); + $questtypeAgent->run($request, $response); // Render output $task = $questtypeAgent->render(); From e8c00354181be9fd326b3dbf13b0cd0d697617c7 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 9 Mar 2014 14:35:13 +0100 Subject: [PATCH 076/340] implement Questtype ?textinput? --- .../textinput/TextinputQuesttypeAgent.inc | 24 ++++++ .../TextinputQuesttypeController.inc | 74 +++++++++++++++++++ .../textinput/TextinputQuesttypeModel.inc | 67 +++++++++++++++++ questtypes/textinput/html/index.tpl | 11 +++ 4 files changed, 176 insertions(+) create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/index.tpl 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..e41facfd --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,74 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + + + + + /** + * Action: index. + * + * Display a text with input fields and evaluate if user input + * matches with stored regular expressions. + */ + public function index($questId) + { + // Check for submission + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Get right answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + + // Match regexs with user answers + $allSolved = true; + for($i=0; $irequest->getPostParam('answer-'.strval($i+1)); + $score = preg_match($regexs[$i]['regex'], $answer); + if($score === 0 || $score === false) { + $allSolved = false; + } + } + + // Set status + if($allSolved) { + $this->setQuestSolved(); + } + else { + $this->setQuestUnsolved(); + } + } + + + // Get Task + $task = $this->Textinput->getTextinputQuest($questId); + + // Process text + $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY); + + + // Pass data to view + $this->set('text', $textParts); + } + + } + +?> diff --git a/questtypes/textinput/TextinputQuesttypeModel.inc b/questtypes/textinput/TextinputQuesttypeModel.inc new file mode 100644 index 00000000..d242f251 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeModel.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\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 + ); + + + return $data[0]; + } + + + /** + * 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 number, regex '. + 'FROM questtypes_textinput_regexs '. + 'WHERE questtypes_textinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + } + +?> diff --git a/questtypes/textinput/html/index.tpl b/questtypes/textinput/html/index.tpl new file mode 100644 index 00000000..b3701630 --- /dev/null +++ b/questtypes/textinput/html/index.tpl @@ -0,0 +1,11 @@ +
              + + 0) : ?> + + + + + +

              + + From ddd210dfc16683584b759b61767728d9d1b9787e Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 11 Mar 2014 00:06:18 +0100 Subject: [PATCH 077/340] add support for Apache 2.4 --- .htaccess | 46 +++++++++++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/.htaccess b/.htaccess index e5c5c191..cb3fb9ef 100644 --- a/.htaccess +++ b/.htaccess @@ -1,4 +1,3 @@ -Allow From All Options -Indexes -MultiViews ErrorDocument 403 /www/error403.html @@ -6,20 +5,41 @@ ErrorDocument 404 /www/error404.html ErrorDocument 500 /www/error500.html - - Order Deny,Allow - Deny From All - - - Order Deny,Allow - Deny From All - + + Require all granted + + + Require all denied + - - Order Deny,Allow - Deny From All - + + 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 + + From ad1da597486c28a0c9c8d11f4948a382ca012604 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 11 Mar 2014 00:49:26 +0100 Subject: [PATCH 078/340] save answers for Questtype ?Textinput? --- .../textinput/TextinputQuesttypeController.inc | 12 ++++++++++++ questtypes/textinput/html/index.tpl | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc index e41facfd..ad4073ce 100644 --- a/questtypes/textinput/TextinputQuesttypeController.inc +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -31,10 +31,17 @@ */ public function index($questId) { + // Answers + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); + // Check for submission if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) { // Get right answers + $answers = array(); $regexs = $this->Textinput->getTextinputRegexs($questId); // Match regexs with user answers @@ -42,12 +49,16 @@ for($i=0; $irequest->getPostParam('answer-'.strval($i+1)); + $answers[] = $answer; $score = preg_match($regexs[$i]['regex'], $answer); if($score === 0 || $score === false) { $allSolved = false; } } + // Store answers in session + $_SESSION['answers'][$questId] = $answers; + // Set status if($allSolved) { $this->setQuestSolved(); @@ -67,6 +78,7 @@ // Pass data to view $this->set('text', $textParts); + $this->set('answers', $answers); } } diff --git a/questtypes/textinput/html/index.tpl b/questtypes/textinput/html/index.tpl index b3701630..714d828d 100644 --- a/questtypes/textinput/html/index.tpl +++ b/questtypes/textinput/html/index.tpl @@ -1,7 +1,7 @@
              0) : ?> - + From 6f91287afc3fe79ac3723ac78374ed7650833e8a Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 12 Mar 2014 01:28:26 +0100 Subject: [PATCH 079/340] correct for-loops and answers-array for Questtype ?Textinput? --- .../TextinputQuesttypeController.inc | 28 ++++++++++++------- questtypes/textinput/html/index.tpl | 8 +++--- views/html/quests/quest.tpl | 2 +- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc index ad4073ce..52acdb25 100644 --- a/questtypes/textinput/TextinputQuesttypeController.inc +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -40,25 +40,33 @@ // Check for submission if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) { + // Get answers + $answers = $this->request->getPostParam('answers'); + + // Store answers in session + $_SESSION['answers'][$questId] = $answers; + // Get right answers - $answers = array(); $regexs = $this->Textinput->getTextinputRegexs($questId); // Match regexs with user answers $allSolved = true; - for($i=0; $i &$regex) { - $answer = $this->request->getPostParam('answer-'.strval($i+1)); - $answers[] = $answer; - $score = preg_match($regexs[$i]['regex'], $answer); - if($score === 0 || $score === false) { + if(!array_key_exists($i, $answers)) + { $allSolved = false; + break; + } + + $score = preg_match($regex['regex'], $answers[$i]); + if($score === 0 || $score === false) + { + $allSolved = false; + break; } } - // Store answers in session - $_SESSION['answers'][$questId] = $answers; - // Set status if($allSolved) { $this->setQuestSolved(); @@ -77,7 +85,7 @@ // Pass data to view - $this->set('text', $textParts); + $this->set('texts', $textParts); $this->set('answers', $answers); } diff --git a/questtypes/textinput/html/index.tpl b/questtypes/textinput/html/index.tpl index 714d828d..40f0d213 100644 --- a/questtypes/textinput/html/index.tpl +++ b/questtypes/textinput/html/index.tpl @@ -1,10 +1,10 @@ - + &$text) : ?> 0) : ?> - + - - + +

              diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 66110f7b..42d218d1 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -76,6 +76,6 @@

              -

              +
              From 5461a4d93711d4c0c8b124717970cfda56723a8e Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 12 Mar 2014 01:29:11 +0100 Subject: [PATCH 080/340] implement Questtype ?Multiple choice? --- .../MultiplechoiceQuesttypeAgent.inc | 24 +++++ .../MultiplechoiceQuesttypeController.inc | 88 +++++++++++++++++++ .../MultiplechoiceQuesttypeModel.inc | 64 ++++++++++++++ questtypes/multiplechoice/html/index.tpl | 11 +++ 4 files changed, 187 insertions(+) create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/index.tpl 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..44c99e3b --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,88 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + + + + + /** + * Action: index. + * + * Display a text with input fields and evaluate if user input + * matches with stored regular expressions. + */ + public function index($questId) + { + // Answers + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); + + // Check for submission + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Get answers + $answers = $this->request->getPostParam('answers'); + + // Store answers in session + $_SESSION['answers'][$questId] = $answers; + + // Get right answers + $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($questId); + + // 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]); + } + } + + // Set status + if($allSolved && count($answers) == 0) { + $this->setQuestSolved(); + } + else { + $this->setQuestUnsolved(); + } + } + + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + + + // Pass data to view + $this->set('questions', $questions); + $this->set('answers', $answers); + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..cf170390 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,64 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 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 question, tick, pos '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND tick = True', + 'i', + $questId + ); + } + + } + +?> diff --git a/questtypes/multiplechoice/html/index.tpl b/questtypes/multiplechoice/html/index.tpl new file mode 100644 index 00000000..1e64f3c7 --- /dev/null +++ b/questtypes/multiplechoice/html/index.tpl @@ -0,0 +1,11 @@ + +
                + &$question) : ?> +
              1. + ]"> +
              2. + +
              + + From a017c7a79db48430ecf8b30763bf7ed3a0cf2ed2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 17 Mar 2014 16:00:48 +0100 Subject: [PATCH 081/340] belanglose vervollst?ndigung / test --- .hgignore | 2 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 ++++++++++++ agents/bottomlevel/MenuAgent.inc | 35 + .../QuestgroupshierarchypathAgent.inc | 35 + .../bottomlevel/QuestgroupspictureAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 37 ++ agents/intermediate/QuestsAgent.inc | 37 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 67 ++ apis/WebApi.inc | 250 ++++++++ app/Controller.inc | 99 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 231 +++++++ app/QuesttypeController.inc | 294 +++++++++ app/QuesttypeModel.inc | 154 +++++ app/QuesttypeView.inc | 76 +++ app/ToplevelAgent.inc | 36 ++ app/Utils.inc | 51 ++ app/controllers/SeminaryRoleController.inc | 136 ++++ app/controllers/ToplevelController.inc | 170 +++++ .../QuesttypeAgentNotFoundException.inc | 77 +++ .../QuesttypeAgentNotValidException.inc | 77 +++ .../QuesttypeControllerNotFoundException.inc | 77 +++ .../QuesttypeControllerNotValidException.inc | 77 +++ .../QuesttypeModelNotFoundException.inc | 77 +++ .../QuesttypeModelNotValidException.inc | 77 +++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 127 ++++ configs/CoreConfig.inc | 167 +++++ controllers/BinaryController.inc | 37 ++ controllers/CharactergroupsController.inc | 143 +++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 101 +++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 35 + controllers/MediaController.inc | 143 +++++ controllers/MenuController.inc | 50 ++ controllers/QuestgroupsController.inc | 124 ++++ .../QuestgroupshierarchypathController.inc | 76 +++ controllers/QuestgroupspictureController.inc | 65 ++ controllers/QuestsController.inc | 275 ++++++++ controllers/SeminariesController.inc | 214 ++++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 231 +++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ core/Agent.inc | 607 ++++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 424 ++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 2719 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 260 ++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 36 ++ models/CharactergroupsModel.inc | 147 +++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 155 +++++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 90 +++ models/QuestgroupsModel.inc | 307 +++++++++ models/QuestgroupshierarchyModel.inc | 103 +++ models/QuestsModel.inc | 304 +++++++++ models/QuesttextsModel.inc | 135 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 161 +++++ models/SeminarycharacterfieldsModel.inc | 58 ++ models/UserrolesModel.inc | 77 +++ models/UsersModel.inc | 249 +++++++ models/UserseminaryrolesModel.inc | 78 +++ questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 48 ++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/index.tpl | 4 + .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 88 +++ .../MultiplechoiceQuesttypeModel.inc | 64 ++ questtypes/multiplechoice/html/index.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 94 +++ .../textinput/TextinputQuesttypeModel.inc | 67 ++ questtypes/textinput/html/index.tpl | 11 + requests/WebRequest.inc | 401 ++++++++++++ responses/WebResponse.inc | 250 ++++++++ views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/charactergroups/group.tpl | 33 + views/html/charactergroups/groupsgroup.tpl | 18 + views/html/charactergroups/index.tpl | 9 + views/html/charactergroupsquests/quest.tpl | 46 ++ views/html/characters/character.tpl | 25 + views/html/characters/index.tpl | 8 + views/html/error/index.tpl | 2 + views/html/html.tpl | 31 + views/html/introduction/index.tpl | 21 + views/html/menu/index.tpl | 9 + views/html/questgroups/questgroup.tpl | 52 ++ views/html/questgroupshierarchypath/index.tpl | 8 + views/html/questgroupspicture/index.tpl | 3 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 81 +++ views/html/seminaries/create.tpl | 10 + views/html/seminaries/delete.tpl | 8 + views/html/seminaries/edit.tpl | 10 + views/html/seminaries/index.tpl | 14 + views/html/seminaries/seminary.tpl | 30 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 14 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 14 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 12 + views/html/users/logout.tpl | 0 views/html/users/user.tpl | 19 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 1 + www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ 177 files changed, 14495 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/QuestgroupspictureAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/controllers/ToplevelController.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestgroupspictureController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/index.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/index.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/index.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/questgroupspicture/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..5c118c1e --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +syntax: glob +media/* 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..c26a2f70 --- /dev/null +++ b/agents/intermediate/QuestsAgent.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 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); + } + + } + +?> 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..9f104be1 --- /dev/null +++ b/app/QuesttypeAgent.inc @@ -0,0 +1,231 @@ + + * @copyright 2014 Heinrich-Heine-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); + } + + + + + /** + * 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 = \nre\configs\CoreConfig::$defaults['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..743c5d59 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,294 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + + + + /** + * 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..76ccc7fe --- /dev/null +++ b/app/Utils.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z; + + + /** + * 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'); + } + + } + +?> 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..5b5a7558 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,127 @@ + + * @copyright 2014 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' + ); + + + /** + * 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), + array('media/(.*)', 'media/index/$1', true) + ); + + + /** + * 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), + array('media/index/(.*)', 'media/$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..581b5b5a --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,101 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + + // 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); + + // 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..aa69e704 --- /dev/null +++ b/controllers/MediaController.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 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) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getMediaByUrl($seminary['id'], $mediaUrl); + + // 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; + } + + + // Pass data to view + $this->set('media', $media); + } + + + + + /** + * Determine file information and set the HTTP-header for + * caching accordingly. + * + * @param string $fileName Filename + * @return boolean HTTP-status 304 was set (in cache) + */ + private function setCacheHeaders($fileName) + { + // Determine last change of file + $fileLastModified = gmdate('r', filemtime($fileName)); + + // Generate E-Tag + $fileEtag = hash('sha256', $fileLastModified.$fileName); + + + // Set header + $this->response->addHeader("Last-Modified: ".$fileLastModified); + $this->response->addHeader("Etag: ".$fileEtag); + // HTTP-status + $headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE'); + $headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH'); + if( + !is_null($headerModifiedSince) && $fileLastModified < strtotime($headerModifiedSince) && + !is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag + ) { + $this->response->setExit(true); + $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304)); + + return true; + } + + + return false; + } + + } + +?> diff --git a/controllers/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..42e19d05 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,124 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + // Check permission of Questgroups + for($i=1; $iQuestgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Quests + $quests = null; + if(count($childQuestgroupshierarchy) == 0) + { + $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']); + for($i=0; $i 0) { + $quests[$i]['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']); + } + + // Attach sidequests + $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quests[$i]['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..211c8104 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,275 @@ + + * @copyright 2014 Heinrich-Heine-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') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'quest' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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); + $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); + $questtext['count'] = $questtextCount; + $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']); + } + + // 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']); + + // Task + $task = $this->runAndRenderTask($quest['id'], $questtype['classname']); + } + + // Next Quest/Questgroup + $nextQuests = null; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + 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); + } + + + + + /** + * Load, construct, run and render the Agent for the given + * classname of a Questtype and return ist output. + * + * @param int $questId ID of Quest + * @param string $questtypeClassname Classname of Questtype to run and render + * @return string Rendered output of Questtype-Agent + */ + private function runAndRenderTask($questId, $questtypeClassname) + { + $task = null; + $questtypeAgent = null; + try { + // Load Agent + \hhu\z\QuesttypeAgent::load($questtypeClassname); + // Construct Agent + $questtypeAgent = \hhu\z\QuesttypeAgent::factory($questtypeClassname, $this->request, $this->response); + + // Generate request + $request = clone $this->request; + // Generate response + $response = clone $this->response; + $response->clearParams(1); + $response->addParams( + null, + \nre\configs\CoreConfig::$defaults['action'], + $questId + ); + // Run Agent + $questtypeAgent->run($request, $response); + + // Render output + $task = $questtypeAgent->render(); + + } + 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; + } + + } + +?> diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc new file mode 100644 index 00000000..bf89b9f2 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,214 @@ + + * @copyright 2014 Heinrich-Heine-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'); + /** + * 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']); + + // Check permission of Questgroups + for($i=1; $iQuestgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + } + } + + + // 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..a9eb9f91 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,231 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + + // Pass data to view + $this->set('user', $user); + $this->set('characters', $characters); + } + + + /** + * Action: login. + * + * Log in a user. + */ + public function login() + { + $username = ''; + + // Log the user in + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('login'))) + { + $username = $this->request->getPostParam('username'); + $userId = $this->Users->login( + $username, + $this->request->getPostParam('password') + ); + + if(!is_null($userId)) + { + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + // Pass data to view + $this->set('username', $username); + $this->set('failed', ($this->request->getRequestMethod() == 'POST')); + } + + + /** + * Action: logout. + * + * Log out a user. + */ + public function logout() + { + // Unset the currently logged in user + $this->Auth->setUserId(null); + + // Redirect + $this->redirect($this->linker->link(array())); + } + + + /** + * Action: create. + * + * Create a new user. + */ + public function create() + { + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Create new user + $userId = $this->Users->createUser( + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * Action: edit. + * + * Edit a user. + * + * @throws IdNotFoundException + * @param string $userUrl URL-Username of an user + */ + public function edit($userUrl) + { + // User + $user = $this->Users->getUserByUrl($userUrl); + + // Check request method + if($this->request->getRequestMethod() == 'POST') + { + // Save changes + if(!is_null($this->request->getPostParam('save'))) + { + // Edit user + $this->Users->editUser( + $user['id'], + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + $user = $this->Users->getUserById($user['id']); + } + + + // Redirect to entry + $this->redirect($this->linker->link(array($user['url']), 1)); + } + + + // Pass data to view + $this->set('user', $user); + } + + + /** + * Action: delete. + * + * Delete a user. + * + * @throws IdNotFoundException + * @param string $userUrl URL-Username of an user + */ + public function delete($userUrl) + { + // User + $user = $this->Users->getUserByUrl($userUrl); + + // Check request method + if($this->request->getRequestMethod() == 'POST') + { + // Check confirmation + if(!is_null($this->request->getPostParam('delete'))) + { + // Delete user + $this->Users->deleteUser($user['id']); + + // Redirect to overview + $this->redirect($this->linker->link(null, 1)); + } + + // Redirect to entry + $this->redirect($this->linker->link(array('user', $user['url']), 1)); + } + + + // Show confirmation + $this->set('user', $user); + } + + + } + +?> diff --git a/controllers/components/AchievementComponent.inc b/controllers/components/AchievementComponent.inc new file mode 100644 index 00000000..70601543 --- /dev/null +++ b/controllers/components/AchievementComponent.inc @@ -0,0 +1,41 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to handle achievements. + * + * @author Oliver Hanraths + */ + class AchievementComponent extends \nre\core\Component + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements'); + + + + + /** + * Construct a new Achievements-component. + */ + public function __construct() + { + } + + } + +?> diff --git a/controllers/components/AuthComponent.inc b/controllers/components/AuthComponent.inc new file mode 100644 index 00000000..0db6c69d --- /dev/null +++ b/controllers/components/AuthComponent.inc @@ -0,0 +1,79 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to handle authentication and authorization. + * + * @author Oliver Hanraths + */ + class AuthComponent extends \nre\core\Component + { + /** + * Key to save a user-ID as + * + * @var string + */ + const KEY_USER_ID = 'user_id'; + + + + + /** + * Construct a new Auth-component. + */ + public function __construct() + { + // Start session + if(session_id() === '') { + session_start(); + } + } + + + + + /** + * Set the ID of the user that is currently logged in. + * + * @param int $userId ID of the currently logged in user + */ + public function setUserId($userId) + { + if(is_null($userId)) { + unset($_SESSION[self::KEY_USER_ID]); + } + else { + $_SESSION[self::KEY_USER_ID] = $userId; + } + } + + + /** + * Get the ID of the user that is currently logged in. + * + * @return int ID of the currently logged in user + */ + public function getUserId() + { + if(array_key_exists(self::KEY_USER_ID, $_SESSION)) { + return $_SESSION[self::KEY_USER_ID]; + } + + + return null; + } + + } + +?> diff --git a/core/Agent.inc b/core/Agent.inc new file mode 100644 index 00000000..00ec1a90 --- /dev/null +++ b/core/Agent.inc @@ -0,0 +1,607 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for the implementation af an Agent. + * + * @author coderkun + */ + abstract class Agent + { + /** + * Name of BottomlevelAgent for showing inline errors + * + * @var string + */ + const INLINEERROR_AGENT = 'inlineerror'; + + /** + * Current request + * + * @var Request + */ + private $request; + /** + * Current response + * + * @var Response + */ + private $response; + /** + * Log-system + * + * @var Logger + */ + protected $log; + /** + * SubAgents + * + * @var array + */ + protected $subAgents = array(); + /** + * Controller of this Agent + * + * @var Controller + */ + public $controller = null; + + + + + /** + * Load the class of an Agent. + * + * @static + * @throws AgentNotFoundException + * @throws AgentNotValidException + * @param string $agentName Name of the Agent to load + */ + public static function load($agentName) + { + // Determine full classname + $agentType = self::getAgentType(); + $className = self::getClassName($agentName, $agentType); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + $parentAgentClassName = ClassLoader::concatClassNames($agentType, 'agent'); + $parentAgentClassName = "\\nre\\agents\\$parentAgentClassName"; + ClassLoader::check($className, $parentAgentClassName); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\AgentNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\AgentNotFoundException($e->getClassName()); + } + } + + + + /** + * Instantiate an Agent (Factory Pattern). + * + * @static + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + * @param string $agentName Name of the Agent to instantiate + * @param Request $request Current request + * @param Response $respone Current respone + * @param Logger $log Log-system + */ + public static function factory($agentName, Request $request, Response $response, Logger $log=null) + { + // Determine full classname + $agentType = self::getAgentType(); + $className = self::getClassName($agentName, $agentType); + + + // Construct and return Agent + return new $className($request, $response, $log); + } + + + /** + * Determine the type of an Agent. + * + * @static + * @return string Agent type + */ + private static function getAgentType() + { + return strtolower(ClassLoader::getClassName(get_called_class())); + } + + + /** + * Determine the classname for the given Agent name. + * + * @static + * @param string $agentName Agent name to get classname of + * @param string $agentType Agent type of given Agent name + * @return string Classname for the Agent name + */ + private static function getClassName($agentName, $agentType) + { + $className = ClassLoader::concatClassNames($agentName, 'agent'); + + + return \nre\configs\AppConfig::$app['namespace']."agents\\$agentType\\$className"; + } + + + + + /** + * Construct a new Agent. + * + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + * @param Request $request Current request + * @param Response $respone Current response + * @param Logger $log Log-system + */ + protected function __construct(Request $request, Response $response, Logger $log=null) + { + // Store values + $this->request = $request; + $this->response = $response; + $this->log = $log; + + // Construct SubAgent + $this->actionConstruct(); + + // Load corresponding Controller + $this->loadController(); + } + + + + + /** + * Run the Controller of this Agent and its SubAgents. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @throws DatamodelException + * @throws ActionNotFoundException + * @param Request $request Current request + * @param Response $response Current response + * @return Exception Last occurred exception of SubAgents + */ + public function run(Request $request, Response $response) + { + // Check Controller + if(!is_null($this->controller)) + { + // Call prefilter + $this->controller->preFilter($request, $response); + + // Run controller + $this->controller->run($request, $response); + + // Call postfilter + $this->controller->postFilter($request, $response); + } + + + // Run SubAgents + $exception = null; + foreach($this->subAgents as &$subAgent) + { + try { + $subAgent['object']->run( + $request, + $subAgent['response'] + ); + } + catch(ParamsNotValidException $e) { + $subAgent = $this->errorInline($subAgent, $request, $e); + } + catch(IdNotFoundException $e) { + $subAgent = $this->errorInline($subAgent, $request, $e); + } + catch(DatamodelException $e) { + $exception = $e; + $subAgent = $this->errorInline($subAgent, $request, $e); + } + catch(ActionNotFoundException $e) { + $subAgent = $this->errorInline($subAgent, $request, $e); + } + } + + + // Return last occurred exception + return $exception; + } + + + /** + * Generate output of the Controller of this Agent and its + * SubAgents. + * + * @param array $data View data + * @return string Generated output + */ + public function render($data=array()) + { + // Check Controller + if(!is_null($this->controller)) + { + // Render SubAgents + foreach($this->subAgents as $subAgent) + { + $label = array_key_exists('label', $subAgent) ? $subAgent['label'] : $subAgent['name']; + $data[$label] = $this->renderSubAgent($subAgent); + } + + // Render the Controller of this agent + return $this->controller->render($data); + } + } + + + + + /** + * Construct SubAgents (per Action). + */ + protected function actionConstruct() + { + // Action ermitteln + $action = $this->response->getParam(2); + if(is_null($action)) { + $action = $this->request->getParam(2, 'action'); + $this->response->addParam($action); + } + + // Initialisierungsmethode für diese Action ausführen + if(method_exists($this, $action)) + { + call_user_func_array( + array( + $this, + $action + ), + array( + $this->request, + $this->response + ) + ); + } + } + + + /** + * Load the Controller of this Agent. + * + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + */ + protected function loadController() + { + // Determine Controller name + $controllerName = ClassLoader::getClassName(get_class($this)); + + // Determine ToplevelAgent + $toplevelAgentName = $this->response->getParam(0); + if(is_null($toplevelAgentName)) { + $toplevelAgentName = $this->request->getParam(0, 'toplevel'); + $this->response->addParam($toplevelAgentName); + } + + // Determine Action + $action = $this->response->getParam(2); + if(is_null($action)) { + $action = $this->request->getParam(2, 'action'); + $this->response->addParam($action); + } + + + // Load Controller + Controller::load($controllerName); + + // Construct Controller + $this->controller = Controller::factory($controllerName, $toplevelAgentName, $action, $this); + } + + + /** + * Log an error. + * + * @param Exception $exception Occurred exception + * @param int $logMode Log mode + */ + protected function log($exception, $logMode) + { + if(is_null($this->log)) { + return; + } + + $this->log->log( + $exception->getMessage(), + $logMode + ); + } + + + /** + * Load a SubAgent and add it. + * + * @throws ServiceUnavailableException + * @throws AgentNotFoundException + * @throws AgentNotValidException + * @param string $agentName Name of the Agent to load + * @param mixed … Additional parameters for the agent + */ + protected function addSubAgent($agentName) + { + try { + call_user_func_array( + array( + $this, + '_addSubAgent' + ), + func_get_args() + ); + } + catch(DatamodelException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + } + + + /** + * Load a SubAgent and add it. + * + * @throws ServiceUnavailableException + * @throws DatamodelException + * @throws AgentNotFoundException + * @throws AgentNotValidException + * @param string $agentName Name of the Agent to load + * @param mixed … Additional parameters for the agent + */ + protected function _addSubAgent($agentName) + { + try { + // Load Agent + \nre\agents\BottomlevelAgent::load($agentName); + + // Construct Agent + $this->subAgents[] = call_user_func_array( + array( + $this, + 'newSubAgent' + ), + func_get_args() + ); + } + catch(ViewNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(DriverNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(DriverNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ModelNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ModelNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ControllerNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ControllerNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(AgentNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(AgentNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + } + + + + + /** + * Create a new SubAgent. + * + * @throws DatamodelException + * @param string $agentName Agent name + * @return array SubAgent + */ + private function newSubAgent($agentName) + { + // Response + $response = clone $this->response; + $response->clearParams(1); + $params = func_get_args(); + if(count($params) < 2 || empty($params[1])) { + $params[1] = \nre\configs\CoreConfig::$defaults['action']; + } + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + return array( + 'name' => strtolower($agentName), + 'response' => $response, + 'object' => \nre\agents\BottomlevelAgent::factory( + $agentName, + $this->request, + $response, + $this->log + ) + ); + } + + + /** + * Render a SubAgent. + * + * @param array $subAgent SubAgent to render + * @return string Generated output + */ + private function renderSubAgent(&$subAgent) + { + // Check for InlineError + if(array_key_exists('inlineerror', $subAgent) && !empty($subAgent['inlineerror'])) { + return file_get_contents($subAgent['inlineerror']); + } + + + // Rendern SubAgent and return its output + return $subAgent['object']->render(); + } + + + /** + * Handle the exception of a SubAgent. + * + * @param string $label Name of the original Agent + * @param Excepiton $exception Occurred exception + * @return array InlineError-SubAgent + */ + private function errorInline($subAgent, $request, $exception) + { + // Create the SubAgent for the exception + $subAgent = $this->newInlineError($subAgent['name'], $exception); + + + // Run the InlineError-SubAgent + try { + $subAgent['object']->run( + $request, + $subAgent['response'] + ); + } + catch(ActionNotFoundException $e) { + $this->log($e, Logger::LOGMODE_AUTO); + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + + + // Return the InlineError-SubAgent + return $subAgent; + } + + + /** + * Create a new InlineError. + * + * @param string $label Name of the original Agent + * @param Exception $exception Occurred exception + */ + private function newInlineError($label, $exception) + { + // Log error + $this->log($exception, Logger::LOGMODE_AUTO); + + // Determine Agent name + $agentName = self::INLINEERROR_AGENT; + + // Create SugAgent + $subAgent = array(); + + + try { + // Load Agenten + \nre\agents\BottomlevelAgent::load($agentName); + + // Construct Agent + $subAgent = $this->newSubAgent($agentName); + $subAgent['label'] = $label; + $subAgent['response']->addParam($exception); + } + catch(ViewNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(DatamodelException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(DriverNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(DriverNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ModelNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ModelNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ControllerNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ControllerNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(AgentNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(AgentNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + + + // Return SubAgent + return $subAgent; + } + + + /** + * Handle a hardcore error that could not be handled by the + * system. + */ + private function newInlineErrorService() + { + // Read and return static error file + return ROOT.DS.\nre\configs\CoreConfig::getClassDir('views').DS.\nre\configs\CoreConfig::$defaults['inlineErrorFile'].\nre\configs\Config::getFileExt('views'); + } + + } + +?> diff --git a/core/Api.inc b/core/Api.inc new file mode 100644 index 00000000..b89b12e7 --- /dev/null +++ b/core/Api.inc @@ -0,0 +1,163 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class to implement an API. + * + * The API is the center of each application and specifies how and what + * to run and render. + * + * @author coderkun + */ + abstract class Api + { + /** + * Die aktuelle Anfrage + * + * @var Request + */ + protected $request; + /** + * Der Toplevelagent + * + * @var ToplevelAgent + */ + private $toplevelAgent = null; + /** + * Die aktuelle Antwort + * + * @var Response + */ + protected $response; + /** + * Log-System + * + * @var Logger + */ + protected $log; + + + + + /** + * Construct a new API. + * + * @param Request $request Current request + * @param Response $respone Current response + */ + public function __construct(Request $request, Response $response) + { + // Store request + $this->request = $request; + + // Store response + $this->response = $response; + + // Init logging + $this->log = new \nre\core\Logger(); + } + + + + + /** + * Run the application. + * + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + * @throws AgentNotValidException + * @throws AgentNotFoundException + * @return Exception Last occurred exception of an subagent + */ + public function run() + { + // Load ToplevelAgent + $this->loadToplevelAgent(); + + // Run ToplevelAgent + return $this->toplevelAgent->run($this->request, $this->response); + } + + + /** + * Render the output. + */ + public function render() + { + // Check exit-status + if($this->response->getExit()) { + return; + } + + // Render ToplevelAgent + $this->response->setOutput($this->toplevelAgent->render()); + } + + + + + /** + * Log an exception + * + * @param Exception $exception Occurred exception + * @param int $logMode Log-mode + */ + protected function log($exception, $logMode) + { + $this->log->log( + $exception->getMessage(), + $logMode + ); + } + + + + + /** + * Load the ToplevelAgent specified by the request. + * + * @throws ServiceUnavailableException + * @throws AgentNotValidException + * @throws AgentNotFoundException + */ + private function loadToplevelAgent() + { + // Determine agent + $agentName = $this->response->getParam(0); + if(is_null($agentName)) { + $agentName = $this->request->getParam(0, 'toplevel'); + $this->response->addParam($agentName); + } + + // Load agent + \nre\agents\ToplevelAgent::load($agentName); + + // Construct agent + $this->toplevelAgent = \nre\agents\ToplevelAgent::factory( + $agentName, + $this->request, + $this->response, + $this->log + ); + } + + } + +?> diff --git a/core/Autoloader.inc b/core/Autoloader.inc new file mode 100644 index 00000000..020b61f7 --- /dev/null +++ b/core/Autoloader.inc @@ -0,0 +1,98 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Autoloader. + * + * This class tries to load not yet used classes. + * + * @author coderkun + */ + class Autoloader + { + /** + * Private construct(). + */ + private function __construct() {} + + /** + * Private clone(). + */ + private function __clone() {} + + + + + /** + * Register load-method. + */ + public static function register() + { + spl_autoload_register( + array( + get_class(), + 'load' + ) + ); + } + + + /** + * Look for the given class and try to load it. + * + * @param string $fullClassName Die zu ladende Klasse + */ + public static function load($fullClassName) + { + $fullClassNameA = explode('\\', $fullClassName); + + if(strpos($fullClassName, \nre\configs\CoreConfig::$core['namespace']) !== 0) + { + // App + $className = array_slice($fullClassNameA, substr_count(\nre\configs\AppConfig::$app['namespace'], '\\')); + array_unshift($className, \nre\configs\CoreConfig::getClassDir('app')); + $filename = ROOT.DS.implode(DS, $className).\nre\configs\CoreConfig::getFileExt('includes'); + if(file_exists($filename)) { + require_once($filename); + } + } + else + { + // Core + $className = array_slice($fullClassNameA, substr_count(\nre\configs\CoreConfig::$core['namespace'], '\\')); + $filename = ROOT.DS.implode(DS, $className).\nre\configs\CoreConfig::getFileExt('includes'); + if(file_exists($filename)) { + require_once($filename); + } + } + + + } + + + /** + * Determine classtype of a class. + * + * @param string $className Name of the class to determine the classtype of + * @return string Classtype of the given class + */ + public static function getClassType($className) + { + // CamelCase + return strtolower(preg_replace('/^.*([A-Z][^A-Z]+)$/', '$1', $className)); + } + + } + +?> diff --git a/core/ClassLoader.inc b/core/ClassLoader.inc new file mode 100644 index 00000000..81cf537a --- /dev/null +++ b/core/ClassLoader.inc @@ -0,0 +1,129 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class for safely loading classes. + * + * @author coderkun + */ + class ClassLoader + { + + + + + /** + * Load a class. + * + * @throws ClassNotFoundException + * @param string $className Name of the class to load + */ + public static function load($fullClassName) + { + // Determine folder to look in + $className = explode('\\', $fullClassName); + $className = array_slice($className, substr_count(\nre\configs\AppConfig::$app['namespace'], '\\')); + + // Determine filename + $fileName = ROOT.DS.implode(DS, $className). \nre\configs\CoreConfig::getFileExt('includes'); + + + // Check file + if(!file_exists($fileName)) + { + throw new \nre\exceptions\ClassNotFoundException( + $fullClassName + ); + } + + // Include file + include_once($fileName); + } + + + /** + * Check inheritance of a class. + * + * @throws ClassNotValidException + * @param string $className Name of the class to check + * @param string $parentClassName Name of the parent class + */ + public static function check($className, $parentClassName) + { + // Check if class is subclass of parent class + if(!is_subclass_of($className, $parentClassName)) { + throw new \nre\exceptions\ClassNotValidException( + $className + ); + } + } + + + /** + * Strip the namespace from a class name. + * + * @param string $class Name of a class including its namespace + * @return Name of the given class without its namespace + */ + public static function stripNamespace($class) + { + return array_slice(explode('\\', $class), -1)[0]; + } + + + /** + * Strip the class type from a class name. + * + * @param string $className Name of a class + * @return Name of the given class without its class type + */ + public static function stripClassType($className) + { + return preg_replace('/^(.*)[A-Z][^A-Z]+$/', '$1', $className); + } + + + /** + * Strip the namespace and the class type of a full class name + * to get only its name. + * + * @param string $class Full name of a class + * @return Only the name of the given class + */ + public static function getClassName($class) + { + return self::stripClassType(self::stripNamespace($class)); + } + + + /** + * Concatenate strings to a class name following the CamelCase + * pattern. + * + * @param string $className1 Arbitrary number of strings to concat + * @return string Class name as CamelCase + */ + public static function concatClassNames($className1) + { + return implode('', array_map( + function($arg) { + return ucfirst(strtolower($arg)); + }, + func_get_args() + )); + } + + } + +?> diff --git a/core/Component.inc b/core/Component.inc new file mode 100644 index 00000000..a93363dc --- /dev/null +++ b/core/Component.inc @@ -0,0 +1,85 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class to implement a (Controller) Component. + * + * @author coderkun + */ + abstract class Component + { + + + + + /** + * Load the class of a Component. + * + * @throws ComponentNotFoundException + * @throws ComponentNotValidException + * @param string $componentName Name of the Component to load + */ + public static function load($componentName) + { + // Determine full classname + $className = self::getClassName($componentName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ComponentNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ComponentNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Component (Factory Pattern). + * + * @param string $componentName Name of the Component to instantiate + */ + public static function factory($componentName) + { + // Determine full classname + $className = self::getClassName($componentName); + + // Construct and return Controller + return new $className(); + } + + + /** + * Determine the classname for the given Component name. + * + * @param string $componentName Component name to get classname of + * @return string Classname for the Component name + */ + private static function getClassName($componentName) + { + $className = \nre\core\ClassLoader::concatClassNames($componentName, \nre\core\ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."controllers\\components\\$className"; + } + + } + +?> diff --git a/core/Config.inc b/core/Config.inc new file mode 100644 index 00000000..b51f1e47 --- /dev/null +++ b/core/Config.inc @@ -0,0 +1,49 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Configuration. + * + * This class does not hold any configuration value but helps to + * determine values that can be hold by AppConfig or CoreConfig. + * + * @author coderkun + */ + final class Config + { + + + + + /** + * Get a default value. + * + * @param string $index Index of value to get + */ + public static function getDefault($index) + { + if(array_key_exists($index, \nre\configs\AppConfig::$defaults)) { + return \nre\configs\AppConfig::$defaults[$index]; + } + if(array_key_exists($index, \nre\configs\CoreConfig::$defaults)) { + return \nre\configs\CoreConfig::$defaults[$index]; + } + + + return null; + } + + } + +?> diff --git a/core/Controller.inc b/core/Controller.inc new file mode 100644 index 00000000..c2814e99 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,424 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Controller. + * + * @author coderkun + */ + abstract class Controller + { + /** + * Corresponding Agent + * + * @var Agent + */ + protected $agent; + /** + * View of the Controller + * + * @var View + */ + protected $view = null; + /** + * Data to pass to the View + * + * @var array + */ + protected $viewData = array(); + /** + * Current request + * + * @var Request + */ + protected $request = null; + /** + * Current response + * + * @var Response + */ + protected $response = null; + + + + + /** + * Load the class of a Controller. + * + * @throws ControllerNotFoundException + * @throws ControllerNotValidException + * @param string $controllerName Name of the Controller to load + */ + public static function load($controllerName) + { + // Determine full classname + $className = self::getClassName($controllerName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ControllerNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ControllerNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Controller (Factory Pattern). + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ViewNotFoundException + * @param string $controllerName Name of the Controller to instantiate + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + public static function factory($controllerName, $layoutName, $action, $agent) + { + // Determine full classname + $className = self::getClassName($controllerName); + + // Construct and return Controller + return new $className($layoutName, $action, $agent); + } + + + /** + * Determine the classname for the given Controller name. + * + * @param string $controllerName Controller name to get classname of + * @return string Classname for the Controller name + */ + private static function getClassName($controllerName) + { + $className = \nre\core\ClassLoader::concatClassNames($controllerName, \nre\core\ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."controllers\\$className"; + } + + + + + /** + * Construct a new Controller. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + * @param Agent $agent Corresponding Agent + */ + protected function __construct($layoutName, $action, $agent) + { + // Store values + $this->agent = $agent; + + // Load Components + $this->loadComponents(); + + // Load Models + $this->loadModels(); + + // Load View + $this->loadView($layoutName, $action); + } + + + + + /** + * Prefilter that is executed before running the Controller. + * + * @param Request $request Current request + * @param Response $response Current response + */ + public function preFilter(Request $request, Response $response) + { + // Request speichern + $this->request = $request; + + // Response speichern + $this->response = $response; + + + // Linker erstellen + $this->set('linker', new \nre\core\Linker($request)); + + } + + + /** + * Prefilter that is executed after running the Controller. + * + * @param Request $request Current request + * @param Response $response Current response + */ + public function postFilter(Request $request, Response $response) + { + } + + + /** + * Run the Controller. + * + * This method executes the Action of the Controller defined by + * the current Request. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @throws DatamodelException + * @throws ActionNotFoundException + * @param Request $request Current request + * @param Response $response Current response + */ + public function run(Request $request, Response $response) + { + // Determine Action + $action = $response->getParam(2, 'action'); + if(!method_exists($this, $action)) { + throw new \nre\exceptions\ActionNotFoundException($action); + } + + // Determine parameters + $params = $response->getParams(3); + if(empty($params)) { + $params = $request->getParams(3); + } + + // Fill missing parameters + $rc = new \ReflectionClass($this); + $nullParamsCount = $rc->getMethod($action)->getNumberOfParameters() - count($params); + $nullParams = ($nullParamsCount > 0 ? array_fill(0, $nullParamsCount, NULL) : array()); + + + // Call Action + call_user_func_array( + array( + $this, + $action + ), + array_merge( + $params, + $nullParams + ) + ); + } + + + /** + * Generate the output. + * + * @param array $viewData Data to pass to the View + * @return string Generated output + */ + public function render($viewData=null) + { + // Combine given data and data of this Controller + $data = $this->viewData; + if(!is_null($viewData)) { + $data = array_merge($viewData, $data); + } + + // Rendern and return output + return $this->view->render($data); + } + + + + + /** + * Set data for the View. + * + * @param string $name Key + * @param mixed $data Value + */ + protected function set($name, $data) + { + $this->viewData[$name] = $data; + } + + + /** + * Redirect to the given URL. + * + * @param string $url Relative URL + */ + protected function redirect($url) + { + $url = 'http://'.$_SERVER['HTTP_HOST'].$url; + header('Location: '.$url); + exit; + } + + + /** + * Check if Models of this Controller are loaded and available. + * + * @param string $modelName Arbitrary number of Models to check + * @return bool All given Models are loaded and available + */ + protected function checkModels($modelName) + { + foreach(func_get_args() as $modelName) + { + if(!isset($this->$modelName) || !is_subclass_of($this->$modelName, 'Model')) { + return false; + } + } + + + return true; + } + + + /** + * Get the View of the Controller + * + * @return View View of the Controller + */ + protected function getView() + { + return $this->view; + } + + + + + /** + * Load the Components of this Controller. + * + * @throws ComponentNotValidException + * @throws ComponentNotFoundException + */ + private function loadComponents() + { + // Determine components + $components = array(); + if(property_exists($this, 'components')) { + $components = $this->components; + } + if(!is_array($components)) { + $components = array($components); + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
              \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..d2a2559161443dd7666423b67ee58c7d64c09e6e GIT binary patch literal 2719 zcmaKsO^6&t6vs5b^)IYd+Lq(bd0tRj*#v zd#|d#-Mr?4Kp8>32lbWfgct|c-+&*=wlzX*0tdi5!98FJJObVh&VaXq%JU3(3*_fL z&x0EvzXq-Y-vZZy?}BXi122CDu7~`E=QqCpXYfY!Uj=#H-~2cmxf${X&#fTa-HspY z4|;i@=P1bY4}k3JI9LWP2$SMXa4YyWh);a%`#%ABozH#$mmvG`HF!PvJ;>|*1l|Q+ z_3}D&a(p&{Jig0wFUWq6g1qjy?>`3eI>*5`kb{#T>-}+45%+Z%y#7Ww zzY!b&(ItjJ)~|r9|ETBVo;5!{4OTEd2UfusLAWM90@;sGLALW9$aa4O*^ghm{3po! zSc^^Y(cA)}OALT_gFC%^2;2gB3S>K{JzF5h=M2d6UI2Oh^IpCHBJ6o#`)rf%1DC-9 zxqobfi$onp-G|C$Z-LwgZn95&o_3(_M&-ii1Ivg#sO%e;?Wnw8E}SRM#Q-Yj=zi2r z*@XuCKZ5!oDxU!^51?+&TZLb~DFQUuf6ghNGcH4@?DM^-e0I6;o#uRY%6(|;WMRCe zj0znYIcd_YZU56DpUTL(zJ4?fDMwP%inO#8Yh_mZ`!Y*LOi)75>5hBZkJ-?~Z5O9n z)S}q+v~nresTq?RQM+>P{=*vppoa!Ba)3(If!L6{-W5qNYZeTB4MZ*Tbv7u z{IFHaBy5^2V=&XcF>BIiDRjY96g*AFBBNDVM}(%SNg6X32EQ* zQcx$g)F)Swj?)vh(#+IMt+&C-aJe*&5RrzN)i(Cy=s7piP7t$$GQ^FrezhTp503ct1u8F*u?uwUujC<#1^>$&5;Z zqp4Z4RjJ##Xvanm%Az?oq~(q=S=oKCH-A3OOh|lDv8}2s_SLmMPs)U?S=bNCSYddi z_?BZ@v8CcRFCvXileOEVd)M1{1pU{W6$y7GBQ2lEjFm{Kqa?w+qVovp%$>zy?hWL6 z$>sNL*n(PDp|1xchT=x1n<7_kV8;!L5&oWfU*Az`B{=4NSD$Y_iDNO=ZBolgvT}vz z8$1NLS4kn&V!d$r!;ZlHha|W^lPL@k4M?uEb_BiNh9lT7u;Azi?sj% literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..80b79587 --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,260 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-03-04 11:59+0100\n" +"PO-Revision-Date: 2014-03-04 12:00+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:1 +#: ../../../views/html/charactergroups/groupsgroup.tpl:1 +#: ../../../views/html/charactergroups/index.tpl:1 +#: ../../../views/html/charactergroupsquests/quest.tpl:1 +#: ../../../views/html/menu/index.tpl:3 +#: ../../../views/html/questgroups/questgroup.tpl:1 +#: ../../../views/html/quests/quest.tpl:1 +#: ../../../views/html/quests/sidequest.tpl:1 +#: ../../../views/html/seminaries/create.tpl:1 +#: ../../../views/html/seminaries/delete.tpl:1 +#: ../../../views/html/seminaries/edit.tpl:1 +#: ../../../views/html/seminaries/index.tpl:1 +#: ../../../views/html/seminaries/seminary.tpl:1 +msgid "Seminaries" +msgstr "Kurse" + +#: ../../../views/html/charactergroups/group.tpl:3 +#: ../../../views/html/charactergroups/groupsgroup.tpl:3 +#: ../../../views/html/charactergroups/index.tpl:3 +#: ../../../views/html/characters/character.tpl:16 +#: ../../../views/html/seminaries/seminary.tpl:9 +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:8 +#: ../../../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:31 +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 +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:8 +msgid "User" +msgstr "Benutzer" + +#: ../../../views/html/html.tpl:21 +msgid "as" +msgstr "als" + +#: ../../../views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: ../../../views/html/menu/index.tpl:2 ../../../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:5 ../../../views/html/users/login.tpl:2 +#: ../../../views/html/users/login.tpl:11 +msgid "Login" +msgstr "Login" + +#: ../../../views/html/menu/index.tpl:7 +msgid "Logout" +msgstr "Logout" + +#: ../../../views/html/questgroups/questgroup.tpl:22 +#: ../../../views/html/questgroups/questgroup.tpl:47 +#: ../../../views/html/seminaries/seminary.tpl:26 +msgid "locked" +msgstr "gesperrt" + +#: ../../../views/html/questgroups/questgroup.tpl:38 +msgid "containing optional Quests" +msgstr "Enthaltene optionale Quests" + +#: ../../../views/html/quests/quest.tpl:15 +#: ../../../views/html/quests/sidequest.tpl:17 +msgid "solved" +msgstr "gelöst" + +#: ../../../views/html/quests/quest.tpl:17 +#: ../../../views/html/quests/sidequest.tpl:19 +msgid "unsolved" +msgstr "ungelöst" + +#: ../../../views/html/quests/quest.tpl:53 +msgid "Go on" +msgstr "Hier geht es weiter" + +#: ../../../views/html/quests/quest.tpl:57 +msgid "Quest" +msgstr "Quest" + +#: ../../../views/html/quests/quest.tpl:70 +#: ../../../views/html/quests/sidequest.tpl:51 +msgid "Task" +msgstr "Aufgabe" + +#: ../../../views/html/quests/sidequest.tpl:9 +msgid "This Quest is optional" +msgstr "Diese Quest ist optional" + +#: ../../../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:5 +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:4 +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:12 +#, 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 "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..59125e32 --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,155 @@ + + * @copyright 2014 Heinrich-Heine-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]; + } + + } + +?> 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..088d54c0 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,307 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + if(is_null($parentQuestgroupId)) + { + return $this->db->query( + 'SELECT id, questgroupshierarchy_id, pos, title, url '. + 'FROM questgroups '. + 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id IS NULL '. + 'ORDER BY questgroups.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + return $this->db->query( + 'SELECT id, questgroupshierarchy_id, pos, title, url '. + 'FROM questgroups '. + 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id = ? '. + 'ORDER BY questgroups.pos ASC', + 'ii', + $hierarchyId, $parentQuestgroupId + ); + } + } + + + /** + * 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 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); + } + + + + + /** + * 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..09ebc125 --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,304 @@ + + * @copyright 2014 Heinrich-Heine-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); + } + + } + +?> 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/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..0f3012fa --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.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\questtypes; + + + /** + * Controller of the Dummy-QuesttypeAgent for testing basic + * QuesttypeAgent-functionality. + * + * @author Oliver Hanraths + */ + class DummyQuesttypeController extends \hhu\z\QuesttypeController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Check for submission + if($this->request->getRequestMethod() == 'POST') + { + // Right answer (dummy) + if(!is_null($this->request->getPostParam('submit'))) { + $this->setQuestSolved(); + } + // Wrong answer (dummy) + else { + $this->setQuestUnsolved(); + } + } + } + + } + +?> 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/index.tpl b/questtypes/dummy/html/index.tpl new file mode 100644 index 00000000..f482a07b --- /dev/null +++ b/questtypes/dummy/html/index.tpl @@ -0,0 +1,4 @@ +
              + + + 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..44c99e3b --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,88 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + + + + + /** + * Action: index. + * + * Display a text with input fields and evaluate if user input + * matches with stored regular expressions. + */ + public function index($questId) + { + // Answers + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); + + // Check for submission + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Get answers + $answers = $this->request->getPostParam('answers'); + + // Store answers in session + $_SESSION['answers'][$questId] = $answers; + + // Get right answers + $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($questId); + + // 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]); + } + } + + // Set status + if($allSolved && count($answers) == 0) { + $this->setQuestSolved(); + } + else { + $this->setQuestUnsolved(); + } + } + + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + + + // Pass data to view + $this->set('questions', $questions); + $this->set('answers', $answers); + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..cf170390 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,64 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 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 question, tick, pos '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND tick = True', + 'i', + $questId + ); + } + + } + +?> diff --git a/questtypes/multiplechoice/html/index.tpl b/questtypes/multiplechoice/html/index.tpl new file mode 100644 index 00000000..1e64f3c7 --- /dev/null +++ b/questtypes/multiplechoice/html/index.tpl @@ -0,0 +1,11 @@ +
              +
                + &$question) : ?> +
              1. + ]"> +
              2. + +
              + + 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..52acdb25 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,94 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + + + + + /** + * Action: index. + * + * Display a text with input fields and evaluate if user input + * matches with stored regular expressions. + */ + public function index($questId) + { + // Answers + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); + + // Check for submission + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Get answers + $answers = $this->request->getPostParam('answers'); + + // Store answers in session + $_SESSION['answers'][$questId] = $answers; + + // Get right answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + + // Match regexs with user answers + $allSolved = true; + foreach($regexs as $i => &$regex) + { + if(!array_key_exists($i, $answers)) + { + $allSolved = false; + break; + } + + $score = preg_match($regex['regex'], $answers[$i]); + if($score === 0 || $score === false) + { + $allSolved = false; + break; + } + } + + // Set status + if($allSolved) { + $this->setQuestSolved(); + } + else { + $this->setQuestUnsolved(); + } + } + + + // Get Task + $task = $this->Textinput->getTextinputQuest($questId); + + // Process text + $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY); + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('answers', $answers); + } + + } + +?> diff --git a/questtypes/textinput/TextinputQuesttypeModel.inc b/questtypes/textinput/TextinputQuesttypeModel.inc new file mode 100644 index 00000000..d242f251 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeModel.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\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 + ); + + + return $data[0]; + } + + + /** + * 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 number, regex '. + 'FROM questtypes_textinput_regexs '. + 'WHERE questtypes_textinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + } + +?> diff --git a/questtypes/textinput/html/index.tpl b/questtypes/textinput/html/index.tpl new file mode 100644 index 00000000..40f0d213 --- /dev/null +++ b/questtypes/textinput/html/index.tpl @@ -0,0 +1,11 @@ +
              + &$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/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..d0f3e0cd --- /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..96146f31 --- /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..3108b483 --- /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..584428fb --- /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..cf0e9923 --- /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..b4bb166d --- /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..f16261cb --- /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..0f68bc07 --- /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..ab275bdf --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,31 @@ + + + + + + The Legend of Z + + + + + +
              +

              The Legend of Z

              + + +
              + + + ( XPs, ) + +
              + +
              +
              + +
              + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..90ca59f7 --- /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..f9f8246c --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ + +
            • ">
            • +
            • ">
            • + +
            • + +
            • + +
              diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..d8d2ce42 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,52 @@ +

              +

              + + + + +

              :

              + +

              + + + + 0) : ?> +

              +
                + +
              • + : + + + + + +
              • + +
              + + + + +

              +
                + +
              • + + + 0) : ?> +
                + : +
                  + +
                • + +
                + + + + +
              • + +
              + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..9bfe0099 --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,8 @@ + 0) : ?> +Pfad: + + 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..42d218d1 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,81 @@ +

              +

              + + + + + +

              + +

              +

              + + + + + + + +
              + +

              + +

              + +

              +
              + + + +
              +

              +

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

              + 0) : ?> +
                + + +
              • :
              • + +
              • :
              • + + +
              + + : + + Spiel vorbei + +
              + + + +
              +

              +

              + +
              + diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..823eec70 --- /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..d2253f14 --- /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..007acf15 --- /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..8ac8c8b0 --- /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..e84f15f4 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,30 @@ +

              +

              + + +

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

              +

              Beschreibung

              +

              + + +

              +
                + +
              • + : + +
              • + + + + +
              + 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..8536804d --- /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..10f280ab --- /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..ef47268c --- /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..5d2883af --- /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..36c13da4 --- /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..506fdf94 --- /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..9f44090c --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1 @@ +@charset "UTF-8"; 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'; + +?> From cb70cf75d9d44a84b4799f64249dc67dc03da1d2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 17 Mar 2014 20:42:38 +0100 Subject: [PATCH 082/340] css (reset & mobile first nav) --- www/css/desktop.css | 75 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/www/css/desktop.css b/www/css/desktop.css index 9f44090c..0f927d20 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -1 +1,76 @@ @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;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} +ul,ol{padding:0;list-style-type:none} +p{margin:0 0 .9375em;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:#347894;text-decoration:none} + +header{background:#0f373c;margin-bottom:1.1875em;position:fixed;width:100%;z-index:99} +header .fa{padding:0 10px 0 0;color:#5e9499} + +nav{z-index:99} +menu{display:none;opacity:0;position:absolute;margin:0;padding:0 0 16px;background:#0f373c;right:0;left:0} +menu li{display:block;margin:0 5%} +menu a{display:block;padding:15px 0;color:#fff} + +#profile{float:right;padding:18px;color:#fff} + +#toggle,.toggle{display:none} +.toggle{display:inline-block;padding:18px 18px 18px 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-top:5.375em;margin-bottom:30px} + + +/** Media Queries **/ + From 69e1bef4fa2fc317869e89b774b00b50b39e2269 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 17 Mar 2014 20:45:45 +0100 Subject: [PATCH 083/340] meta data for css, webfonts, ie8 support --- views/html/html.tpl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index ab275bdf..54499a6f 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -4,8 +4,16 @@ The Legend of Z - + + + + + + From b57e0175b3b71658e25144b3ddf4a93e33c6cfde Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 17 Mar 2014 21:09:59 +0100 Subject: [PATCH 084/340] menu changes according to css --- views/html/html.tpl | 13 +++++++++---- views/html/menu/index.tpl | 8 ++++---- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 54499a6f..98dc7390 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -18,10 +18,17 @@
              -

              The Legend of Z

              -
              +
              +

              The Legend of Z

              @@ -30,8 +37,6 @@
              - -
              diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index f9f8246c..62af9696 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,9 +1,9 @@ -
            • ">
            • -
            • ">
            • +
            • ">
            • +
            • ">
            • -
            • +
            • -
            • +
            • From c3a555d6a00a70c21c818add136892e47408ff99 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 18 Mar 2014 18:11:14 +0100 Subject: [PATCH 085/340] seminary detail restructuring & css up to 479px width --- views/html/seminaries/seminary.tpl | 46 ++++++++++++++++++------------ www/css/desktop.css | 38 ++++++++++++++++++++---- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index e84f15f4..30fb5a71 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,5 +1,32 @@

              +

              Beschreibung

              +

              + + +

              +
                + +
              • +
                +

                : + +

                +
                + +
                +

                350 / 450 XP

                +

                Einleitungstext: Mit völlig verseuchtem Tagewerk machst du dich an die Arbeit und stellst schnell fest...

                + Auf ins Abenteuer! + +

                + +
                +
              • + +
              + +

              format(new \DateTime($seminary['created'])))?> -

              -

              Beschreibung

              -

              - - -

              -
                - -
              • - : - -
              • - - - - -
              - +

              \ No newline at end of file diff --git a/www/css/desktop.css b/www/css/desktop.css index 0f927d20..b47ef8b4 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -3,14 +3,17 @@ /** CSS-Reset **/ html{font-family:'Open Sans',sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} -body{margin:0;background:#f7f5f2;font:100%/1.6 'Open Sans',sans-serif;-webkit-animation:bugfix infinite 1s} +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} -ul,ol{padding:0;list-style-type:none} -p{margin:0 0 .9375em;padding: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} @@ -52,11 +55,12 @@ table{border-collapse:collapse;border-spacing:0} .wrap{margin:0 5%;max-height:999999px;min-height:1px} a{color:#347894;text-decoration:none} +.fwb{font-weight:700} header{background:#0f373c;margin-bottom:1.1875em;position:fixed;width:100%;z-index:99} header .fa{padding:0 10px 0 0;color:#5e9499} -nav{z-index:99} +header nav{z-index:99} menu{display:none;opacity:0;position:absolute;margin:0;padding:0 0 16px;background:#0f373c;right:0;left:0} menu li{display:block;margin:0 5%} menu a{display:block;padding:15px 0;color:#fff} @@ -68,9 +72,33 @@ menu a{display:block;padding:15px 0;color:#fff} #toggle:checked ~ menu{display:block;opacity:1} #navicon{display:block;color:#fff} - article{padding-top:5.375em;margin-bottom:30px} +.questgroup{margin:0 0 25px 0;padding:150px 0 0 0;border-radius:3px;background:url('http://s14.directupload.net/images/140318/35229m97.png') no-repeat} +.questgroup section{padding:20px;background:#fff} + +.xpbar{ +height:10px; +position:relative; +background:#eee; +border-radius:25px; +margin-bottom:16px +} + +.xpbar span{ +display:block; +height:100%; +border-top-left-radius:20px; +border-bottom-left-radius:20px; +background:#bcd75e; +position:relative; +overflow:hidden +} + +.xpnumeric{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} /** Media Queries **/ From 3e040b777d542ac7d81aede64e510d990648571c Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 18 Mar 2014 18:25:44 +0100 Subject: [PATCH 086/340] questgroup number determines css mood pic class --- views/html/seminaries/seminary.tpl | 4 ++-- www/css/desktop.css | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 30fb5a71..859cbea9 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -5,9 +5,9 @@

              -
                +
                  -
                • +
                • : diff --git a/www/css/desktop.css b/www/css/desktop.css index b47ef8b4..63f44b2d 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -74,8 +74,10 @@ menu a{display:block;padding:15px 0;color:#fff} article{padding-top:5.375em;margin-bottom:30px} -.questgroup{margin:0 0 25px 0;padding:150px 0 0 0;border-radius:3px;background:url('http://s14.directupload.net/images/140318/35229m97.png') no-repeat} -.questgroup section{padding:20px;background:#fff} +.questgroups li{margin:0 0 25px 0;padding:150px 0 0 0;border-radius:3px;background-position:50% 0} +.questgroups section{padding:20px;background:#fff} +.questgroup1{background:url('http://s14.directupload.net/images/140318/35229m97.png') no-repeat} +.questgroup2{background:url('http://s14.directupload.net/images/140318/hwon92ew.png') no-repeat} .xpbar{ height:10px; From 0ae054303114749254df73cc5f0703992e8294a5 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 18 Mar 2014 20:33:15 +0100 Subject: [PATCH 087/340] progress bar & xp value alignment --- views/html/seminaries/seminary.tpl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 859cbea9..d902183e 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -12,10 +12,12 @@

                  :

                  -
                  - +
                  +
                  + +
                  +

                  350 / 450 XP

                  -

                  350 / 450 XP

                  Einleitungstext: Mit völlig verseuchtem Tagewerk machst du dich an die Arbeit und stellst schnell fest...

                  Auf ins Abenteuer! From aede9b918cb2b26501914b79c7256016d2e28490 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 18 Mar 2014 20:33:41 +0100 Subject: [PATCH 088/340] questgroup list design update --- views/html/questgroups/questgroup.tpl | 24 +++++++++++++++++------- www/css/desktop.css | 23 +++++++++++++++++------ 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index d8d2ce42..f50f0fe5 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -12,15 +12,25 @@ 0) : ?>

                  -
                    + diff --git a/www/css/desktop.css b/www/css/desktop.css index 63f44b2d..b7142c6b 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -54,11 +54,13 @@ table{border-collapse:collapse;border-spacing:0} .cb{clear:both} .wrap{margin:0 5%;max-height:999999px;min-height:1px} -a{color:#347894;text-decoration:none} + +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{padding:0 10px 0 0;color:#5e9499} +header .fa{color:#5e9499} header nav{z-index:99} menu{display:none;opacity:0;position:absolute;margin:0;padding:0 0 16px;background:#0f373c;right:0;left:0} @@ -79,25 +81,34 @@ article{padding-top:5.375em;margin-bottom:30px} .questgroup1{background:url('http://s14.directupload.net/images/140318/35229m97.png') no-repeat} .questgroup2{background:url('http://s14.directupload.net/images/140318/hwon92ew.png') no-repeat} +.qg li{margin:0 0 25px 0} +.qgtitle{background:#5cb6bd;border-radius:3px 3px 0 0} +.qgtitle a{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} + + .xpbar{ +width:60%; +float:left; height:10px; position:relative; background:#eee; border-radius:25px; -margin-bottom:16px +margin:8px 0 16px } .xpbar span{ display:block; height:100%; -border-top-left-radius:20px; -border-bottom-left-radius:20px; +border-radius:20px; background:#bcd75e; position:relative; overflow:hidden } -.xpnumeric{color:#869845} +.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} From cf14e794b2710e46b0e8a593804e607e95dc25f6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 18 Mar 2014 21:13:20 +0100 Subject: [PATCH 089/340] quest list design & example code for bonus questline and solved quest --- views/html/questgroups/questgroup.tpl | 16 ++++++++++++-- www/css/desktop.css | 31 +++++++-------------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index f50f0fe5..21e6e1c7 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -39,11 +39,23 @@

                    -
                      +
                        +
                      • + +
                      • +
                      • + +
                      • - +
                        + +
                        0) : ?>
                        : diff --git a/www/css/desktop.css b/www/css/desktop.css index b7142c6b..a8d25a4d 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -82,34 +82,19 @@ article{padding-top:5.375em;margin-bottom:30px} .questgroup2{background:url('http://s14.directupload.net/images/140318/hwon92ew.png') no-repeat} .qg li{margin:0 0 25px 0} -.qgtitle{background:#5cb6bd;border-radius:3px 3px 0 0} -.qgtitle a{display:block;padding:20px;color:#fff;font-weight:700} +.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 -} - +.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} From 1c1d12a1160ac7d4c79ae12b3d475c21ee756db3 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 00:06:17 +0100 Subject: [PATCH 090/340] evolve QuesttypeAgents with fixed Actions and Character submission handling --- agents/intermediate/QuestsAgent.inc | 20 ++ app/QuesttypeAgent.inc | 34 +- app/QuesttypeController.inc | 48 ++- controllers/QuestsController.inc | 335 ++++++++++++++++-- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2719 -> 2705 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 37 +- models/CharactersModel.inc | 29 ++ models/QuestsModel.inc | 40 +++ questtypes/dummy/DummyQuesttypeController.inc | 64 +++- .../dummy/html/{index.tpl => quest.tpl} | 0 questtypes/dummy/html/submission.tpl | 0 questtypes/dummy/html/submissions.tpl | 0 .../MultiplechoiceQuesttypeController.inc | 108 +++--- .../html/{index.tpl => quest.tpl} | 0 questtypes/multiplechoice/html/submission.tpl | 0 .../multiplechoice/html/submissions.tpl | 0 .../TextinputQuesttypeController.inc | 109 +++--- .../textinput/html/{index.tpl => quest.tpl} | 0 questtypes/textinput/html/submission.tpl | 0 questtypes/textinput/html/submissions.tpl | 0 views/html/quests/submission.tpl | 14 + views/html/quests/submissions.tpl | 36 ++ 22 files changed, 729 insertions(+), 145 deletions(-) rename questtypes/dummy/html/{index.tpl => quest.tpl} (100%) create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/dummy/html/submissions.tpl rename questtypes/multiplechoice/html/{index.tpl => quest.tpl} (100%) create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/multiplechoice/html/submissions.tpl rename questtypes/textinput/html/{index.tpl => quest.tpl} (100%) create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 questtypes/textinput/html/submissions.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc index c26a2f70..1a823fa6 100644 --- a/agents/intermediate/QuestsAgent.inc +++ b/agents/intermediate/QuestsAgent.inc @@ -32,6 +32,26 @@ $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/app/QuesttypeAgent.inc b/app/QuesttypeAgent.inc index 9f104be1..9ae988da 100644 --- a/app/QuesttypeAgent.inc +++ b/app/QuesttypeAgent.inc @@ -189,6 +189,34 @@ + /** + * Save the answers of a Character for a Quest. + * + * @param int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + $this->controller->saveAnswersOfCharacter($questId, $characterId, $answers); + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @param array $answers Character answers for the Quest + */ + public function matchAnswersOfCharacter($questId, $characterId, $answers) + { + return $this->controller->matchAnswersOfCharacter($questId, $characterId, $answers); + } + + + + /** * Load the Controller of this Agent. * @@ -216,7 +244,11 @@ } // Determine Action - $action = \nre\configs\CoreConfig::$defaults['action']; + $action = $this->response->getParam(2); + if(is_null($action)) { + $action = $this->request->getParam(2, 'action'); + $this->response->addParam($action); + } // Load Controller diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 743c5d59..2987659a 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -29,6 +29,52 @@ + /** + * Save the answers of a Character for a Quest. + * + * @param int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public abstract function saveAnswersOfCharacter($questId, $characterId, $answers); + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @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($questId, $characterId, $answers); + + + /** + * Action: quest. + * + * Show the task of a Quest. + * + * @param int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public abstract function quest($questId, $characterId); + + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public abstract function submission($questId, $characterId); + + + + /** * Load a QuesttypeController. * @@ -255,7 +301,7 @@ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Set solved - $this->Quests->setQuestSolved($quest['id'], $character['id']); + $this->Quests->setQuestSolved($quest['id'], $character['id']); // Redirect diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 211c8104..f3340578 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -31,7 +31,9 @@ * @var array */ public $permissions = array( - 'quest' => array('admin', 'moderator', 'user') + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator') ); /** * User seminary permissions @@ -39,7 +41,9 @@ * @var array */ public $seminaryPermissions = array( - 'quest' => array('admin', 'moderator', 'user') + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator') ); @@ -158,8 +162,8 @@ // Questtype $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); - // Task - $task = $this->runAndRenderTask($quest['id'], $questtype['classname']); + // Render task + $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); } // Next Quest/Questgroup @@ -203,42 +207,168 @@ } + /** + * 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); + } /** - * Load, construct, run and render the Agent for the given - * classname of a Questtype and return ist output. + * Show and handle the submission of a Character for a Quest. * - * @param int $questId ID of Quest - * @param string $questtypeClassname Classname of Questtype to run and render - * @return string Rendered output of Questtype-Agent + * @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 */ - private function runAndRenderTask($questId, $questtypeClassname) + 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; - $questtypeAgent = null; try { - // Load Agent - \hhu\z\QuesttypeAgent::load($questtypeClassname); - // Construct Agent - $questtypeAgent = \hhu\z\QuesttypeAgent::factory($questtypeClassname, $this->request, $this->response); - - // Generate request + // Generate request and response $request = clone $this->request; - // Generate response - $response = clone $this->response; - $response->clearParams(1); - $response->addParams( - null, - \nre\configs\CoreConfig::$defaults['action'], - $questId - ); - // Run Agent - $questtypeAgent->run($request, $response); - - // Render output - $task = $questtypeAgent->render(); - + $response = $this->createQuesttypeResponse('quest', $quest['id'], $character['id']); + + // 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'); + + // Store answers in session + $_SESSION['answers'][$quest['id']] = $answers; + + // Save answers in database + $questtypeAgent->saveAnswersOfCharacter($quest['id'], $character['id'], $answers); + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($quest['id'], $character['id'], $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(); @@ -270,6 +400,149 @@ 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', $quest['id'], $character['id']); + + // 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/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index d2a2559161443dd7666423b67ee58c7d64c09e6e..0ad327264913d4f176c26dad8e53281cf46435cc 100644 GIT binary patch delta 634 zcmXxg&nrYx6u|NGyfO2}_!%!UHC~Dh3q#3|Bo<29U{-cUNHVi{9y>29YrQ{UQtT|0 zC`y#1EEHK-k)2YC4NKp{Rj)qxoOAEF=f0A=?H%gnV*p;tO<$uT1_H zlf(~LhEEvB7i`8?6Wb)~i7Qa+8DksjeVwTH4aFt=-7rc*FB->^B{F3;ekMd3$=ehw zFpUk^j!ig-RhYv%%wr|4VhwJh4zi83ls(h|_D%lG3I7`0kw{T^Mjjz=s0Th!8-1e= z;PCuvtVdGPin_l8b$`~_XB;;5G0ak*zyaJq9pofr!6W4cwb4Cl<44q&yqNeMb&xN# zF~Kw}C535pO`JuSco4PEq;UrI56z*T3sCP(h9plYe{`~aUU$zxS8r5`d;dZI;dT7GtZCx%*bfSe^965> Ra~v8878Ymo^U-ti;s-P2N>~5@ delta 648 zcmYMwF-RL>6u|L!UUKHz8WZ%aHc+qBLC_%y6)}S%DpV+xmJFp!Ng*~tF)c|DTn@3| zfK`6GP2Qz=1Mguc4q`WsV;erlB$n_NE@C^bpboNuw3K(K1MHgoVJ!ID z;4_Jv6fTh^+W7M5?ZNzp*k!1Qn~6ssTZp?pO?z(TDdY`mgYug|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]; + } + } ?> diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 09ebc125..b93edfb1 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -299,6 +299,46 @@ 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/questtypes/dummy/DummyQuesttypeController.inc b/questtypes/dummy/DummyQuesttypeController.inc index 0f3012fa..8c9a20dd 100644 --- a/questtypes/dummy/DummyQuesttypeController.inc +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -25,22 +25,58 @@ /** - * Action: index. + * Save the answers of a Character for a Quest. + * + * @param int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest */ - public function index() + public function saveAnswersOfCharacter($questId, $characterId, $answers) { - // Check for submission - if($this->request->getRequestMethod() == 'POST') - { - // Right answer (dummy) - if(!is_null($this->request->getPostParam('submit'))) { - $this->setQuestSolved(); - } - // Wrong answer (dummy) - else { - $this->setQuestUnsolved(); - } - } + // Do nothing + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @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($questId, $characterId, $answers) + { + // Set status + return true; + } + + + /** + * Action: quest. + * + * Show the task of a Quest. + * + * @param int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) + { + // Nothing to do + } + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + // Nothing to do } } diff --git a/questtypes/dummy/html/index.tpl b/questtypes/dummy/html/quest.tpl similarity index 100% rename from questtypes/dummy/html/index.tpl rename to questtypes/dummy/html/quest.tpl 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/dummy/html/submissions.tpl b/questtypes/dummy/html/submissions.tpl new file mode 100644 index 00000000..e69de29b diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc index 44c99e3b..77717863 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -24,12 +24,61 @@ /** - * Action: index. + * Save the answers of a Character for a Quest. + * TODO saveAnswersOfCharacter() * - * Display a text with input fields and evaluate if user input - * matches with stored regular expressions. + * @param int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest */ - public function index($questId) + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @return boolean True/false for a right/wrong answer or null for moderator evaluation + */ + public function matchAnswersOfCharacter($questId, $characterId, $answers) + { + // Get right answers + $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($questId); + + // 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 int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) { // Answers if(!array_key_exists('answers', $_SESSION)) { @@ -37,43 +86,6 @@ } $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); - // Check for submission - if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) - { - // Get answers - $answers = $this->request->getPostParam('answers'); - - // Store answers in session - $_SESSION['answers'][$questId] = $answers; - - // Get right answers - $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($questId); - - // 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]); - } - } - - // Set status - if($allSolved && count($answers) == 0) { - $this->setQuestSolved(); - } - else { - $this->setQuestUnsolved(); - } - } - - // Get questions $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); @@ -83,6 +95,20 @@ $this->set('answers', $answers); } + + /** + * Action: submission. + * @TODO submission() + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + } + } ?> diff --git a/questtypes/multiplechoice/html/index.tpl b/questtypes/multiplechoice/html/quest.tpl similarity index 100% rename from questtypes/multiplechoice/html/index.tpl rename to questtypes/multiplechoice/html/quest.tpl diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..e69de29b diff --git a/questtypes/multiplechoice/html/submissions.tpl b/questtypes/multiplechoice/html/submissions.tpl new file mode 100644 index 00000000..e69de29b diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc index 52acdb25..7963ffd7 100644 --- a/questtypes/textinput/TextinputQuesttypeController.inc +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -24,12 +24,64 @@ /** - * Action: index. + * Save the answers of a Character for a Quest. + * TODO saveAnswersOfCharacter() + * + * @param int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @return boolean True/false for a right/wrong answer or null for moderator evaluation + */ + public function matchAnswersOfCharacter($questId, $characterId, $answers) + { + // Get right answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + + // Match regexs with user answers + $allSolved = true; + foreach($regexs as $i => &$regex) + { + if(!array_key_exists($i, $answers)) + { + $allSolved = false; + break; + } + + $score = preg_match($regex['regex'], $answers[$i]); + if($score === 0 || $score === false) + { + $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 int $questId ID of Quest to show + * @param int $characterId ID of Character */ - public function index($questId) + public function quest($questId, $characterId) { // Answers if(!array_key_exists('answers', $_SESSION)) { @@ -37,45 +89,6 @@ } $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); - // Check for submission - if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) - { - // Get answers - $answers = $this->request->getPostParam('answers'); - - // Store answers in session - $_SESSION['answers'][$questId] = $answers; - - // Get right answers - $regexs = $this->Textinput->getTextinputRegexs($questId); - - // Match regexs with user answers - $allSolved = true; - foreach($regexs as $i => &$regex) - { - if(!array_key_exists($i, $answers)) - { - $allSolved = false; - break; - } - - $score = preg_match($regex['regex'], $answers[$i]); - if($score === 0 || $score === false) - { - $allSolved = false; - break; - } - } - - // Set status - if($allSolved) { - $this->setQuestSolved(); - } - else { - $this->setQuestUnsolved(); - } - } - // Get Task $task = $this->Textinput->getTextinputQuest($questId); @@ -89,6 +102,20 @@ $this->set('answers', $answers); } + + /** + * Action: submission. + * @TODO submission() + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + } + } ?> diff --git a/questtypes/textinput/html/index.tpl b/questtypes/textinput/html/quest.tpl similarity index 100% rename from questtypes/textinput/html/index.tpl rename to questtypes/textinput/html/quest.tpl diff --git a/questtypes/textinput/html/submission.tpl b/questtypes/textinput/html/submission.tpl new file mode 100644 index 00000000..e69de29b diff --git a/questtypes/textinput/html/submissions.tpl b/questtypes/textinput/html/submissions.tpl new file mode 100644 index 00000000..e69de29b diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..0f28d7e5 --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,14 @@ +

                        +

                        + + + + + + + + +

                        +
                        + +
                        diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..7867e1d2 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,36 @@ +

                        +

                        + + + + + +

                        + +

                        +

                        + + + + + + +
                        +

                        +
                          + +
                        • + +
                        • + +
                        + +

                        +
                          + +
                        • + +
                        • + +
                        +
                        From a4aefd55c96b2f15ca2b3645fb1de7c519121c79 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 00:09:48 +0100 Subject: [PATCH 091/340] delete orphaned Questtype templates --- questtypes/dummy/html/submissions.tpl | 0 questtypes/multiplechoice/html/submissions.tpl | 0 questtypes/textinput/html/submissions.tpl | 0 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 questtypes/dummy/html/submissions.tpl delete mode 100644 questtypes/multiplechoice/html/submissions.tpl delete mode 100644 questtypes/textinput/html/submissions.tpl diff --git a/questtypes/dummy/html/submissions.tpl b/questtypes/dummy/html/submissions.tpl deleted file mode 100644 index e69de29b..00000000 diff --git a/questtypes/multiplechoice/html/submissions.tpl b/questtypes/multiplechoice/html/submissions.tpl deleted file mode 100644 index e69de29b..00000000 diff --git a/questtypes/textinput/html/submissions.tpl b/questtypes/textinput/html/submissions.tpl deleted file mode 100644 index e69de29b..00000000 From df2b0780f4d61fcf9c9d3f53acd0d91adffe0080 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 00:10:05 +0100 Subject: [PATCH 092/340] implement QuesttypeAgent ?Submit? --- questtypes/submit/SubmitQuesttypeAgent.inc | 24 ++++ .../submit/SubmitQuesttypeController.inc | 117 ++++++++++++++++++ questtypes/submit/SubmitQuesttypeModel.inc | 72 +++++++++++ questtypes/submit/html/quest.tpl | 14 +++ questtypes/submit/html/submission.tpl | 11 ++ 5 files changed, 238 insertions(+) create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl 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..dcbb81e6 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.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; + + + /** + * 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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + // Get already submitted answer + $characterSubmission = $this->Submit->getCharacterSubmission($questId, $characterId); + + // Save answer + if(is_null($characterSubmission) && array_key_exists(0, $answers)) { + $this->Submit->setCharacterSubmission($questId, $characterId, $answers[0]); + } + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @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($questId, $characterId, $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 int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($questId, $characterId); + + // Wordcount + $wordcount = 0; + if(!is_null($characterSubmission)) { + $wordcount = count(preg_split('/\s+/', $characterSubmission['text'])); + } + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('wordcount', $wordcount); + } + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + // Get Character submission + $submission = $this->Submit->getCharacterSubmission($questId, $characterId); + + // Status + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + + + // 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 @@ +
                        + +

                        + + + + + + + +
                        From f989227bb4886fd7942fba09d5a4262cba04ce8f Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 00:27:47 +0100 Subject: [PATCH 093/340] remove duplicated displaying of logged user --- views/html/html.tpl | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 98dc7390..5f06a1ef 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -22,21 +22,17 @@ + + ( XPs, ) + + From 3c8458b98e43a9fb4e3ba799b89eafc665a46b5c Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 00:32:58 +0100 Subject: [PATCH 094/340] show right menu items based on login/-out status of user --- views/html/menu/index.tpl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 62af9696..24e5af4a 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,6 +1,7 @@ -
                      • ">
                      • -
                      • ">
                      • +
                      • +
                      • +
                      • From e10d1d065002ce36aedcb530d45d560ee677241f Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 00:49:19 +0100 Subject: [PATCH 095/340] add correct abstract to seminary page --- app/Utils.inc | 32 ++++++++++++++++++++++++++++ controllers/SeminariesController.inc | 18 +++++++++++++--- models/QuestgroupsModel.inc | 26 ++++++++++++++++++++++ views/html/seminaries/seminary.tpl | 8 ++++--- 4 files changed, 78 insertions(+), 6 deletions(-) diff --git a/app/Utils.inc b/app/Utils.inc index 76ccc7fe..d248dee1 100644 --- a/app/Utils.inc +++ b/app/Utils.inc @@ -46,6 +46,38 @@ 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/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index bf89b9f2..571e636f 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -100,9 +100,21 @@ // Get Questgroups $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); - // Check permission of Questgroups - for($i=1; $iQuestgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + // Get additional data + for($i=0; $iQuestgroups->getFirstQuestgroupText($hierarchy['questgroups'][$i]['id']); + if(!empty($text)) + { + $text = \hhu\z\Utils::shortenString($text['text'], 100, 120).' …'; + $hierarchy['questgroups'][$i]['text'] = $text; + } + + // Check permission of Questgroups + if($i >= 1) { + $hierarchy['questgroups'][$i]['access'] = $this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + } } } diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index 088d54c0..d19795d2 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -145,6 +145,32 @@ } + /** + * 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. * diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index d902183e..eadd7b0b 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,6 +1,6 @@

                        -

                        Beschreibung

                        +

                        @@ -18,7 +18,9 @@

                  350 / 450 XP

                  -

                  Einleitungstext: Mit völlig verseuchtem Tagewerk machst du dich an die Arbeit und stellst schnell fest...

                  + +

                  + Auf ins Abenteuer!

                  @@ -39,4 +41,4 @@

                  format(new \DateTime($seminary['created'])))?> -

                  \ No newline at end of file +

                  From 29cfdee59f559714441a9f5661ddd0028b77b244 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 01:24:45 +0100 Subject: [PATCH 096/340] calculate XPs for Questgroups --- controllers/QuestgroupsController.inc | 17 ++++- controllers/SeminariesController.inc | 3 + models/QuestgroupsModel.inc | 95 ++++++++++++++++++++++++++- views/html/questgroups/questgroup.tpl | 2 +- views/html/seminaries/seminary.tpl | 2 +- 5 files changed, 112 insertions(+), 7 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 42e19d05..7ae4bd72 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -83,15 +83,26 @@ // Get Questgroups $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); - // Check permission of Questgroups - for($i=1; $iQuestgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['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) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 571e636f..867e25e1 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -111,6 +111,9 @@ $hierarchy['questgroups'][$i]['text'] = $text; } + // Get Character XPs + $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->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']); diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index d19795d2..e1fe83a4 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -49,9 +49,11 @@ */ public function getQuestgroupsForHierarchy($hierarchyId, $parentQuestgroupId=null) { + // Get Questgroups + $questgroups = array(); if(is_null($parentQuestgroupId)) { - return $this->db->query( + $questgroups = $this->db->query( 'SELECT id, questgroupshierarchy_id, pos, title, url '. 'FROM questgroups '. 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id IS NULL '. @@ -62,7 +64,7 @@ } else { - return $this->db->query( + $questgroups = $this->db->query( 'SELECT id, questgroupshierarchy_id, pos, title, url '. 'FROM questgroups '. 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id = ? '. @@ -71,6 +73,17 @@ $hierarchyId, $parentQuestgroupId ); } + + // Add additional data + foreach($questgroups as &$questgroup) + { + // Total XPs + $questgroup['xps'] = $this->getAchievableXPsForQuestgroup($questgroup['id']); + } + + + // Return Questgroups + return $questgroups; } @@ -251,6 +264,84 @@ } + /** + * 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; + } + + /** diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 21e6e1c7..a2e1b265 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -22,7 +22,7 @@
                  -

                  50 / 200 XP

                  +

                  / XP

                  Versteckte Questline gefunden:

                  diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index eadd7b0b..6737617c 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -16,7 +16,7 @@
                  -

                  350 / 450 XP

                  +

                  / XP

                  From a914aac3c7eef30c77e2e0872628a140d425377e Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 19 Mar 2014 01:30:12 +0100 Subject: [PATCH 097/340] remove mentioned ?Sidequestline? from Questgroup template --- views/html/questgroups/questgroup.tpl | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index a2e1b265..368eab4e 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -15,22 +15,17 @@ From 7780ac7f2d0843da3de5c8773f60dd9d190c4427 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 12:47:08 +0100 Subject: [PATCH 098/340] implement saving of Quest answers in database --- controllers/QuestsController.inc | 9 +-- .../MultiplechoiceQuesttypeController.inc | 34 +++++++--- .../MultiplechoiceQuesttypeModel.inc | 51 ++++++++++++++- questtypes/multiplechoice/html/quest.tpl | 4 +- questtypes/multiplechoice/html/submission.tpl | 9 +++ .../submit/SubmitQuesttypeController.inc | 4 ++ .../TextinputQuesttypeController.inc | 62 +++++++++++++++---- .../textinput/TextinputQuesttypeModel.inc | 49 ++++++++++++++- questtypes/textinput/html/quest.tpl | 4 +- questtypes/textinput/html/submission.tpl | 7 +++ 10 files changed, 201 insertions(+), 32 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index f3340578..54ffd25e 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -191,6 +191,9 @@ } } + // Has Character solved quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + // Pass data to view $this->set('seminary', $seminary); @@ -204,6 +207,7 @@ $this->set('nextquestgroup', $nextQuestgroup); $this->set('task', $task); $this->set('media', $questmedia); + $this->set('solved', $solved); } @@ -336,14 +340,11 @@ $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); // Solve Quest - if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit')) && !$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { // Get user answers $answers = $this->request->getPostParam('answers'); - // Store answers in session - $_SESSION['answers'][$quest['id']] = $answers; - // Save answers in database $questtypeAgent->saveAnswersOfCharacter($quest['id'], $character['id'], $answers); diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc index 77717863..f17998cc 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -25,7 +25,6 @@ /** * Save the answers of a Character for a Quest. - * TODO saveAnswersOfCharacter() * * @param int $questId ID of Quest to save answers for * @param int $characterId ID of Character to save answers of @@ -33,6 +32,15 @@ */ public function saveAnswersOfCharacter($questId, $characterId, $answers) { + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + + // Save answers + foreach($questions as &$question) + { + $answer = (array_key_exists(intval($question['pos'])-1, $answers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($question['id'], $characterId, $answer); + } } @@ -80,25 +88,24 @@ */ public function quest($questId, $characterId) { - // Answers - if(!array_key_exists('answers', $_SESSION)) { - $_SESSION['answers'] = array(); - } - $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); - // Get questions $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + foreach($questions as &$question) { + $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $characterId); + } + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); // Pass data to view $this->set('questions', $questions); - $this->set('answers', $answers); + $this->set('solved', $solved); } /** * Action: submission. - * @TODO submission() * * Show the submission of a Character for a Quest. * @@ -107,6 +114,15 @@ */ public function submission($questId, $characterId) { + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + foreach($questions as &$question) { + $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $characterId); + } + + + // Pass data to view + $this->set('questions', $questions); } } diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc index cf170390..fef90bcb 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -32,7 +32,7 @@ public function getQuestionsOfQuest($questId) { return $this->db->query( - 'SELECT question, tick '. + 'SELECT id, pos, question, tick '. 'FROM questtypes_multiplechoice '. 'WHERE quest_id = ?', 'i', @@ -51,7 +51,7 @@ public function getTickQuestionsOfQuest($questId) { return $this->db->query( - 'SELECT question, tick, pos '. + 'SELECT id, question, tick, pos '. 'FROM questtypes_multiplechoice '. 'WHERE quest_id = ? AND tick = True', 'i', @@ -59,6 +59,53 @@ ); } + + /** + * 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 index 1e64f3c7..15254ad9 100644 --- a/questtypes/multiplechoice/html/quest.tpl +++ b/questtypes/multiplechoice/html/quest.tpl @@ -2,10 +2,10 @@
                    &$question) : ?>
                  1. - />
                  - + /> diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl index e69de29b..0459b59b 100644 --- a/questtypes/multiplechoice/html/submission.tpl +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,9 @@ +
                    + +
                  • + + × + +
                  • + +
                  diff --git a/questtypes/submit/SubmitQuesttypeController.inc b/questtypes/submit/SubmitQuesttypeController.inc index dcbb81e6..f6a8091a 100644 --- a/questtypes/submit/SubmitQuesttypeController.inc +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -83,10 +83,14 @@ $wordcount = count(preg_split('/\s+/', $characterSubmission['text'])); } + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + // Pass data to view $this->set('submission', $characterSubmission); $this->set('wordcount', $wordcount); + $this->set('solved', $solved); } diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc index 7963ffd7..50f56b14 100644 --- a/questtypes/textinput/TextinputQuesttypeController.inc +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -25,7 +25,6 @@ /** * Save the answers of a Character for a Quest. - * TODO saveAnswersOfCharacter() * * @param int $questId ID of Quest to save answers for * @param int $characterId ID of Character to save answers of @@ -33,6 +32,16 @@ */ public function saveAnswersOfCharacter($questId, $characterId, $answers) { + // Get regexs + $regexs = $this->Textinput->getTextinputRegexs($questId); + + // Save answers + foreach($regexs as &$regex) + { + $pos = intval($regex['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : ''; + $this->Textinput->setCharacterSubmission($regex['id'], $characterId, $answer); + } } @@ -58,8 +67,7 @@ break; } - $score = preg_match($regex['regex'], $answers[$i]); - if($score === 0 || $score === false) + if(!$this->isMatching($regex['regex'], $answers[$i])) { $allSolved = false; break; @@ -83,29 +91,31 @@ */ public function quest($questId, $characterId) { - // Answers - if(!array_key_exists('answers', $_SESSION)) { - $_SESSION['answers'] = array(); - } - $answers = array_key_exists($questId, $_SESSION['answers']) ? $_SESSION['answers'][$questId] : array(); - - // Get Task $task = $this->Textinput->getTextinputQuest($questId); // Process text $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY); + // Get Character answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + foreach($regexs as &$regex) { + $regex['answer'] = $this->Textinput->getCharacterSubmission($regex['id'], $characterId); + } + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + // Pass data to view $this->set('texts', $textParts); - $this->set('answers', $answers); + $this->set('regexs', $regexs); + $this->set('solved', $solved); } /** * Action: submission. - * @TODO submission() * * Show the submission of a Character for a Quest. * @@ -114,6 +124,34 @@ */ public function submission($questId, $characterId) { + // Get Task + $task = $this->Textinput->getTextinputQuest($questId); + + // Process text + $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY); + + // Get Character answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + foreach($regexs as &$regex) { + $regex['answer'] = $this->Textinput->getCharacterSubmission($regex['id'], $characterId); + $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 index d242f251..f8282d14 100644 --- a/questtypes/textinput/TextinputQuesttypeModel.inc +++ b/questtypes/textinput/TextinputQuesttypeModel.inc @@ -53,7 +53,7 @@ public function getTextinputRegexs($questId) { return $this->db->query( - 'SELECT number, regex '. + 'SELECT id, number, regex '. 'FROM questtypes_textinput_regexs '. 'WHERE questtypes_textinput_quest_id = ? '. 'ORDER BY number ASC', @@ -62,6 +62,53 @@ ); } + + /** + * 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 index 40f0d213..c61bd50c 100644 --- a/questtypes/textinput/html/quest.tpl +++ b/questtypes/textinput/html/quest.tpl @@ -1,11 +1,11 @@
                  &$text) : ?> 0) : ?> - + />

                  - + />
                  diff --git a/questtypes/textinput/html/submission.tpl b/questtypes/textinput/html/submission.tpl index e69de29b..dbc453c3 100644 --- a/questtypes/textinput/html/submission.tpl +++ b/questtypes/textinput/html/submission.tpl @@ -0,0 +1,7 @@ + &$text) : ?> + 0) : ?> + + + + + From 207b735df554834878dd2a1fa3ad3318f070d4d6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 21 Mar 2014 14:10:55 +0100 Subject: [PATCH 099/340] issue fix: dynamic images for quest groups --- .hgignore | 2 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 ++++++++++++ agents/bottomlevel/MenuAgent.inc | 35 + .../QuestgroupshierarchypathAgent.inc | 35 + .../bottomlevel/QuestgroupspictureAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 37 ++ agents/intermediate/QuestsAgent.inc | 57 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 67 ++ apis/WebApi.inc | 250 ++++++++ app/Controller.inc | 99 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 263 ++++++++ app/QuesttypeController.inc | 340 ++++++++++ app/QuesttypeModel.inc | 154 +++++ app/QuesttypeView.inc | 76 +++ app/ToplevelAgent.inc | 36 ++ app/Utils.inc | 83 +++ app/controllers/SeminaryRoleController.inc | 136 ++++ app/controllers/ToplevelController.inc | 170 +++++ .../QuesttypeAgentNotFoundException.inc | 77 +++ .../QuesttypeAgentNotValidException.inc | 77 +++ .../QuesttypeControllerNotFoundException.inc | 77 +++ .../QuesttypeControllerNotValidException.inc | 77 +++ .../QuesttypeModelNotFoundException.inc | 77 +++ .../QuesttypeModelNotValidException.inc | 77 +++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 127 ++++ configs/CoreConfig.inc | 167 +++++ controllers/BinaryController.inc | 37 ++ controllers/CharactergroupsController.inc | 143 +++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 101 +++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 35 + controllers/MediaController.inc | 143 +++++ controllers/MenuController.inc | 50 ++ controllers/QuestgroupsController.inc | 135 ++++ .../QuestgroupshierarchypathController.inc | 76 +++ controllers/QuestgroupspictureController.inc | 65 ++ controllers/QuestsController.inc | 549 ++++++++++++++++ controllers/SeminariesController.inc | 229 +++++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 231 +++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ core/Agent.inc | 607 ++++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 424 ++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 2705 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 265 ++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 36 ++ models/CharactergroupsModel.inc | 147 +++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 184 ++++++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 90 +++ models/QuestgroupsModel.inc | 424 ++++++++++++ models/QuestgroupshierarchyModel.inc | 103 +++ models/QuestsModel.inc | 344 ++++++++++ models/QuesttextsModel.inc | 135 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 161 +++++ models/SeminarycharacterfieldsModel.inc | 58 ++ models/UserrolesModel.inc | 77 +++ models/UsersModel.inc | 249 +++++++ models/UserseminaryrolesModel.inc | 78 +++ questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 84 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 130 ++++ .../MultiplechoiceQuesttypeModel.inc | 111 ++++ questtypes/multiplechoice/html/quest.tpl | 11 + questtypes/multiplechoice/html/submission.tpl | 9 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 121 ++++ questtypes/submit/SubmitQuesttypeModel.inc | 72 +++ questtypes/submit/html/quest.tpl | 14 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 159 +++++ .../textinput/TextinputQuesttypeModel.inc | 114 ++++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 ++++++++++++ responses/WebResponse.inc | 250 ++++++++ views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/charactergroups/group.tpl | 33 + views/html/charactergroups/groupsgroup.tpl | 18 + views/html/charactergroups/index.tpl | 9 + views/html/charactergroupsquests/quest.tpl | 46 ++ views/html/characters/character.tpl | 25 + views/html/characters/index.tpl | 8 + views/html/error/index.tpl | 2 + views/html/html.tpl | 40 ++ views/html/introduction/index.tpl | 21 + views/html/menu/index.tpl | 10 + views/html/questgroups/questgroup.tpl | 69 ++ views/html/questgroupshierarchypath/index.tpl | 8 + views/html/questgroupspicture/index.tpl | 3 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 81 +++ views/html/quests/submission.tpl | 14 + views/html/quests/submissions.tpl | 36 ++ views/html/seminaries/create.tpl | 10 + views/html/seminaries/delete.tpl | 8 + views/html/seminaries/edit.tpl | 10 + views/html/seminaries/index.tpl | 14 + views/html/seminaries/seminary.tpl | 45 ++ views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 14 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 14 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 12 + views/html/users/logout.tpl | 0 views/html/users/user.tpl | 19 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 101 +++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ 187 files changed, 15803 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/QuestgroupspictureAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/controllers/ToplevelController.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestgroupspictureController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/questgroupspicture/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..5c118c1e --- /dev/null +++ b/.hgignore @@ -0,0 +1,2 @@ +syntax: glob +media/* 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..9ae988da --- /dev/null +++ b/app/QuesttypeAgent.inc @@ -0,0 +1,263 @@ + + * @copyright 2014 Heinrich-Heine-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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + $this->controller->saveAnswersOfCharacter($questId, $characterId, $answers); + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @param array $answers Character answers for the Quest + */ + public function matchAnswersOfCharacter($questId, $characterId, $answers) + { + return $this->controller->matchAnswersOfCharacter($questId, $characterId, $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..2987659a --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,340 @@ + + * @copyright 2014 Heinrich-Heine-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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public abstract function saveAnswersOfCharacter($questId, $characterId, $answers); + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @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($questId, $characterId, $answers); + + + /** + * Action: quest. + * + * Show the task of a Quest. + * + * @param int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public abstract function quest($questId, $characterId); + + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public abstract function submission($questId, $characterId); + + + + + /** + * 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..5b5a7558 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,127 @@ + + * @copyright 2014 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' + ); + + + /** + * 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), + array('media/(.*)', 'media/index/$1', true) + ); + + + /** + * 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), + array('media/index/(.*)', 'media/$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..581b5b5a --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,101 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + + // 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); + + // 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..aa69e704 --- /dev/null +++ b/controllers/MediaController.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 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) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getMediaByUrl($seminary['id'], $mediaUrl); + + // 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; + } + + + // Pass data to view + $this->set('media', $media); + } + + + + + /** + * Determine file information and set the HTTP-header for + * caching accordingly. + * + * @param string $fileName Filename + * @return boolean HTTP-status 304 was set (in cache) + */ + private function setCacheHeaders($fileName) + { + // Determine last change of file + $fileLastModified = gmdate('r', filemtime($fileName)); + + // Generate E-Tag + $fileEtag = hash('sha256', $fileLastModified.$fileName); + + + // Set header + $this->response->addHeader("Last-Modified: ".$fileLastModified); + $this->response->addHeader("Etag: ".$fileEtag); + // HTTP-status + $headerModifiedSince = $this->request->getServerParam('HTTP_IF_MODIFIED_SINCE'); + $headerNoneMatch = $this->request->getServerParam('HTTP_IF_NONE_MATCH'); + if( + !is_null($headerModifiedSince) && $fileLastModified < strtotime($headerModifiedSince) && + !is_null($headerNoneMatch) && $headerNoneMatch == $fileEtag + ) { + $this->response->setExit(true); + $this->response->addHeader(\nre\core\WebUtils::getHttpHeader(304)); + + return true; + } + + + return false; + } + + } + +?> diff --git a/controllers/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..7ae4bd72 --- /dev/null +++ b/controllers/QuestgroupsController.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\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']); + for($i=0; $i 0) { + $quests[$i]['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']); + } + + // Attach sidequests + $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quests[$i]['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..54ffd25e --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,549 @@ + + * @copyright 2014 Heinrich-Heine-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); + $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); + $questtext['count'] = $questtextCount; + $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']); + } + + // 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); + } + + // Next Quest/Questgroup + $nextQuests = null; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + 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); + } + } + + // Has Character solved quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + + // 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', $quest['id'], $character['id']); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit')) && !$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) + { + // Get user answers + $answers = $this->request->getPostParam('answers'); + + // Save answers in database + $questtypeAgent->saveAnswersOfCharacter($quest['id'], $character['id'], $answers); + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($quest['id'], $character['id'], $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', $quest['id'], $character['id']); + + // 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..867e25e1 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,229 @@ + + * @copyright 2014 Heinrich-Heine-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'); + /** + * 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; $iQuestgroups->getFirstQuestgroupText($hierarchy['questgroups'][$i]['id']); + if(!empty($text)) + { + $text = \hhu\z\Utils::shortenString($text['text'], 100, 120).' …'; + $hierarchy['questgroups'][$i]['text'] = $text; + } + + // Get Character XPs + $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->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']); + } + } + } + + + // 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..a9eb9f91 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,231 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + + // Pass data to view + $this->set('user', $user); + $this->set('characters', $characters); + } + + + /** + * Action: login. + * + * Log in a user. + */ + public function login() + { + $username = ''; + + // Log the user in + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('login'))) + { + $username = $this->request->getPostParam('username'); + $userId = $this->Users->login( + $username, + $this->request->getPostParam('password') + ); + + if(!is_null($userId)) + { + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + // Pass data to view + $this->set('username', $username); + $this->set('failed', ($this->request->getRequestMethod() == 'POST')); + } + + + /** + * Action: logout. + * + * Log out a user. + */ + public function logout() + { + // Unset the currently logged in user + $this->Auth->setUserId(null); + + // Redirect + $this->redirect($this->linker->link(array())); + } + + + /** + * Action: create. + * + * Create a new user. + */ + public function create() + { + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Create new user + $userId = $this->Users->createUser( + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * Action: edit. + * + * Edit a user. + * + * @throws IdNotFoundException + * @param string $userUrl URL-Username of an user + */ + public function edit($userUrl) + { + // User + $user = $this->Users->getUserByUrl($userUrl); + + // Check request method + if($this->request->getRequestMethod() == 'POST') + { + // Save changes + if(!is_null($this->request->getPostParam('save'))) + { + // Edit user + $this->Users->editUser( + $user['id'], + $this->request->getPostParam('username'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + $user = $this->Users->getUserById($user['id']); + } + + + // Redirect to entry + $this->redirect($this->linker->link(array($user['url']), 1)); + } + + + // Pass data to view + $this->set('user', $user); + } + + + /** + * Action: delete. + * + * Delete a user. + * + * @throws IdNotFoundException + * @param string $userUrl URL-Username of an user + */ + public function delete($userUrl) + { + // User + $user = $this->Users->getUserByUrl($userUrl); + + // Check request method + if($this->request->getRequestMethod() == 'POST') + { + // Check confirmation + if(!is_null($this->request->getPostParam('delete'))) + { + // Delete user + $this->Users->deleteUser($user['id']); + + // Redirect to overview + $this->redirect($this->linker->link(null, 1)); + } + + // Redirect to entry + $this->redirect($this->linker->link(array('user', $user['url']), 1)); + } + + + // Show confirmation + $this->set('user', $user); + } + + + } + +?> diff --git a/controllers/components/AchievementComponent.inc b/controllers/components/AchievementComponent.inc new file mode 100644 index 00000000..70601543 --- /dev/null +++ b/controllers/components/AchievementComponent.inc @@ -0,0 +1,41 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to handle achievements. + * + * @author Oliver Hanraths + */ + class AchievementComponent extends \nre\core\Component + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements'); + + + + + /** + * Construct a new Achievements-component. + */ + public function __construct() + { + } + + } + +?> diff --git a/controllers/components/AuthComponent.inc b/controllers/components/AuthComponent.inc new file mode 100644 index 00000000..0db6c69d --- /dev/null +++ b/controllers/components/AuthComponent.inc @@ -0,0 +1,79 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to handle authentication and authorization. + * + * @author Oliver Hanraths + */ + class AuthComponent extends \nre\core\Component + { + /** + * Key to save a user-ID as + * + * @var string + */ + const KEY_USER_ID = 'user_id'; + + + + + /** + * Construct a new Auth-component. + */ + public function __construct() + { + // Start session + if(session_id() === '') { + session_start(); + } + } + + + + + /** + * Set the ID of the user that is currently logged in. + * + * @param int $userId ID of the currently logged in user + */ + public function setUserId($userId) + { + if(is_null($userId)) { + unset($_SESSION[self::KEY_USER_ID]); + } + else { + $_SESSION[self::KEY_USER_ID] = $userId; + } + } + + + /** + * Get the ID of the user that is currently logged in. + * + * @return int ID of the currently logged in user + */ + public function getUserId() + { + if(array_key_exists(self::KEY_USER_ID, $_SESSION)) { + return $_SESSION[self::KEY_USER_ID]; + } + + + return null; + } + + } + +?> diff --git a/core/Agent.inc b/core/Agent.inc new file mode 100644 index 00000000..00ec1a90 --- /dev/null +++ b/core/Agent.inc @@ -0,0 +1,607 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for the implementation af an Agent. + * + * @author coderkun + */ + abstract class Agent + { + /** + * Name of BottomlevelAgent for showing inline errors + * + * @var string + */ + const INLINEERROR_AGENT = 'inlineerror'; + + /** + * Current request + * + * @var Request + */ + private $request; + /** + * Current response + * + * @var Response + */ + private $response; + /** + * Log-system + * + * @var Logger + */ + protected $log; + /** + * SubAgents + * + * @var array + */ + protected $subAgents = array(); + /** + * Controller of this Agent + * + * @var Controller + */ + public $controller = null; + + + + + /** + * Load the class of an Agent. + * + * @static + * @throws AgentNotFoundException + * @throws AgentNotValidException + * @param string $agentName Name of the Agent to load + */ + public static function load($agentName) + { + // Determine full classname + $agentType = self::getAgentType(); + $className = self::getClassName($agentName, $agentType); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + $parentAgentClassName = ClassLoader::concatClassNames($agentType, 'agent'); + $parentAgentClassName = "\\nre\\agents\\$parentAgentClassName"; + ClassLoader::check($className, $parentAgentClassName); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\AgentNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\AgentNotFoundException($e->getClassName()); + } + } + + + + /** + * Instantiate an Agent (Factory Pattern). + * + * @static + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + * @param string $agentName Name of the Agent to instantiate + * @param Request $request Current request + * @param Response $respone Current respone + * @param Logger $log Log-system + */ + public static function factory($agentName, Request $request, Response $response, Logger $log=null) + { + // Determine full classname + $agentType = self::getAgentType(); + $className = self::getClassName($agentName, $agentType); + + + // Construct and return Agent + return new $className($request, $response, $log); + } + + + /** + * Determine the type of an Agent. + * + * @static + * @return string Agent type + */ + private static function getAgentType() + { + return strtolower(ClassLoader::getClassName(get_called_class())); + } + + + /** + * Determine the classname for the given Agent name. + * + * @static + * @param string $agentName Agent name to get classname of + * @param string $agentType Agent type of given Agent name + * @return string Classname for the Agent name + */ + private static function getClassName($agentName, $agentType) + { + $className = ClassLoader::concatClassNames($agentName, 'agent'); + + + return \nre\configs\AppConfig::$app['namespace']."agents\\$agentType\\$className"; + } + + + + + /** + * Construct a new Agent. + * + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + * @param Request $request Current request + * @param Response $respone Current response + * @param Logger $log Log-system + */ + protected function __construct(Request $request, Response $response, Logger $log=null) + { + // Store values + $this->request = $request; + $this->response = $response; + $this->log = $log; + + // Construct SubAgent + $this->actionConstruct(); + + // Load corresponding Controller + $this->loadController(); + } + + + + + /** + * Run the Controller of this Agent and its SubAgents. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @throws DatamodelException + * @throws ActionNotFoundException + * @param Request $request Current request + * @param Response $response Current response + * @return Exception Last occurred exception of SubAgents + */ + public function run(Request $request, Response $response) + { + // Check Controller + if(!is_null($this->controller)) + { + // Call prefilter + $this->controller->preFilter($request, $response); + + // Run controller + $this->controller->run($request, $response); + + // Call postfilter + $this->controller->postFilter($request, $response); + } + + + // Run SubAgents + $exception = null; + foreach($this->subAgents as &$subAgent) + { + try { + $subAgent['object']->run( + $request, + $subAgent['response'] + ); + } + catch(ParamsNotValidException $e) { + $subAgent = $this->errorInline($subAgent, $request, $e); + } + catch(IdNotFoundException $e) { + $subAgent = $this->errorInline($subAgent, $request, $e); + } + catch(DatamodelException $e) { + $exception = $e; + $subAgent = $this->errorInline($subAgent, $request, $e); + } + catch(ActionNotFoundException $e) { + $subAgent = $this->errorInline($subAgent, $request, $e); + } + } + + + // Return last occurred exception + return $exception; + } + + + /** + * Generate output of the Controller of this Agent and its + * SubAgents. + * + * @param array $data View data + * @return string Generated output + */ + public function render($data=array()) + { + // Check Controller + if(!is_null($this->controller)) + { + // Render SubAgents + foreach($this->subAgents as $subAgent) + { + $label = array_key_exists('label', $subAgent) ? $subAgent['label'] : $subAgent['name']; + $data[$label] = $this->renderSubAgent($subAgent); + } + + // Render the Controller of this agent + return $this->controller->render($data); + } + } + + + + + /** + * Construct SubAgents (per Action). + */ + protected function actionConstruct() + { + // Action ermitteln + $action = $this->response->getParam(2); + if(is_null($action)) { + $action = $this->request->getParam(2, 'action'); + $this->response->addParam($action); + } + + // Initialisierungsmethode für diese Action ausführen + if(method_exists($this, $action)) + { + call_user_func_array( + array( + $this, + $action + ), + array( + $this->request, + $this->response + ) + ); + } + } + + + /** + * Load the Controller of this Agent. + * + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + */ + protected function loadController() + { + // Determine Controller name + $controllerName = ClassLoader::getClassName(get_class($this)); + + // Determine ToplevelAgent + $toplevelAgentName = $this->response->getParam(0); + if(is_null($toplevelAgentName)) { + $toplevelAgentName = $this->request->getParam(0, 'toplevel'); + $this->response->addParam($toplevelAgentName); + } + + // Determine Action + $action = $this->response->getParam(2); + if(is_null($action)) { + $action = $this->request->getParam(2, 'action'); + $this->response->addParam($action); + } + + + // Load Controller + Controller::load($controllerName); + + // Construct Controller + $this->controller = Controller::factory($controllerName, $toplevelAgentName, $action, $this); + } + + + /** + * Log an error. + * + * @param Exception $exception Occurred exception + * @param int $logMode Log mode + */ + protected function log($exception, $logMode) + { + if(is_null($this->log)) { + return; + } + + $this->log->log( + $exception->getMessage(), + $logMode + ); + } + + + /** + * Load a SubAgent and add it. + * + * @throws ServiceUnavailableException + * @throws AgentNotFoundException + * @throws AgentNotValidException + * @param string $agentName Name of the Agent to load + * @param mixed … Additional parameters for the agent + */ + protected function addSubAgent($agentName) + { + try { + call_user_func_array( + array( + $this, + '_addSubAgent' + ), + func_get_args() + ); + } + catch(DatamodelException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + } + + + /** + * Load a SubAgent and add it. + * + * @throws ServiceUnavailableException + * @throws DatamodelException + * @throws AgentNotFoundException + * @throws AgentNotValidException + * @param string $agentName Name of the Agent to load + * @param mixed … Additional parameters for the agent + */ + protected function _addSubAgent($agentName) + { + try { + // Load Agent + \nre\agents\BottomlevelAgent::load($agentName); + + // Construct Agent + $this->subAgents[] = call_user_func_array( + array( + $this, + 'newSubAgent' + ), + func_get_args() + ); + } + catch(ViewNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(DriverNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(DriverNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ModelNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ModelNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ControllerNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(ControllerNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(AgentNotValidException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + catch(AgentNotFoundException $e) { + $this->subAgents[] = $this->newInlineError($agentName, $e); + } + } + + + + + /** + * Create a new SubAgent. + * + * @throws DatamodelException + * @param string $agentName Agent name + * @return array SubAgent + */ + private function newSubAgent($agentName) + { + // Response + $response = clone $this->response; + $response->clearParams(1); + $params = func_get_args(); + if(count($params) < 2 || empty($params[1])) { + $params[1] = \nre\configs\CoreConfig::$defaults['action']; + } + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + return array( + 'name' => strtolower($agentName), + 'response' => $response, + 'object' => \nre\agents\BottomlevelAgent::factory( + $agentName, + $this->request, + $response, + $this->log + ) + ); + } + + + /** + * Render a SubAgent. + * + * @param array $subAgent SubAgent to render + * @return string Generated output + */ + private function renderSubAgent(&$subAgent) + { + // Check for InlineError + if(array_key_exists('inlineerror', $subAgent) && !empty($subAgent['inlineerror'])) { + return file_get_contents($subAgent['inlineerror']); + } + + + // Rendern SubAgent and return its output + return $subAgent['object']->render(); + } + + + /** + * Handle the exception of a SubAgent. + * + * @param string $label Name of the original Agent + * @param Excepiton $exception Occurred exception + * @return array InlineError-SubAgent + */ + private function errorInline($subAgent, $request, $exception) + { + // Create the SubAgent for the exception + $subAgent = $this->newInlineError($subAgent['name'], $exception); + + + // Run the InlineError-SubAgent + try { + $subAgent['object']->run( + $request, + $subAgent['response'] + ); + } + catch(ActionNotFoundException $e) { + $this->log($e, Logger::LOGMODE_AUTO); + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + + + // Return the InlineError-SubAgent + return $subAgent; + } + + + /** + * Create a new InlineError. + * + * @param string $label Name of the original Agent + * @param Exception $exception Occurred exception + */ + private function newInlineError($label, $exception) + { + // Log error + $this->log($exception, Logger::LOGMODE_AUTO); + + // Determine Agent name + $agentName = self::INLINEERROR_AGENT; + + // Create SugAgent + $subAgent = array(); + + + try { + // Load Agenten + \nre\agents\BottomlevelAgent::load($agentName); + + // Construct Agent + $subAgent = $this->newSubAgent($agentName); + $subAgent['label'] = $label; + $subAgent['response']->addParam($exception); + } + catch(ViewNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(DatamodelException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(DriverNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(DriverNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ModelNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ModelNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ControllerNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(ControllerNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(AgentNotValidException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + catch(AgentNotFoundException $e) { + $subAgent['inlineerror'] = $this->newInlineErrorService(); + } + + + // Return SubAgent + return $subAgent; + } + + + /** + * Handle a hardcore error that could not be handled by the + * system. + */ + private function newInlineErrorService() + { + // Read and return static error file + return ROOT.DS.\nre\configs\CoreConfig::getClassDir('views').DS.\nre\configs\CoreConfig::$defaults['inlineErrorFile'].\nre\configs\Config::getFileExt('views'); + } + + } + +?> diff --git a/core/Api.inc b/core/Api.inc new file mode 100644 index 00000000..b89b12e7 --- /dev/null +++ b/core/Api.inc @@ -0,0 +1,163 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class to implement an API. + * + * The API is the center of each application and specifies how and what + * to run and render. + * + * @author coderkun + */ + abstract class Api + { + /** + * Die aktuelle Anfrage + * + * @var Request + */ + protected $request; + /** + * Der Toplevelagent + * + * @var ToplevelAgent + */ + private $toplevelAgent = null; + /** + * Die aktuelle Antwort + * + * @var Response + */ + protected $response; + /** + * Log-System + * + * @var Logger + */ + protected $log; + + + + + /** + * Construct a new API. + * + * @param Request $request Current request + * @param Response $respone Current response + */ + public function __construct(Request $request, Response $response) + { + // Store request + $this->request = $request; + + // Store response + $this->response = $response; + + // Init logging + $this->log = new \nre\core\Logger(); + } + + + + + /** + * Run the application. + * + * @throws DatamodelException + * @throws DriverNotValidException + * @throws DriverNotFoundException + * @throws ViewNotFoundException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ControllerNotValidException + * @throws ControllerNotFoundException + * @throws AgentNotValidException + * @throws AgentNotFoundException + * @return Exception Last occurred exception of an subagent + */ + public function run() + { + // Load ToplevelAgent + $this->loadToplevelAgent(); + + // Run ToplevelAgent + return $this->toplevelAgent->run($this->request, $this->response); + } + + + /** + * Render the output. + */ + public function render() + { + // Check exit-status + if($this->response->getExit()) { + return; + } + + // Render ToplevelAgent + $this->response->setOutput($this->toplevelAgent->render()); + } + + + + + /** + * Log an exception + * + * @param Exception $exception Occurred exception + * @param int $logMode Log-mode + */ + protected function log($exception, $logMode) + { + $this->log->log( + $exception->getMessage(), + $logMode + ); + } + + + + + /** + * Load the ToplevelAgent specified by the request. + * + * @throws ServiceUnavailableException + * @throws AgentNotValidException + * @throws AgentNotFoundException + */ + private function loadToplevelAgent() + { + // Determine agent + $agentName = $this->response->getParam(0); + if(is_null($agentName)) { + $agentName = $this->request->getParam(0, 'toplevel'); + $this->response->addParam($agentName); + } + + // Load agent + \nre\agents\ToplevelAgent::load($agentName); + + // Construct agent + $this->toplevelAgent = \nre\agents\ToplevelAgent::factory( + $agentName, + $this->request, + $this->response, + $this->log + ); + } + + } + +?> diff --git a/core/Autoloader.inc b/core/Autoloader.inc new file mode 100644 index 00000000..020b61f7 --- /dev/null +++ b/core/Autoloader.inc @@ -0,0 +1,98 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Autoloader. + * + * This class tries to load not yet used classes. + * + * @author coderkun + */ + class Autoloader + { + /** + * Private construct(). + */ + private function __construct() {} + + /** + * Private clone(). + */ + private function __clone() {} + + + + + /** + * Register load-method. + */ + public static function register() + { + spl_autoload_register( + array( + get_class(), + 'load' + ) + ); + } + + + /** + * Look for the given class and try to load it. + * + * @param string $fullClassName Die zu ladende Klasse + */ + public static function load($fullClassName) + { + $fullClassNameA = explode('\\', $fullClassName); + + if(strpos($fullClassName, \nre\configs\CoreConfig::$core['namespace']) !== 0) + { + // App + $className = array_slice($fullClassNameA, substr_count(\nre\configs\AppConfig::$app['namespace'], '\\')); + array_unshift($className, \nre\configs\CoreConfig::getClassDir('app')); + $filename = ROOT.DS.implode(DS, $className).\nre\configs\CoreConfig::getFileExt('includes'); + if(file_exists($filename)) { + require_once($filename); + } + } + else + { + // Core + $className = array_slice($fullClassNameA, substr_count(\nre\configs\CoreConfig::$core['namespace'], '\\')); + $filename = ROOT.DS.implode(DS, $className).\nre\configs\CoreConfig::getFileExt('includes'); + if(file_exists($filename)) { + require_once($filename); + } + } + + + } + + + /** + * Determine classtype of a class. + * + * @param string $className Name of the class to determine the classtype of + * @return string Classtype of the given class + */ + public static function getClassType($className) + { + // CamelCase + return strtolower(preg_replace('/^.*([A-Z][^A-Z]+)$/', '$1', $className)); + } + + } + +?> diff --git a/core/ClassLoader.inc b/core/ClassLoader.inc new file mode 100644 index 00000000..81cf537a --- /dev/null +++ b/core/ClassLoader.inc @@ -0,0 +1,129 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class for safely loading classes. + * + * @author coderkun + */ + class ClassLoader + { + + + + + /** + * Load a class. + * + * @throws ClassNotFoundException + * @param string $className Name of the class to load + */ + public static function load($fullClassName) + { + // Determine folder to look in + $className = explode('\\', $fullClassName); + $className = array_slice($className, substr_count(\nre\configs\AppConfig::$app['namespace'], '\\')); + + // Determine filename + $fileName = ROOT.DS.implode(DS, $className). \nre\configs\CoreConfig::getFileExt('includes'); + + + // Check file + if(!file_exists($fileName)) + { + throw new \nre\exceptions\ClassNotFoundException( + $fullClassName + ); + } + + // Include file + include_once($fileName); + } + + + /** + * Check inheritance of a class. + * + * @throws ClassNotValidException + * @param string $className Name of the class to check + * @param string $parentClassName Name of the parent class + */ + public static function check($className, $parentClassName) + { + // Check if class is subclass of parent class + if(!is_subclass_of($className, $parentClassName)) { + throw new \nre\exceptions\ClassNotValidException( + $className + ); + } + } + + + /** + * Strip the namespace from a class name. + * + * @param string $class Name of a class including its namespace + * @return Name of the given class without its namespace + */ + public static function stripNamespace($class) + { + return array_slice(explode('\\', $class), -1)[0]; + } + + + /** + * Strip the class type from a class name. + * + * @param string $className Name of a class + * @return Name of the given class without its class type + */ + public static function stripClassType($className) + { + return preg_replace('/^(.*)[A-Z][^A-Z]+$/', '$1', $className); + } + + + /** + * Strip the namespace and the class type of a full class name + * to get only its name. + * + * @param string $class Full name of a class + * @return Only the name of the given class + */ + public static function getClassName($class) + { + return self::stripClassType(self::stripNamespace($class)); + } + + + /** + * Concatenate strings to a class name following the CamelCase + * pattern. + * + * @param string $className1 Arbitrary number of strings to concat + * @return string Class name as CamelCase + */ + public static function concatClassNames($className1) + { + return implode('', array_map( + function($arg) { + return ucfirst(strtolower($arg)); + }, + func_get_args() + )); + } + + } + +?> diff --git a/core/Component.inc b/core/Component.inc new file mode 100644 index 00000000..a93363dc --- /dev/null +++ b/core/Component.inc @@ -0,0 +1,85 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class to implement a (Controller) Component. + * + * @author coderkun + */ + abstract class Component + { + + + + + /** + * Load the class of a Component. + * + * @throws ComponentNotFoundException + * @throws ComponentNotValidException + * @param string $componentName Name of the Component to load + */ + public static function load($componentName) + { + // Determine full classname + $className = self::getClassName($componentName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ComponentNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ComponentNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Component (Factory Pattern). + * + * @param string $componentName Name of the Component to instantiate + */ + public static function factory($componentName) + { + // Determine full classname + $className = self::getClassName($componentName); + + // Construct and return Controller + return new $className(); + } + + + /** + * Determine the classname for the given Component name. + * + * @param string $componentName Component name to get classname of + * @return string Classname for the Component name + */ + private static function getClassName($componentName) + { + $className = \nre\core\ClassLoader::concatClassNames($componentName, \nre\core\ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."controllers\\components\\$className"; + } + + } + +?> diff --git a/core/Config.inc b/core/Config.inc new file mode 100644 index 00000000..b51f1e47 --- /dev/null +++ b/core/Config.inc @@ -0,0 +1,49 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Configuration. + * + * This class does not hold any configuration value but helps to + * determine values that can be hold by AppConfig or CoreConfig. + * + * @author coderkun + */ + final class Config + { + + + + + /** + * Get a default value. + * + * @param string $index Index of value to get + */ + public static function getDefault($index) + { + if(array_key_exists($index, \nre\configs\AppConfig::$defaults)) { + return \nre\configs\AppConfig::$defaults[$index]; + } + if(array_key_exists($index, \nre\configs\CoreConfig::$defaults)) { + return \nre\configs\CoreConfig::$defaults[$index]; + } + + + return null; + } + + } + +?> diff --git a/core/Controller.inc b/core/Controller.inc new file mode 100644 index 00000000..c2814e99 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,424 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Controller. + * + * @author coderkun + */ + abstract class Controller + { + /** + * Corresponding Agent + * + * @var Agent + */ + protected $agent; + /** + * View of the Controller + * + * @var View + */ + protected $view = null; + /** + * Data to pass to the View + * + * @var array + */ + protected $viewData = array(); + /** + * Current request + * + * @var Request + */ + protected $request = null; + /** + * Current response + * + * @var Response + */ + protected $response = null; + + + + + /** + * Load the class of a Controller. + * + * @throws ControllerNotFoundException + * @throws ControllerNotValidException + * @param string $controllerName Name of the Controller to load + */ + public static function load($controllerName) + { + // Determine full classname + $className = self::getClassName($controllerName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ControllerNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ControllerNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Controller (Factory Pattern). + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ViewNotFoundException + * @param string $controllerName Name of the Controller to instantiate + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + public static function factory($controllerName, $layoutName, $action, $agent) + { + // Determine full classname + $className = self::getClassName($controllerName); + + // Construct and return Controller + return new $className($layoutName, $action, $agent); + } + + + /** + * Determine the classname for the given Controller name. + * + * @param string $controllerName Controller name to get classname of + * @return string Classname for the Controller name + */ + private static function getClassName($controllerName) + { + $className = \nre\core\ClassLoader::concatClassNames($controllerName, \nre\core\ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."controllers\\$className"; + } + + + + + /** + * Construct a new Controller. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + * @param Agent $agent Corresponding Agent + */ + protected function __construct($layoutName, $action, $agent) + { + // Store values + $this->agent = $agent; + + // Load Components + $this->loadComponents(); + + // Load Models + $this->loadModels(); + + // Load View + $this->loadView($layoutName, $action); + } + + + + + /** + * Prefilter that is executed before running the Controller. + * + * @param Request $request Current request + * @param Response $response Current response + */ + public function preFilter(Request $request, Response $response) + { + // Request speichern + $this->request = $request; + + // Response speichern + $this->response = $response; + + + // Linker erstellen + $this->set('linker', new \nre\core\Linker($request)); + + } + + + /** + * Prefilter that is executed after running the Controller. + * + * @param Request $request Current request + * @param Response $response Current response + */ + public function postFilter(Request $request, Response $response) + { + } + + + /** + * Run the Controller. + * + * This method executes the Action of the Controller defined by + * the current Request. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @throws DatamodelException + * @throws ActionNotFoundException + * @param Request $request Current request + * @param Response $response Current response + */ + public function run(Request $request, Response $response) + { + // Determine Action + $action = $response->getParam(2, 'action'); + if(!method_exists($this, $action)) { + throw new \nre\exceptions\ActionNotFoundException($action); + } + + // Determine parameters + $params = $response->getParams(3); + if(empty($params)) { + $params = $request->getParams(3); + } + + // Fill missing parameters + $rc = new \ReflectionClass($this); + $nullParamsCount = $rc->getMethod($action)->getNumberOfParameters() - count($params); + $nullParams = ($nullParamsCount > 0 ? array_fill(0, $nullParamsCount, NULL) : array()); + + + // Call Action + call_user_func_array( + array( + $this, + $action + ), + array_merge( + $params, + $nullParams + ) + ); + } + + + /** + * Generate the output. + * + * @param array $viewData Data to pass to the View + * @return string Generated output + */ + public function render($viewData=null) + { + // Combine given data and data of this Controller + $data = $this->viewData; + if(!is_null($viewData)) { + $data = array_merge($viewData, $data); + } + + // Rendern and return output + return $this->view->render($data); + } + + + + + /** + * Set data for the View. + * + * @param string $name Key + * @param mixed $data Value + */ + protected function set($name, $data) + { + $this->viewData[$name] = $data; + } + + + /** + * Redirect to the given URL. + * + * @param string $url Relative URL + */ + protected function redirect($url) + { + $url = 'http://'.$_SERVER['HTTP_HOST'].$url; + header('Location: '.$url); + exit; + } + + + /** + * Check if Models of this Controller are loaded and available. + * + * @param string $modelName Arbitrary number of Models to check + * @return bool All given Models are loaded and available + */ + protected function checkModels($modelName) + { + foreach(func_get_args() as $modelName) + { + if(!isset($this->$modelName) || !is_subclass_of($this->$modelName, 'Model')) { + return false; + } + } + + + return true; + } + + + /** + * Get the View of the Controller + * + * @return View View of the Controller + */ + protected function getView() + { + return $this->view; + } + + + + + /** + * Load the Components of this Controller. + * + * @throws ComponentNotValidException + * @throws ComponentNotFoundException + */ + private function loadComponents() + { + // Determine components + $components = array(); + if(property_exists($this, 'components')) { + $components = $this->components; + } + if(!is_array($components)) { + $components = array($components); + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                  \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..0ad327264913d4f176c26dad8e53281cf46435cc GIT binary patch literal 2705 zcmaKsO^6&t6vs<9nm9@PNK8zOCY9*MeD&_kCWLjCC1f|V>*B0#b|)&iNKNO4kAfWQBv=M5$bNhP!nF7l#3#P=@;4x_^R4f{3UVAjfp>z}Kwj@U_%L|W z%LC}-{FFc*-|IOD@_NTWUT@s@Pk_A64ER3wU>;<>KQM>LC;Nd|(-I1eIf3FYGTD-Y4FR-Kd%aH${L3$IpAp=ZwoCRF3&kR6e_0_)hbFu9tmiJjTLgTNxEP zGIH9aS;zjTL!QmZy1sri4Jk)b)2g(z9BXCP`uj3VMx3F9pwk`qvLCadi90S%wWvk0 z>uKduu2VB6HKKOo!lhXiC&B3`GGwioP9-MguF5*82rJEKXVNGOb942y6>EWinmNF_ z!hP{fzppIhi_9r&S5p&-JjUX0#eA9&{u%!liuht~w$m($q-*!0k~c0~9<($JQb%GF_B|Ry34d5{=j;B+hY8`KQ%#AzAS} z6br|d6`|50B_d3*$k zCs(l%FP^EDW@lz=JqIg8<gw+I`GHQD{i=xAZKGT#9grD#?LjTg3+EHFSkf0 zsOd0`klCsnZN{$jLNHC5OyS2|N_>zjLnnt$V5AU(31z9HT)QfVN^?nOR1!?3X4zJy z?&P8!9~qNHbNrB&`^RPF@K|sDLYkS7_=>`CcwZcb>#kG1hHS+exx=mFF8g0eMtO zA=P5Nc;&0M;g$aN1mI{Uslh(^o5I1EJi*))`ISxvjPO0XN$eJp&QtmtjCZ#nOP kRLlHiba%N$wnN4^|7;lSmGe3nKtAwhi6SKqug7}pAG@W6%m4rY literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..95a49652 --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,265 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-03-18 23:06+0100\n" +"PO-Revision-Date: 2014-03-18 23:09+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:1 +#: ../../../views/html/charactergroups/groupsgroup.tpl:1 +#: ../../../views/html/charactergroups/index.tpl:1 +#: ../../../views/html/charactergroupsquests/quest.tpl:1 +#: ../../../views/html/menu/index.tpl:3 +#: ../../../views/html/questgroups/questgroup.tpl:1 +#: ../../../views/html/quests/quest.tpl:1 +#: ../../../views/html/quests/submission.tpl:1 +#: ../../../views/html/quests/submissions.tpl:1 +#: ../../../views/html/seminaries/create.tpl:1 +#: ../../../views/html/seminaries/delete.tpl:1 +#: ../../../views/html/seminaries/edit.tpl:1 +#: ../../../views/html/seminaries/index.tpl:1 +#: ../../../views/html/seminaries/seminary.tpl:1 +msgid "Seminaries" +msgstr "Kurse" + +#: ../../../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:9 +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:8 +#: ../../../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:31 +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 +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:8 +msgid "User" +msgstr "Benutzer" + +#: ../../../views/html/html.tpl:21 +msgid "as" +msgstr "als" + +#: ../../../views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: ../../../views/html/menu/index.tpl:2 ../../../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:5 ../../../views/html/users/login.tpl:2 +#: ../../../views/html/users/login.tpl:11 +msgid "Login" +msgstr "Login" + +#: ../../../views/html/menu/index.tpl:7 +msgid "Logout" +msgstr "Logout" + +#: ../../../views/html/questgroups/questgroup.tpl:22 +#: ../../../views/html/questgroups/questgroup.tpl:47 +#: ../../../views/html/seminaries/seminary.tpl:26 +msgid "locked" +msgstr "gesperrt" + +#: ../../../views/html/questgroups/questgroup.tpl:39 +msgid "containing optional Quests" +msgstr "Enthaltene optionale Quests" + +#: ../../../views/html/quests/quest.tpl:21 +#: ../../../views/html/quests/submissions.tpl:28 +msgid "solved" +msgstr "gelöst" + +#: ../../../views/html/quests/quest.tpl:23 +#: ../../../views/html/quests/submissions.tpl:19 +msgid "unsolved" +msgstr "ungelöst" + +#: ../../../views/html/quests/quest.tpl:56 +msgid "Go on" +msgstr "Hier geht es weiter" + +#: ../../../views/html/quests/quest.tpl:61 +#: ../../../views/html/quests/quest.tpl:63 +msgid "Quest" +msgstr "Quest" + +#: ../../../views/html/quests/quest.tpl:77 +msgid "Task" +msgstr "Aufgabe" + +#: ../../../views/html/quests/submission.tpl:11 +#, 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:5 +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:4 +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:12 +#, 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 "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..109bee80 --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,184 @@ + + * @copyright 2014 Heinrich-Heine-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]; + } + + } + +?> 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..e1fe83a4 --- /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 '. + '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 '. + '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/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..8c9a20dd --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,84 @@ + + * @copyright 2014 Heinrich-Heine-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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + // Do nothing + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @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($questId, $characterId, $answers) + { + // Set status + return true; + } + + + /** + * Action: quest. + * + * Show the task of a Quest. + * + * @param int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) + { + // Nothing to do + } + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + // 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..f17998cc --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,130 @@ + + * @copyright 2014 Heinrich-Heine-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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + + // Save answers + foreach($questions as &$question) + { + $answer = (array_key_exists(intval($question['pos'])-1, $answers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($question['id'], $characterId, $answer); + } + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @return boolean True/false for a right/wrong answer or null for moderator evaluation + */ + public function matchAnswersOfCharacter($questId, $characterId, $answers) + { + // Get right answers + $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($questId); + + // 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 int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) + { + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + foreach($questions as &$question) { + $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $characterId); + } + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + + + // Pass data to view + $this->set('questions', $questions); + $this->set('solved', $solved); + } + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($questId); + foreach($questions as &$question) { + $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $characterId); + } + + + // 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..15254ad9 --- /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..f6a8091a --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,121 @@ + + * @copyright 2014 Heinrich-Heine-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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + // Get already submitted answer + $characterSubmission = $this->Submit->getCharacterSubmission($questId, $characterId); + + // Save answer + if(is_null($characterSubmission) && array_key_exists(0, $answers)) { + $this->Submit->setCharacterSubmission($questId, $characterId, $answers[0]); + } + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @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($questId, $characterId, $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 int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($questId, $characterId); + + // Wordcount + $wordcount = 0; + if(!is_null($characterSubmission)) { + $wordcount = count(preg_split('/\s+/', $characterSubmission['text'])); + } + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + + + // 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 int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + // Get Character submission + $submission = $this->Submit->getCharacterSubmission($questId, $characterId); + + // Status + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + + + // 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..50f56b14 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 int $questId ID of Quest to save answers for + * @param int $characterId ID of Character to save answers of + * @param array $answers Character answers for the Quest + */ + public function saveAnswersOfCharacter($questId, $characterId, $answers) + { + // Get regexs + $regexs = $this->Textinput->getTextinputRegexs($questId); + + // Save answers + foreach($regexs as &$regex) + { + $pos = intval($regex['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : ''; + $this->Textinput->setCharacterSubmission($regex['id'], $characterId, $answer); + } + } + + + /** + * Check if answers of a Character for a Quest match the correct ones. + * + * @param int $questId ID of Quest to match answers for + * @param int $characterId ID of Character to match answers of + * @return boolean True/false for a right/wrong answer or null for moderator evaluation + */ + public function matchAnswersOfCharacter($questId, $characterId, $answers) + { + // Get right answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + + // 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 int $questId ID of Quest to show + * @param int $characterId ID of Character + */ + public function quest($questId, $characterId) + { + // Get Task + $task = $this->Textinput->getTextinputQuest($questId); + + // Process text + $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY); + + // Get Character answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + foreach($regexs as &$regex) { + $regex['answer'] = $this->Textinput->getCharacterSubmission($regex['id'], $characterId); + } + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($questId, $characterId); + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('regexs', $regexs); + $this->set('solved', $solved); + } + + + /** + * Action: submission. + * + * Show the submission of a Character for a Quest. + * + * @param int $questId ID of Quest to show submission for + * @param int $characterId ID of Character to show submission of + */ + public function submission($questId, $characterId) + { + // Get Task + $task = $this->Textinput->getTextinputQuest($questId); + + // Process text + $textParts = preg_split('/(\$\$)/', $task['text'], -1, PREG_SPLIT_NO_EMPTY); + + // Get Character answers + $regexs = $this->Textinput->getTextinputRegexs($questId); + foreach($regexs as &$regex) { + $regex['answer'] = $this->Textinput->getCharacterSubmission($regex['id'], $characterId); + $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..f8282d14 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 + ); + + + return $data[0]; + } + + + /** + * 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..c61bd50c --- /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/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..d0f3e0cd --- /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..96146f31 --- /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..3108b483 --- /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..584428fb --- /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..cf0e9923 --- /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..b4bb166d --- /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..f16261cb --- /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..0f68bc07 --- /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..5f06a1ef --- /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..90ca59f7 --- /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..24e5af4a --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,10 @@ + +
                • +
                • +
                • + +
                • + +
                • + +
                  diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..368eab4e --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,69 @@ +

                  +

                  + + + + +

                  :

                  + +

                  + + + + 0) : ?> +

                  +
                    + +
                  • + +
                    +
                    +
                    + +
                    +

                    / XP

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

                  + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..9bfe0099 --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,8 @@ + 0) : ?> +Pfad: + + 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..42d218d1 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,81 @@ +

                  +

                  + + + + + +

                  + +

                  +

                  + + + + + + + +
                  + +

                  + +

                  + +

                  +
                  + + + +
                  +

                  +

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

                  + 0) : ?> +
                    + + +
                  • :
                  • + +
                  • :
                  • + + +
                  + + : + + Spiel vorbei + +
                  + + + +
                  +

                  +

                  + +
                  + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..0f28d7e5 --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,14 @@ +

                  +

                  + + + + + + + + +

                  +
                  + +
                  diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..7867e1d2 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,36 @@ +

                  +

                  + + + + + +

                  + +

                  +

                  + + + + + + +
                  +

                  +
                    + +
                  • + +
                  • + +
                  + +

                  +
                    + +
                  • + +
                  • + +
                  +
                  diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..823eec70 --- /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..d2253f14 --- /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..007acf15 --- /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..8ac8c8b0 --- /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..bba6513d --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,45 @@ +

                  +

                  +

                  +

                  + + +

                  + + + + + +

                  + 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..8536804d --- /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..10f280ab --- /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..ef47268c --- /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..5d2883af --- /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..36c13da4 --- /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..506fdf94 --- /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..d86b521d --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,101 @@ +@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} +menu{display:none;opacity:0;position:absolute;margin:0;padding:0 0 16px;background:#0f373c;right:0;left:0} +menu li{display:block;margin:0 5%} +menu a{display:block;padding:15px 0;color:#fff} + +#profile{float:right;padding:18px;color:#fff} + +#toggle,.toggle{display:none} +.toggle{display:inline-block;padding:18px 18px 18px 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-top:5.375em;margin-bottom:30px} + +.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} + +/** Media Queries **/ + 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'; + +?> From de143e57cc3fbc9be65e92d4a180eb5d98759795 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 21 Mar 2014 15:44:11 +0100 Subject: [PATCH 100/340] row display test of seminary questgroups for 480px+ width --- views/html/seminaries/seminary.tpl | 2 +- www/css/desktop.css | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index bba6513d..001803ff 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -5,7 +5,7 @@

                  -
                    +
                    • diff --git a/www/css/desktop.css b/www/css/desktop.css index d86b521d..b1ecf0b0 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -99,3 +99,7 @@ article{padding-top:5.375em;margin-bottom:30px} /** Media Queries **/ +@media only screen and (min-width:480px){ +.questgroups li{width:48%;float:left} +.questgroups li:nth-child(even){float:right} +} From 19126d97f5f27e8f526562436aeb62aa714e77dd Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 15:48:42 +0100 Subject: [PATCH 101/340] set status of Quest in listing and set progress bar width for Questgroups --- controllers/QuestgroupsController.inc | 9 +++++--- views/html/questgroups/questgroup.tpl | 33 ++++++++------------------- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 7ae4bd72..8657bf89 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -108,15 +108,18 @@ if(count($childQuestgroupshierarchy) == 0) { $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']); - for($i=0; $i &$quest) { + // Set status + $quest['solved'] = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + // Check permission if($i > 0) { - $quests[$i]['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']); + $quest['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']); } // Attach sidequests - $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quests[$i]['id']); + $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quest['id']); } } diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 368eab4e..a80ec0ae 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -1,5 +1,4 @@ -

                      -

                      +

                      @@ -19,12 +18,12 @@
                      - +

                      / XP

                      - +
                    • @@ -35,33 +34,21 @@

                        -
                      • - -
                      • -
                      • - -
                      • -
                      • +
                      • -
                        - -
                        + class="solved"> 0) : ?> -
                        - : -
                          +
                            -
                          • +
                          • + +
                          - + From 1a4a71f08a23988cdaac0c9ee5d9ad7cd9482feb Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 15:52:39 +0100 Subject: [PATCH 102/340] implementing image resizing for MediaAgent --- configs/AppConfig.inc | 9 ++- controllers/MediaController.inc | 99 ++++++++++++++++++++++++++++++++- tmp/empty | 0 views/binary/media/index.tpl | 2 +- 4 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 tmp/empty diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 5b5a7558..9077da8f 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -61,7 +61,8 @@ public static $dirs = array( 'locale' => 'locale', 'media' => 'media', - 'questtypes' => 'questtypes' + 'questtypes' => 'questtypes', + 'temporary' => 'tmp' ); @@ -86,8 +87,7 @@ 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), - array('media/(.*)', 'media/index/$1', true) + array('media/(.*)', 'media/$1?layout=binary', false) ); @@ -104,8 +104,7 @@ //array('seminaries/seminary/(.*)', '$1', false) array('characters/index/(.*)', 'characters/$1', true), array('charactergroup/index/(.*)', 'charactergroup/$1', true), - array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), - array('media/index/(.*)', 'media/$1', true) + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true) ); diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index aa69e704..897f2701 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -72,7 +72,7 @@ * @param string $seminaryUrl URL-title of the Seminary * @param string $mediaUrl URL-name of the medium */ - public function index($seminaryUrl, $mediaUrl) + public function index($seminaryUrl, $mediaUrl, $action=null) { // Get Seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); @@ -80,6 +80,10 @@ // 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'].""); @@ -94,9 +98,38 @@ 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); } @@ -138,6 +171,70 @@ 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/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/views/binary/media/index.tpl b/views/binary/media/index.tpl index d0f3e0cd..0d6fb0df 100644 --- a/views/binary/media/index.tpl +++ b/views/binary/media/index.tpl @@ -1 +1 @@ - + From 289b19242c4d5b72431360f7906069d17dec4795 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 15:52:57 +0100 Subject: [PATCH 103/340] ignore temporary files --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 5c118c1e..822d53ff 100644 --- a/.hgignore +++ b/.hgignore @@ -1,2 +1,3 @@ syntax: glob media/* +tmp/* From b95fb7ebe3222aa62cd12d39409822bf6eea570f Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 15:53:42 +0100 Subject: [PATCH 104/340] set width of progress bar for Questgroups on Seminary page --- views/html/seminaries/seminary.tpl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 6737617c..532d9f2d 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,5 +1,4 @@ -

                          -

                          +

                          @@ -14,7 +13,7 @@

                          - +

                          / XP

                          From 81180076d6afcb6d8fc2f7c9a7cb7c06ebc09e06 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 15:55:28 +0100 Subject: [PATCH 105/340] determine Media (image) for Questgroups on Seminary page --- controllers/SeminariesController.inc | 21 +++++++++++++++------ models/QuestgroupsModel.inc | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 867e25e1..86df9068 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'users', 'questgroupshierarchy', 'questgroups'); + public $models = array('seminaries', 'users', 'questgroupshierarchy', 'questgroups', 'media'); /** * User permissions * @@ -101,22 +101,31 @@ $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); // Get additional data - for($i=0; $i &$questgroup) { // Get first Questgroup text - $text = $this->Questgroups->getFirstQuestgroupText($hierarchy['questgroups'][$i]['id']); + $text = $this->Questgroups->getFirstQuestgroupText($questgroup['id']); if(!empty($text)) { $text = \hhu\z\Utils::shortenString($text['text'], 100, 120).' …'; - $hierarchy['questgroups'][$i]['text'] = $text; + $questgroup['text'] = $text; } // Get Character XPs - $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($hierarchy['questgroups'][$i]['id'], $character['id']); + $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); // Check permission of Questgroups if($i >= 1) { - $hierarchy['questgroups'][$i]['access'] = $this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id']); + $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) { } } } diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index e1fe83a4..7f5e466b 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -54,7 +54,7 @@ if(is_null($parentQuestgroupId)) { $questgroups = $this->db->query( - 'SELECT id, questgroupshierarchy_id, pos, title, url '. + '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', @@ -65,7 +65,7 @@ else { $questgroups = $this->db->query( - 'SELECT id, questgroupshierarchy_id, pos, title, url '. + 'SELECT id, questgroupshierarchy_id, pos, title, url, questgroupspicture_id '. 'FROM questgroups '. 'WHERE questgroups.questgroupshierarchy_id = ? AND parent_questgroup_id = ? '. 'ORDER BY questgroups.pos ASC', From c9d0e4a1869cfea87f168222be2a5ce9fa2e7eb5 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 21 Mar 2014 16:00:08 +0100 Subject: [PATCH 106/340] link Questgroup media on Seminary page --- views/html/seminaries/seminary.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 81754fd2..135254a9 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -7,7 +7,9 @@
                          • - + + +

                            : From 9d930c548a9f693fbc640ff76718accd265332c3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 21 Mar 2014 17:23:10 +0100 Subject: [PATCH 107/340] menu bar update for 1024px+ & fixed padding problems --- views/html/html.tpl | 2 +- www/css/desktop.css | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 5f06a1ef..080220da 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -18,7 +18,7 @@

                            -
                            diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 470912e8..11806da4 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,5 +1,6 @@
                          • +
                          • The Legend of Z
                          • Gilden
                          • diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 2a92b76c..7ec11160 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -1,7 +1,11 @@ +
                            + +
                            + +

                            -

                            :

                            diff --git a/www/css/desktop.css b/www/css/desktop.css index 716990ca..a12b7a3c 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -77,6 +77,9 @@ menu a,#profile{display:block;padding:15px 0;color:#fff} article{padding:70px 0 30px} +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + .breadcrumbs li{display:block;font-size:.875em} .breadcrumbs .fa{padding-right:5px;font-size:.75em} @@ -128,11 +131,16 @@ 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} +.wrap{margin:0 0 0 300px} +article{padding:20px 40px 30px 40px} +.moodpic{margin:-20px -40px} .breadcrumbs li{display:inline;padding-right:10px} } -@media only screen and (min-width:1200px){ -.wrap{width:860px;max-width:860px} +@media only screen and (min-width:1366px){ +body{background:#eae8e4} +.wrap{width:800px;max-width:800px} +article{background:#f7f5f2} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:auto} } \ No newline at end of file From ac8953fd5489ad374901571fcce23170c9a2ff17 Mon Sep 17 00:00:00 2001 From: Daniel Date: Sun, 23 Mar 2014 20:23:56 +0100 Subject: [PATCH 130/340] active state for menu links, hover states for links & buttons --- www/css/desktop.css | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/www/css/desktop.css b/www/css/desktop.css index a12b7a3c..b5dd5970 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -56,16 +56,24 @@ table{border-collapse:collapse;border-spacing:0} .wrap{margin:0 5%;max-height:999999px;min-height:1px} a{color:#50a4ab;text-decoration:none} +a:hover{color:#5bbac2} .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%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + 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} + +menu a,#profile{display:block;padding:15px 0;color:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa{color:#bcd75e} #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} @@ -106,6 +114,8 @@ article{padding:70px 0 30px} .cta,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} .orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} .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} From 82312339244482d72b6c80aaf562c65c82ff32b8 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 24 Mar 2014 19:47:30 +0100 Subject: [PATCH 131/340] quest text scrolling system with always visible bars (mobile usability) --- views/html/html.tpl | 9 +++ views/html/quests/quest.tpl | 8 +-- www/css/desktop.css | 5 +- www/js/jquery.nicescroll.min.js | 111 ++++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+), 7 deletions(-) create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/views/html/html.tpl b/views/html/html.tpl index 5ade1521..cbaa5a65 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -8,6 +8,15 @@ + + + + + + + + +
                            + +
                            +
                            + +
                            + + + 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..11806da4 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,15 @@ + +
                          • +
                          • The Legend of Z
                          • +
                          • +
                          • +
                          • Gilden
                          • +
                          • Achievements
                          • +
                          • Karte
                          • +
                          • Bibliothek
                          • + +
                          • + +
                          • + +
                            diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..7ec11160 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,63 @@ +
                            + +
                            + + +

                            + + + +

                            :

                            + +

                            + + + + 0) : ?> +

                            +
                              + +
                            • + + +
                              +
                              +

                              Fortschritt:

                              +
                              + +
                              +

                              / 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..595194f5 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,79 @@ +

                            + + + + + +

                            + +

                            +

                            + + + +
                            + +

                            + +

                            + +

                            +
                            + + + +
                            +

                            +
                            +

                            +
                            + +
                              + +
                            • + + +
                            • + + +
                            • + +
                            + + + 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..bffdff7c --- /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..0f6b867b --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,189 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +#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} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} +} + +@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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} +} + +@media only screen and (min-width:1366px){ +body{background:#eae8e4} +.wrap{width:800px;max-width:800px} +article{background:#f7f5f2} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:auto} +} \ 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'; + +?> diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Thu, 27 Mar 2014 22:05:24 +0100 Subject: [PATCH 135/340] guild profile design + dummy data --- views/html/charactergroups/group.tpl | 89 +++++++++++++++++++++------- views/html/characters/character.tpl | 2 +- www/css/desktop.css | 32 ++++++++++ 3 files changed, 102 insertions(+), 21 deletions(-) diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index a6d65e71..9959c327 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -1,33 +1,82 @@ -

                            + +
                            + +
                            +
                            +
                            + +
                            +
                            +

                            + Schweb wie ein Schmetterling! Stich wie eine Biene! +
                            +
                              +
                            • 6 Mitglieder
                            • +
                            • XP
                            • +
                            • 7. Platz
                            • +
                            -

                            -
                              +

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

                                +

                                XP

                                +
                              • +
                              • + +

                                +

                                XP

                                + 0) : ?> +
                              • +
                              • + +

                                +

                                XP

                                + 0) : ?> +
                              • +
                              • + +

                                +

                                XP

                                + 0) : ?> +
                              • +
                              • + +

                                +

                                XP

                                + 0) : ?> +
                              • +
                              • + +

                                +

                                XP

                                + 0) : ?> +
                            -

                            - - - - - - - - - - -
                            format(new \DateTime($quest['created']))?>/ XPs
                            +

                            +
                              + +
                            • +

                              + format(new \DateTime($quest['created']))?> + + / XP +

                              +
                            • + +
                            diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index 9da8031a..cb7ec385 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -22,7 +22,7 @@

                            Platz

                            -

                            Belohnungen

                            +

                            Belohnungen

                            • Aktive Beteiligung

                              diff --git a/www/css/desktop.css b/www/css/desktop.css index 0f6b867b..cdd52d8e 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -151,6 +151,23 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .ctopics .xpbar span{background:#50a4ab} +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;text-align:center;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0} +.gchars .fa{position:absolute;margin-left:-14px} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + /** Media Queries **/ @media only screen and (min-width:480px){ @@ -161,10 +178,23 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .cinfo{float:left;width:70%} .cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:60%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} } @media only screen and (min-width:768px){ .xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{float:right} } @media only screen and (min-width:1024px){ @@ -178,6 +208,8 @@ menu a{padding:10px 0} article{padding:20px 40px 80px 40px} .moodpic{margin:-20px -40px} .breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} } @media only screen and (min-width:1366px){ From cff124886c9768ca60891f449b88592e58a0b8d1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 27 Mar 2014 22:20:13 +0100 Subject: [PATCH 136/340] improved readability on mobile --- views/html/charactergroups/group.tpl | 6 +++--- www/css/desktop.css | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 9959c327..19cc7b43 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -15,9 +15,9 @@ Schweb wie ein Schmetterling! Stich wie eine Biene!
                                -
                              • 6 Mitglieder
                              • -
                              • XP
                              • -
                              • 7. Platz
                              • +
                              • 6 Mitglieder
                              • +
                              • XP
                              • +
                              • 7. Platz
                            diff --git a/www/css/desktop.css b/www/css/desktop.css index cdd52d8e..8cb26ad1 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -180,7 +180,7 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .cportrait{float:right;width:25%} .gdata .gbanner{float:left;min-width:100px} -.gdata .gdesc{float:left;width:60%} +.gdata .gdesc{float:left;width:75%} .gdata ul{clear:both} .gchars li{width:32%;margin-right:5px} .gchars li:nth-child(even){float:left} @@ -190,6 +190,8 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .xpbar{width:70%} .gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} .gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} .gchars li{width:19%} From dcf5d4a8c142a8c8e774bc584655201f4fad7fc0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 27 Mar 2014 22:27:23 +0100 Subject: [PATCH 137/340] minor data position rearrangement --- views/html/charactergroups/group.tpl | 4 ++-- www/css/desktop.css | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 19cc7b43..9e792865 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -15,9 +15,9 @@ Schweb wie ein Schmetterling! Stich wie eine Biene!
                              -
                            • 6 Mitglieder
                            • -
                            • XP
                            • 7. Platz
                            • +
                            • XP
                            • +
                            • 6 Mitglieder
                  diff --git a/www/css/desktop.css b/www/css/desktop.css index 8cb26ad1..4ebf4fb8 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -156,6 +156,7 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gdata{margin-top:20px} .gdata img{border-radius:3px;margin-bottom:15px} .gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} .gchars li{background:#fff;padding:15px 0;border-radius:3px;text-align:center;float:left;margin-bottom:5px;width:49%} .gchars li:nth-child(even){float:right} From 3369d06dc39fd101a250802b09e591b972ff5ea1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 28 Mar 2014 00:04:07 +0100 Subject: [PATCH 138/340] IE charactergroup leader positioning fix --- views/html/charactergroups/group.tpl | 19 +++++++------------ www/css/desktop.css | 6 +++--- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 9e792865..31b6db4f 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -26,41 +26,36 @@
                  • - + 0) : ?> - +

                    XP

                  • - +

                    XP

                    - 0) : ?>
                  • - +

                    XP

                    - 0) : ?>
                  • - +

                    XP

                    - 0) : ?>
                  • - +

                    XP

                    - 0) : ?>
                  • - +

                    XP

                    - 0) : ?>
                  diff --git a/www/css/desktop.css b/www/css/desktop.css index 4ebf4fb8..dcce96dd 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -158,11 +158,11 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gdata h1{margin:0;padding:0} .gdata .fa{color:#aca8a1} -.gchars li{background:#fff;padding:15px 0;border-radius:3px;text-align:center;float:left;margin-bottom:5px;width:49%} +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} .gchars li:nth-child(even){float:right} .gchars img{width:50px;height:50px;border-radius:25px} -.gchars p{margin:0;padding:0} -.gchars .fa{position:absolute;margin-left:-14px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} .gquests li{padding:12px 15px 0 15px;border-radius:3px} .gquests li:nth-child(odd){background:#fff} From 20906f1c42861778d9e01c9f216af90e60cc3a2c Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Mar 2014 21:28:45 +0100 Subject: [PATCH 139/340] new structure for optional Questgroups instead of single Quests --- controllers/QuestgroupsController.inc | 39 ++-- .../QuestgroupshierarchypathController.inc | 26 ++- controllers/QuestsController.inc | 85 ++++---- controllers/SeminariesController.inc | 4 +- models/QuestgroupsModel.inc | 181 +++++++++++++----- models/QuestgroupshierarchyModel.inc | 27 ++- models/QuestsModel.inc | 163 ++++++---------- models/QuesttextsModel.inc | 36 +++- views/html/questgroups/questgroup.tpl | 14 +- views/html/questgroupshierarchypath/index.tpl | 16 +- views/html/quests/quest.tpl | 11 +- views/html/quests/submissions.tpl | 5 - 12 files changed, 354 insertions(+), 253 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 8657bf89..8a04d0bd 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroupshierarchy', 'questgroups', 'quests'); + public $models = array('seminaries', 'questgroupshierarchy', 'questgroups', 'quests', 'questtexts'); /** * User permissions * @@ -63,7 +63,7 @@ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); // Get Questgrouphierarchy - $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyById($questgroup['questgroupshierarchy_id']); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); // Get Character $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); @@ -77,21 +77,25 @@ } // Get child Questgroupshierarchy - $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupshierarchy['id']); - foreach($childQuestgroupshierarchy as &$hierarchy) + $childQuestgroupshierarchy = null; + if(!empty($questgroup['hierarchy'])) { - // Get Questgroups - $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); - - // Get additional data - for($i=0; $iQuestgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) { - // Get Character XPs - $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($hierarchy['questgroups'][$i]['id'], $character['id']); + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); - // Check permission of Questgroups - if($i >= 1) { - $hierarchy['questgroups'][$i]['access'] = $this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['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']); + } } } } @@ -107,7 +111,7 @@ $quests = null; if(count($childQuestgroupshierarchy) == 0) { - $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']); + $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); foreach($quests as $i => &$quest) { // Set status @@ -118,8 +122,8 @@ $quest['access'] = $this->Quests->hasCharacterSolvedQuest($quests[$i-1]['id'], $character['id']); } - // Attach sidequests - $quests[$i]['sidequests'] = $this->Quests->getSidequestsForQuest($quest['id']); + // Attach related Questgroups + $quest['relatedQuestgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuest($quest['id']); } } @@ -127,7 +131,6 @@ // 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 index e746d198..196aa928 100644 --- a/controllers/QuestgroupshierarchypathController.inc +++ b/controllers/QuestgroupshierarchypathController.inc @@ -25,7 +25,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroups', 'questgroupshierarchy'); + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); @@ -46,22 +46,28 @@ // Get Questgroup $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); // Get parent Questgrouphierarchy - $parentQuestgroupshierarchy = array(); $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); if($showGroup) { - $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($currentQuestgroup['questgroupshierarchy_id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + array_unshift($parentQuestgroupshierarchy, $quest); array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); } - while(!is_null($currentQuestgroup['parent_questgroup_id'])) + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { - // Get Questgroup - $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['parent_questgroup_id']); - - // Get Questgroupshierarchy - $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($currentQuestgroup['questgroupshierarchy_id']); - + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); } diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 3204bb7c..03caa466 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -88,30 +88,41 @@ else { // Previous Quests - if(count($previousQuests) > 0) + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) { - $solved = false; - foreach($previousQuests as &$previousQuest) + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) { - if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) { - $solved = true; - break; + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } } + + break; } - if(!$solved) { - throw new \nre\exceptions\AccessDeniedException(); - } + } + if(!$solved || $tried) { + 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 (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); } // Get Questtext @@ -128,7 +139,7 @@ try { $questtext = $this->Questtexts->getQuesttextByUrl($quest['id'], $questtexttypeUrl, $questtextPos); $questtext['count'] = $questtextCount; - $questtext['sidequests'] = $this->Quests->getSidequestsForQuesttext($questtext['id']); + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); } catch(\nre\exceptions\IdNotFoundException $e) { if(!($questtexttypeUrl == 'Prolog' || $questtexttypeUrl == 'Epilog' && $questtextPos == 1)) { @@ -181,23 +192,24 @@ $nextQuestgroup = null; if($questtexttypeUrl == 'Epilog' || $solved) { - if($quest['is_mainquest']) - { - // Next Quest - $nextQuests = $this->Quests->getNextQuests($quest['id']); + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); - // Next Questgroup - if(empty($nextQuests)) + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) { $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); - $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyById($nextQuestgroup['questgroupshierarchy_id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['questgroup_url'] = $this->Questgroups->getQuestgroupById($nextQuest['questgroup_id'])['url']; + $nextQuests = array($nextQuest); } - } - else { - // Related (Main-) Quest - $nextQuest = $relatedQuesttext['quest']; - $nextQuest['questgroup_url'] = $questgroup['url']; - $nextQuests = array($nextQuest); } } @@ -237,16 +249,6 @@ // 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'])) { @@ -270,7 +272,6 @@ $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); diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 86df9068..695baabc 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -94,14 +94,13 @@ $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); // Questgrouphierarchy and Questgroups - $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyForSeminary($seminary['id']); + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($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 @@ -147,7 +146,6 @@ 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() diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index 7f5e466b..f933c3da 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('questgroupshierarchy', 'quests'); + public $models = array('questgroupshierarchy', 'quests', 'questtexts'); @@ -54,10 +54,11 @@ 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', + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', 'i', $hierarchyId ); @@ -65,10 +66,11 @@ 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', + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', 'ii', $hierarchyId, $parentQuestgroupId ); @@ -97,7 +99,7 @@ public function getQuestgroupById($questgroupId) { $data = $this->db->query( - 'SELECT id, questgroupshierarchy_id, parent_questgroup_id, pos, title, url, questgroupspicture_id '. + 'SELECT id, title, url, questgroupspicture_id '. 'FROM questgroups '. 'WHERE questgroups.id = ?', 'i', @@ -123,10 +125,9 @@ 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 '. + 'SELECT id, title, url, questgroupspicture_id '. 'FROM questgroups '. - 'LEFT JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups.questgroupshierarchy_id '. - 'WHERE questgroupshierarchy.seminary_id = ? AND questgroups.url = ?', + 'WHERE seminary_id = ? AND url = ?', 'is', $seminaryId, $questgroupUrl ); @@ -197,11 +198,14 @@ 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']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); } @@ -223,11 +227,14 @@ 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']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); } @@ -245,22 +252,74 @@ */ 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); + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); + foreach($quests as &$quest) { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) { + return false; + } } - $quests = $this->Quests->getMainquestsForQuestgroup($currentQuestgroup['id']); - $lastQuest = array_pop($quests); + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } - return $this->Quests->hasCharacterSolvedQuest($lastQuest['id'], $characterId); + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; } @@ -278,20 +337,32 @@ // Current Questgroup $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); // Quests of current Questgroup - $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']); - foreach($quests as &$quest) { + $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); + foreach($quests as &$quest) + { $xps += $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as $group) { + $xps += $this->getAchievableXPsForQuestgroup($group['id']); + } } // XPs of child Questgroups - $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['questgroupshierarchy_id']); - foreach($childQuestgroupshierarchy as &$hierarchy) + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) { - $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); - foreach($questgroups as &$questgroup) { - $xps += $this->getAchievableXPsForQuestgroup($questgroup['id']); + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->getAchievableXPsForQuestgroup($questgroup['id']); + } } } @@ -318,16 +389,26 @@ $questgroup = $this->getQuestgroupById($questgroupId); // Quests of current Questgroup - $quests = $this->Quests->getMainquestsForQuestgroup($questgroup['id']); + $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); foreach($quests as &$quest) { if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) { $xps += $quest['xps']; } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as $group) { + $xps += $this->getAchievedXPsForQuestgroup($group['id'], $characterId); + } } // XPs of child Questgroups - $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['questgroupshierarchy_id']); + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['id']); foreach($childQuestgroupshierarchy as &$hierarchy) { $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); @@ -357,7 +438,8 @@ { $data = $this->db->query( 'SELECT * '. - 'FROM questgroups '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. 'WHERE parent_questgroup_id = ? AND pos = ? + 1', 'ii', $parentQuestgroupId, $questgroupPos @@ -367,7 +449,8 @@ { $data = $this->db->query( 'SELECT * '. - 'FROM questgroups '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. 'WHERE parent_questgroup_id IS NULL AND pos = ? + 1', 'i', $questgroupPos @@ -395,7 +478,8 @@ { $data = $this->db->query( 'SELECT * '. - 'FROM questgroups '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. 'WHERE parent_questgroup_id = ? AND pos = ? - 1', 'ii', $parentQuestgroupId, $questgroupPos @@ -405,7 +489,8 @@ { $data = $this->db->query( 'SELECT * '. - 'FROM questgroups '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. 'WHERE parent_questgroup_id IS NULL AND pos = ? - 1', 'i', $questgroupPos diff --git a/models/QuestgroupshierarchyModel.inc b/models/QuestgroupshierarchyModel.inc index df676ea4..c1dae116 100644 --- a/models/QuestgroupshierarchyModel.inc +++ b/models/QuestgroupshierarchyModel.inc @@ -65,7 +65,7 @@ * @param int $seminaryId ID of the seminary to get hierarchy for * @return array Toplevel hierarchy */ - public function getHierarchyForSeminary($seminaryId) + public function getHierarchyOfSeminary($seminaryId) { return $this->db->query( 'SELECT id, seminary_id, parent_questgroupshierarchy_id, pos, title_singular, title_plural, url '. @@ -80,6 +80,31 @@ } + /** + * Get the Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + /** * Get the child hierarchy entries of a Questgroup hierarchy. * diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index b93edfb1..c09a0e03 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -40,16 +40,36 @@ * @param int $questgroupId ID of a Questgroup * @return array Quests of the given Questgroup */ - public function getMainquestsForQuestgroup($questgroupId) + public function getQuestsForQuestgroup($questgroupId) { - return $this->db->query( + $quests = array(); + + // Get first Quest + $quest = $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 = ?', + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', 'i', $questgroupId ); + if(empty($quest)) { + return $quests; + } + $quest = $quest[0]; + array_push($quests, $quest); + + // Get next Quests + // TODO Multiple next Quests + while(($nextQuests = $this->getNextQuests($quest['id'])) != null) + { + $quest = $nextQuests[0]; + array_push($quests, $quest); + } + + + // Return Quests + return $quests; } @@ -65,12 +85,10 @@ 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 '. + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. '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 = ?', + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', 'iis', $seminaryId, $questgroupId, $questUrl ); @@ -78,8 +96,6 @@ throw new \nre\exceptions\IdNotFoundException($questUrl); } - $data[0]['is_mainquest'] = ($data[0]['is_mainquest'] == 1); - return $data[0]; } @@ -95,11 +111,9 @@ 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 '. + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. '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 @@ -108,89 +122,11 @@ 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. * @@ -200,13 +136,11 @@ 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 = ?', + 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_id = ?', 'i', $questId ); @@ -223,12 +157,10 @@ { 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 = ?', + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', 'i', $questId ); @@ -277,6 +209,29 @@ } + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (-1,0)', + 'ii', + $questId, + $characterId + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + /** * Determine if the given Character has solved the given Quest. * diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index 38a1286d..3a84a9a5 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -62,6 +62,25 @@ } + /** + * Get all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId) + { + return $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 = ?', + 'i', + $questId + ); + } + + /** * Get count of Questtexts for a Quest. * @@ -96,24 +115,23 @@ * @param int $sidequestId ID of the Sidequest to get the Questtext for * @param array Questtext data */ - public function getQuesttextForSidequest($sidequestId) + public function getRelatedQuesttextForQuestgroup($questgroupId) { $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 '. + 'FROM questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. - 'WHERE quests.id = ?', + 'WHERE questgroups_questtexts.questgroup_id = ?', 'i', - $sidequestId + $questgroupId ); - if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException($sidequestId); + if(!empty($data)) { + return $data[0]; } - return $data[0]; + return null; } diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 7ec11160..f1c2842b 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -7,11 +7,16 @@ -

                  :

                  + +

                  :

                  + +

                  +

                  + 0) : ?>

                  @@ -37,6 +42,7 @@
                +

                @@ -45,11 +51,11 @@
              • class="solved"> - 0) : ?> + 0) : ?>
                  - +
                • - +
                diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl index c51e0e8f..7fcf0c60 100644 --- a/views/html/questgroupshierarchypath/index.tpl +++ b/views/html/questgroupshierarchypath/index.tpl @@ -1,7 +1,21 @@ 0) : ?> diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 595194f5..79ee88d3 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -3,12 +3,7 @@ -

                - -

                -

                -
                @@ -29,8 +24,8 @@
                  - -
                • + +
                • @@ -71,7 +66,7 @@
                - : + : Spiel vorbei diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl index d5cea955..b53085d6 100644 --- a/views/html/quests/submissions.tpl +++ b/views/html/quests/submissions.tpl @@ -3,12 +3,7 @@ -

                - -

                -

                - From 9f2687e80ca003bc87798346f7ca02974aa9715e Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Mar 2014 22:43:02 +0100 Subject: [PATCH 140/340] add mood pic for seminary --- controllers/SeminariesController.inc | 7 +++++++ models/SeminariesModel.inc | 4 ++-- views/html/seminaries/seminary.tpl | 6 ++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 695baabc..bf935d3d 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -129,10 +129,17 @@ } } + // Medium + $media = null; + if(!is_null($seminary['media_id'])) { + $media = $this->Media->getMediaById($seminary['media_id']); + } + // Pass data to view $this->set('seminary', $seminary); $this->set('questgroupshierarchy', $questgroupshierarchy); + $this->set('media', $media); } diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index 15963271..39b64763 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -60,7 +60,7 @@ public function getSeminaryById($seminaryId) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url, description '. + 'SELECT id, created, created_user_id, title, url, description, media_id '. 'FROM seminaries '. 'WHERE id = ?', 'i', @@ -85,7 +85,7 @@ public function getSeminaryByUrl($seminaryUrl) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url, description '. + 'SELECT id, created, created_user_id, title, url, description, media_id '. 'FROM seminaries '. 'WHERE url = ?', 's', diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index bffdff7c..04322475 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,3 +1,9 @@ + +
                + +
                + +

                From 252352ee4da2ba71191263576b241e9c65b976b1 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Mar 2014 22:43:15 +0100 Subject: [PATCH 141/340] remove leaders from Character groups --- models/CharactersModel.inc | 2 +- views/html/charactergroups/group.tpl | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index a0f27fbe..8dc1e2f9 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -83,7 +83,7 @@ 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 '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, 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 '. diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 31b6db4f..9f5bd147 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -26,8 +26,6 @@
                • - - 0) : ?>

                  XP

                  From efc60f51d968eadfb726b2c2c8ad2d72ef04d495 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 28 Mar 2014 23:13:09 +0100 Subject: [PATCH 142/340] set moodpics for Questgroups --- .../bottomlevel/QuestgroupspictureAgent.inc | 35 ---------- agents/intermediate/QuestgroupsAgent.inc | 1 - agents/intermediate/QuestsAgent.inc | 3 - controllers/QuestgroupsController.inc | 10 ++- controllers/QuestgroupspictureController.inc | 65 ------------------- controllers/QuestsController.inc | 5 ++ views/html/questgroups/questgroup.tpl | 6 +- views/html/questgroupspicture/index.tpl | 3 - views/html/quests/quest.tpl | 6 +- 9 files changed, 22 insertions(+), 112 deletions(-) delete mode 100644 agents/bottomlevel/QuestgroupspictureAgent.inc delete mode 100644 controllers/QuestgroupspictureController.inc delete mode 100644 views/html/questgroupspicture/index.tpl diff --git a/agents/bottomlevel/QuestgroupspictureAgent.inc b/agents/bottomlevel/QuestgroupspictureAgent.inc deleted file mode 100644 index 8cdca7cd..00000000 --- a/agents/bottomlevel/QuestgroupspictureAgent.inc +++ /dev/null @@ -1,35 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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/intermediate/QuestgroupsAgent.inc b/agents/intermediate/QuestgroupsAgent.inc index 860fd177..52ecd4e1 100644 --- a/agents/intermediate/QuestgroupsAgent.inc +++ b/agents/intermediate/QuestgroupsAgent.inc @@ -29,7 +29,6 @@ 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 index 1a823fa6..273a47ab 100644 --- a/agents/intermediate/QuestsAgent.inc +++ b/agents/intermediate/QuestsAgent.inc @@ -29,7 +29,6 @@ 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); } @@ -39,7 +38,6 @@ 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); } @@ -49,7 +47,6 @@ 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/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 8a04d0bd..b89c98a6 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'questgroupshierarchy', 'questgroups', 'quests', 'questtexts'); + public $models = array('seminaries', 'questgroupshierarchy', 'questgroups', 'quests', 'questtexts', 'media'); /** * User permissions * @@ -105,6 +105,13 @@ // Get Character XPs $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } // Get Quests @@ -133,6 +140,7 @@ $this->set('questgroup', $questgroup); $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); $this->set('quests', $quests); } diff --git a/controllers/QuestgroupspictureController.inc b/controllers/QuestgroupspictureController.inc deleted file mode 100644 index 7810869b..00000000 --- a/controllers/QuestgroupspictureController.inc +++ /dev/null @@ -1,65 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 index 03caa466..f0829c54 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -68,6 +68,11 @@ // Get Questgroup $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } + // Get Quest $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index f1c2842b..f4c9b002 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -1,8 +1,8 @@ +
                  - +
                  - - +

                  diff --git a/views/html/questgroupspicture/index.tpl b/views/html/questgroupspicture/index.tpl deleted file mode 100644 index d8c7b32c..00000000 --- a/views/html/questgroupspicture/index.tpl +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 79ee88d3..6045f06a 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -1,7 +1,11 @@ + +
                  + +
                  +

                  -

                  From 283d24ff78d71b3bcb44550628366471fad07e07 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 04:07:23 +0200 Subject: [PATCH 143/340] show only unlocked Questsgroups and Quests and summarize XPs recursive regarding choosed path by character --- controllers/QuestgroupsController.inc | 58 ++++-- controllers/QuestsController.inc | 5 +- controllers/SeminariesController.inc | 15 +- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2782 -> 2653 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 78 ++++---- models/QuestgroupsModel.inc | 134 +++++++++++--- models/QuestsModel.inc | 186 +++++++++++++------- views/html/questgroups/questgroup.tpl | 9 - views/html/quests/quest.tpl | 4 +- views/html/seminaries/seminary.tpl | 4 - 10 files changed, 317 insertions(+), 176 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index b89c98a6..8abef8aa 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -87,15 +87,20 @@ $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); // Get additional data - for($i=0; $i &$group) { - // Get Character XPs - $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->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']); + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); } } } @@ -115,23 +120,40 @@ // Get Quests - $quests = null; + $quests = array(); if(count($childQuestgroupshierarchy) == 0) { - $quests = $this->Quests->getQuestsForQuestgroup($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']); + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } } - // Attach related Questgroups - $quest['relatedQuestgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuest($quest['id']); + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + + // Add Quest to Quests + $quests[] = $currentQuest; + } } + while(!is_null($currentQuest) && $currentQuest['solved']); } diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index f0829c54..9750a83c 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -124,6 +124,9 @@ } } + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + // Get (related) Questtext $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); if(!empty($relatedQuesttext)) { @@ -138,7 +141,7 @@ $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)) + if(in_array($questtexttypeUrl, $questtexttypes)) { $questtextPos = max(intval($questtextPos), 1); try { diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index bf935d3d..61b83a67 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -103,6 +103,16 @@ // Get additional data foreach($hierarchy['questgroups'] as $i => &$questgroup) { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + // Get first Questgroup text $text = $this->Questgroups->getFirstQuestgroupText($questgroup['id']); if(!empty($text)) @@ -114,11 +124,6 @@ // 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 { diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 1192a6534877b0811d705ef9721053ec44e8748f..5238440677d2c008eb3068fc0dc96da26faaaf35 100644 GIT binary patch delta 1085 zcmX}rPe>GD7{~Evw`tnlG&gfiOKbbLWo2$8Q42*O5#mY0?n^AxN!jw?X$d-cQ5fu4 zMu+UsAK0){K@fPeF4d{c2#iu8s!Jr)_qRUvGS7VGoq6YdpZ9%dYMJ*fm0EK!Wt48B zgSZhhJB7<}PLzdpW@-G0Tk$)lFcp|>!3;KGmwOU7Qtx+%u#tKZlQ@j)aSZ9VVimtJ zi48os>GiwreGIw(2sQ95s@)4r;Jo_|)$szV{i4@b+#jg-e_;;>G|n@=^>U$^lu$Dr zL)zFlw&4VF*bFBP_|WT5Q8Rjm8gR~?NA>r{>t9g)ena&eqp{kDoDz(0ja=Mj*;`Q! zXE&@Z^&BeW57>fVQ3?G)EomFsY{ndFMNXi$;v6c0A=E%uQ2mbh{p+YLn!*Z6*&QzA zJ=6;iP#rx%CGZkA;Va~@8Yi{?jB3B){&au)^Ek_Ml;;U7U;*246qV3KlJ#dYHqDLw zID_i=HEKq0k-qE`DxqcMuvIrCyA1U-s$CAXB0Z>fXHoqRc>Myh(DjcUD6GFS`ByZk zie6SiDw?_WO$p=(6>WoxI&USm6U~H*640M=kkB@5Ba~bhv6t9Q>>^a!wg1`+{wmu^ z>>)HGl}>_$>w9?swKDn+wB$Po|3kSl%hu0pzwlwd-sc{YeV2>n;-zb)a`t?Acy#2d z--ylxx2j7)M=WXxPe+4cDRC^H&G#1a$D`+A_J5_i7^dUZAlV;_E~W;f$EhQK0Z-mu A`v3p{ delta 1169 zcmXxjNobQn7{>8QvzVmSrnOC6T1|~hs!^yonc!|0jJj|me;<|6 zJ5ku}{9T!V|q$NjT@KeKD$Fsi=|m0<$;xE?c&nm1$Z0rMEfSl^wdqJ^&5gHhDP z*2G`z!B5l# ze^7TAART2EGvla%U8o6DsEzE#Rd@gy>Q17@okhKW(Ht?a+4IQ==bz<;89MSfhih>W zwXr2!fvv2v4HKw|526-2g355n+80n8Dk1E}6^bvwJJU rp=6`6uX-mGYYGpRri(>ye60E)e9Bk(7C9dt^==fsQfaEvAIgetQuestgroupById($questgroupId); // Chack all Quests - $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); - foreach($quests as &$quest) { - if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { return false; } + + // Get next Quests + while(!is_null($currentQuest) && !empty($nextQuests = $this->Quests->getNextQuests($currentQuest['id']))) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } } // Check all child Questgroups @@ -328,7 +351,7 @@ * sub-Questgroups. * * @param int $questgroupId ID of Questgroup - * @return Sum of XPs + * @return int Sum of XPs */ public function getAchievableXPsForQuestgroup($questgroupId) { @@ -340,16 +363,9 @@ $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); // Quests of current Questgroup - $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); - foreach($quests as &$quest) - { - $xps += $quest['xps']; - - // Related Questgroups - $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); - foreach($relatedQuestgroups as $group) { - $xps += $this->getAchievableXPsForQuestgroup($group['id']); - } + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); } // XPs of child Questgroups @@ -372,13 +388,45 @@ } + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 + * @return int Sum of XPs */ public function getAchievedXPsForQuestgroup($questgroupId, $characterId) { @@ -389,18 +437,9 @@ $questgroup = $this->getQuestgroupById($questgroupId); // Quests of current Questgroup - $quests = $this->Quests->getQuestsForQuestgroup($questgroup['id']); - foreach($quests as &$quest) - { - if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) { - $xps += $quest['xps']; - } - - // Related Questgroups - $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); - foreach($relatedQuestgroups as $group) { - $xps += $this->getAchievedXPsForQuestgroup($group['id'], $characterId); - } + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); } // XPs of child Questgroups @@ -423,6 +462,47 @@ } + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['id'], $characterId); + } + + + // Return summarized XPs + return $xps; + } + + /** diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index c09a0e03..7b8ed968 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -19,6 +19,24 @@ */ class QuestsModel extends \hhu\z\Model { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 1; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 0; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = -1; @@ -34,45 +52,6 @@ - /** - * Get all Quests for the given Questgroup. - * - * @param int $questgroupId ID of a Questgroup - * @return array Quests of the given Questgroup - */ - public function getQuestsForQuestgroup($questgroupId) - { - $quests = array(); - - // Get first Quest - $quest = $this->db->query( - 'SELECT id, questtype_id, title, url, xps, task '. - 'FROM quests '. - 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. - 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', - 'i', - $questgroupId - ); - if(empty($quest)) { - return $quests; - } - $quest = $quest[0]; - array_push($quests, $quest); - - // Get next Quests - // TODO Multiple next Quests - while(($nextQuests = $this->getNextQuests($quest['id'])) != null) - { - $quest = $nextQuests[0]; - array_push($quests, $quest); - } - - - // Return Quests - return $quests; - } - - /** * Get a Quest and its data by its URL. * @@ -127,6 +106,31 @@ } + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + /** * Get Quests that follow-up a Quest. * @@ -167,6 +171,18 @@ } + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_ENTERED); + } + + /** * Mark a Quest as solved for a Character. * @@ -175,16 +191,7 @@ */ public function setQuestSolved($questId, $characterId) { - $this->db->query( - 'INSERT INTO quests_characters '. - '(quest_id, character_id, status) '. - 'VALUES '. - '(?, ?, ?)', - 'iii', - $questId, - $characterId, - 0 - ); + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SOLVED); } @@ -196,16 +203,31 @@ */ public function setQuestUnsolved($questId, $characterId) { - $this->db->query( - 'INSERT INTO quests_characters '. - '(quest_id, character_id, status) '. - 'VALUES '. - '(?, ?, ?)', - 'iii', + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_UNSOLVED); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', $questId, $characterId, - -1 + static::QUEST_STATUS_ENTERED, static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED ); + + + return (!empty($count) && intval($count[0]['c']) > 0); } @@ -221,10 +243,11 @@ $count = $this->db->query( 'SELECT count(id) AS c '. 'FROM quests_characters '. - 'WHERE quest_id = ? AND character_id = ? AND status IN (-1,0)', - 'ii', + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', $questId, - $characterId + $characterId, + static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED ); @@ -244,10 +267,11 @@ $count = $this->db->query( 'SELECT count(id) AS c '. 'FROM quests_characters '. - 'WHERE quest_id = ? AND character_id = ? AND status = 0', - 'ii', + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', $questId, - $characterId + $characterId, + static::QUEST_STATUS_SOLVED ); @@ -267,10 +291,11 @@ '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'. + 'SELECT character_id FROM quests_characters WHERE quest_id = questtypes_submit_characters.quest_id AND status = ?'. ')', - 'i', - $questId + 'ii', + $questId, + static::QUEST_STATUS_SOLVED ); } @@ -287,10 +312,35 @@ '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'. + 'SELECT character_id FROM quests_characters WHERE quest_id = questtypes_submit_characters.quest_id AND status = ?'. ')', - 'i', - $questId + 'ii', + $questId, + static::QUEST_STATUS_SOLVED + ); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + */ + private function setQuestStatus($questId, $characterId, $status) + { + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status ); } diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index f4c9b002..ff4af3ab 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -23,7 +23,6 @@
                  • -
                    @@ -33,10 +32,6 @@

                    / XP

                    - - -
                    -
                  @@ -49,7 +44,6 @@ diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 6045f06a..9f736e0b 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -32,7 +32,7 @@
                • -
                • +
                • @@ -70,7 +70,7 @@
                - : + : Spiel vorbei diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 04322475..c3a77cae 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -18,7 +18,6 @@

                : -

                @@ -30,9 +29,6 @@

                Auf ins Abenteuer! - -

                -
              • From fd313d34e1c8b794a3545b0232e1cb0a0427a16a Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 04:07:57 +0200 Subject: [PATCH 144/340] correct links to related Questgroups for abort texts of Quests --- controllers/QuestsController.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 9750a83c..9f13848c 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -131,6 +131,9 @@ $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); if(!empty($relatedQuesttext)) { $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } } // Get Questtext @@ -215,7 +218,6 @@ { // Related (Main-) Quest $nextQuest = $relatedQuesttext['quest']; - $nextQuest['questgroup_url'] = $this->Questgroups->getQuestgroupById($nextQuest['questgroup_id'])['url']; $nextQuests = array($nextQuest); } } From ec44c48de6dc0b8315ec648838d79f855b308392 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 04:13:56 +0200 Subject: [PATCH 145/340] do not link Quests that have not been taken on decisions --- controllers/QuestsController.inc | 10 ++++++++++ views/html/quests/quest.tpl | 18 ++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 9f13848c..51d42cc8 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -200,11 +200,20 @@ // Next Quest/Questgroup $nextQuests = null; + $charactedHasChoosenNextQuest = false; $nextQuestgroup = null; if($questtexttypeUrl == 'Epilog' || $solved) { // Next Quest $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } // Next Questgroup if(empty($nextQuests)) @@ -233,6 +242,7 @@ $this->set('queststatustext', $questStatusText); $this->set('relatedquesttext', $relatedQuesttext); $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); $this->set('nextquestgroup', $nextQuestgroup); $this->set('task', $task); $this->set('media', $questmedia); diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 9f736e0b..3d440d50 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -63,9 +63,23 @@
                  -
                • :
                • +
                • + : + + + + + +
                • -
                • :
                • +
                • + : + + + + + +
                From 7904d1176dee20b3a71c3d43bfbe46b78e3b1347 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 04:35:18 +0200 Subject: [PATCH 146/340] do not link Questgroup titles in path --- views/html/questgroupshierarchypath/index.tpl | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl index 7fcf0c60..59b98429 100644 --- a/views/html/questgroupshierarchypath/index.tpl +++ b/views/html/questgroupshierarchypath/index.tpl @@ -4,16 +4,12 @@
              • - - - : - - - + + : + + - - - +
              • From c4b27f1ec43b831e8a8093bc8459a61ade15a017 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 04:36:15 +0200 Subject: [PATCH 147/340] do not save Quest status ?entered? repetitive --- models/QuestsModel.inc | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 7b8ed968..0ea48fc1 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -179,7 +179,7 @@ */ public function setQuestEntered($questId, $characterId) { - $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_ENTERED); + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_ENTERED, false); } @@ -329,9 +329,28 @@ * @param int $questId ID of Quest to mark * @param int $characterId ID of Character to mark the Quest for * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character */ - private function setQuestStatus($questId, $characterId, $status) + private function setQuestStatus($questId, $characterId, $status, $repeated=true) { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status $this->db->query( 'INSERT INTO quests_characters '. '(quest_id, character_id, status) '. From 42437ddd46f483ad1ab3744dc28b092217dd71c8 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 14:21:45 +0200 Subject: [PATCH 148/340] update menu (split system and Seminary related elements) --- agents/bottomlevel/SeminarymenuAgent.inc | 35 ++++++++++++++++ agents/toplevel/HtmlAgent.inc | 3 +- app/controllers/ToplevelController.inc | 26 +++++------- configs/AppConfig.inc | 2 +- controllers/SeminarymenuController.inc | 52 ++++++++++++++++++++++++ views/html/html.tpl | 3 ++ views/html/menu/index.tpl | 10 ++--- views/html/seminarymenu/index.tpl | 7 ++++ 8 files changed, 114 insertions(+), 24 deletions(-) create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 views/html/seminarymenu/index.tpl diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * 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 index 63ed4525..0648160e 100644 --- a/agents/toplevel/HtmlAgent.inc +++ b/agents/toplevel/HtmlAgent.inc @@ -37,8 +37,9 @@ */ public function index(\nre\core\Request $request, \nre\core\Response $response) { - // Add menu + // Add menus $this->addSubAgent('Menu'); + $this->addSubAgent('Seminarymenu'); } diff --git a/app/controllers/ToplevelController.inc b/app/controllers/ToplevelController.inc index 30558c91..5e640dca 100644 --- a/app/controllers/ToplevelController.inc +++ b/app/controllers/ToplevelController.inc @@ -78,6 +78,13 @@ try { static::$user = $this->Users->getUserById($this->Auth->getUserId()); + // Determine user roles + static::$user['roles'] = array(); + $roles = $this->Userroles->getUserrolesForUserById(static::$user['id']); + foreach($roles as &$role) { + static::$user['roles'][] = $role['name']; + } + // Character $controller = $this->agent->getIntermediateAgent()->controller; if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) @@ -122,7 +129,10 @@ private function checkPermission(\nre\core\Request $request, \nre\core\Response $response) { // Determine user - $userId = $this->Auth->getUserId(); + $userRoles = array('guest'); + if(!is_null(static::$user)) { + $userRoles = static::$user['roles']; + } // Do not check error pages @@ -133,20 +143,6 @@ 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'); diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 9077da8f..817b0114 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -103,7 +103,7 @@ array('seminaries/seminary/(.*)', 'seminaries/$1', false), //array('seminaries/seminary/(.*)', '$1', false) array('characters/index/(.*)', 'characters/$1', true), - array('charactergroup/index/(.*)', 'charactergroup/$1', true), + array('charactergroups/index/(.*)', 'charactergroups/$1', true), array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true) ); diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..a2289777 --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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); + $this->set('loggedSeminary', HtmlController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/views/html/html.tpl b/views/html/html.tpl index cbaa5a65..e36da721 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -38,6 +38,9 @@ + + +
                diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 11806da4..393e4747 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,12 +1,8 @@ -
              • -
              • The Legend of Z
              • -
              • +
              • The Legend of Z
              • +
              • +
              • -
              • Gilden
              • -
              • Achievements
              • -
              • Karte
              • -
              • Bibliothek
              • diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..fa6d4457 --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,7 @@ + +
              • +
              • +
              • +
              • +
              • +
                From 4c792258efc62e2002f218210d7f7075aade6140 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 14:21:52 +0200 Subject: [PATCH 149/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2653 -> 2801 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 34 ++++++++++++++------ views/html/seminaries/seminary.tpl | 4 --- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 5238440677d2c008eb3068fc0dc96da26faaaf35..658b0ec007b9918a2cd8c72d6397e854cf392ae5 100644 GIT binary patch delta 1185 zcmX}rPi)I!9LMpe)<0`EQ|mSi-O#b==$f$9k|tP$I1wU?M5Jx1?M7Gq!(3Ju;viya zTtw_5BEmr$4h}ou`A}B*Yo^-&+qy3eSZDW^|QPBvD10S zP)3MN#65>GI{H}S!d__(8-L`+DhxR|)cJr2-b~|bT0aX7Wwqn@22l<&e7xf>v?W5LX=wp6! zib{kI2~-y5P&;~sTG)GJZnKQ*@dxrVE=JV?+E5espzimfb~u3iOvE~h8aHm+$I;LH zW`c?)$k_*Z)PNP7=Qw6j3kh;lMo>qvAGLr9?7|dkfitL7&*55}M`h?4>WCIl3t7Y} zW1BBjH1Ie3;4kXzI@pGG)Qg&^-?qc3_oAo)WA^zG?4W%D8Nwt{?@yunU$o9xt6uW2 z4%fL6#hW;W3%CLQpcWQjc6|dO+=0WW_fDb~a2mD4Nz}xb?ft9B5azb^E_Ty?fa>?s zNB)(f*S5nFYNAiJy^Nedqv(h>6TxP)@vW$;{z|7xh|ssROASgVv60a3ReA{>(Et%9 z^!+vdc1rg!p>tE&M(B)Hv|^=I3sp9@6I%$C!6qx$9$OnieG@9|VWsS*ri>_C%3z!@ zI|-$ukI>(~pHTXh+Es+gRzj&a2U6Kw`cnErI$ti;KDWGe=aR)l@k;%X)8}w!iqn-s zsrJ+vUJX`YK_z%B(Xo&y- delta 1073 zcmXxj&r1|x9LMqR*f`E^S!S;JtFp2!wO9q|$3lk)iHJ_(AFzZ_CuPfnry=On#bB^& zMhERsK^u1RAQaxLOLfYWpeS6(>JkO@{;b~~<~y(FnR)hkzR&aQZ06p#B&*Hgj8RSy zUBs=x>@057aigqlGi$75ai`md@59V+e_PS?qJMD|^C^phAVFt&s9;c9T6HENT z9o)f-yWW1_KE#OUPf!ytp!zLgj4SRt)WB<~{-3;Eb$_6K{|gHk(s_XSZHS6iGKN~| z6w=3TV;fE*m(6q2gpa-b9JQiF)P&3K3TnJJ-u{9b_Zw>5fX?b4af_MX8mZi4+gni` z7aF#V zp?>fPHPACu0xxhUzCtcr<);3hQT?m#PxrUKuVZ@#c^_jDi`b5nsD!37>_3aKSson4 zdDOtKQ7d|jjAb8D32h*k{c$6*%h7H^^~OR?XHEraIh*_gKHpr! diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index a99e4954..d0e103fb 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-03-30 04:06+0100\n" -"PO-Revision-Date: 2014-03-30 04:06+0100\n" +"POT-Creation-Date: 2014-03-30 05:08+0100\n" +"PO-Revision-Date: 2014-03-30 05:09+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -24,13 +24,13 @@ msgstr "Fehler" #: ../../../views/html/charactergroups/groupsgroup.tpl:3 #: ../../../views/html/charactergroups/index.tpl:3 #: ../../../views/html/characters/character.tpl:50 -#: ../../../views/html/seminaries/seminary.tpl:44 +#: ../../../views/html/seminarymenu/index.tpl:4 msgid "Character Groups" msgstr "Charaktergruppen" #: ../../../views/html/charactergroups/group.tpl:25 #: ../../../views/html/characters/index.tpl:2 -#: ../../../views/html/seminaries/seminary.tpl:43 +#: ../../../views/html/seminarymenu/index.tpl:3 #: ../../../views/html/users/user.tpl:11 msgid "Characters" msgstr "Charaktere" @@ -81,6 +81,10 @@ msgid "Users" msgstr "Benutzer" #: ../../../views/html/menu/index.tpl:5 +msgid "Usergroups" +msgstr "Benutzergruppen" + +#: ../../../views/html/menu/index.tpl:6 #: ../../../views/html/seminaries/create.tpl:1 #: ../../../views/html/seminaries/delete.tpl:1 #: ../../../views/html/seminaries/edit.tpl:1 @@ -88,12 +92,12 @@ msgstr "Benutzer" msgid "Seminaries" msgstr "Kurse" -#: ../../../views/html/menu/index.tpl:11 ../../../views/html/users/login.tpl:2 +#: ../../../views/html/menu/index.tpl:8 ../../../views/html/users/login.tpl:2 #: ../../../views/html/users/login.tpl:11 msgid "Login" msgstr "Login" -#: ../../../views/html/menu/index.tpl:13 +#: ../../../views/html/menu/index.tpl:10 msgid "Logout" msgstr "Logout" @@ -123,8 +127,8 @@ msgstr "Richtige Lösung anzeigen" msgid "Go on" msgstr "Hier geht es weiter" -#: ../../../views/html/quests/quest.tpl:66 -#: ../../../views/html/quests/quest.tpl:68 +#: ../../../views/html/quests/quest.tpl:67 +#: ../../../views/html/quests/quest.tpl:76 msgid "Quest" msgstr "Quest" @@ -184,11 +188,19 @@ msgid "Create new seminary" msgstr "Neuen Kurs erstellen" #: ../../../views/html/seminaries/index.tpl:10 -#: ../../../views/html/seminaries/seminary.tpl:47 +#: ../../../views/html/seminaries/seminary.tpl:43 #, php-format msgid "created by %s on %s" msgstr "erstellt von %s am %s" +#: ../../../views/html/seminarymenu/index.tpl:5 +msgid "Achievements" +msgstr "Errungenschaften" + +#: ../../../views/html/seminarymenu/index.tpl:6 +msgid "Library" +msgstr "Bibliothek" + #: ../../../views/html/users/create.tpl:2 msgid "New user" msgstr "Neuer Benutzer" @@ -239,6 +251,10 @@ msgstr "registriert am %s" msgid "Roles" msgstr "Rollen" +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + #~ msgid "locked" #~ msgstr "gesperrt" diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index c3a77cae..252e84f0 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -39,10 +39,6 @@
              • -

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

                From 79eabe176839373f7a56e1338836a08b4f9199c8 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 30 Mar 2014 15:03:11 +0200 Subject: [PATCH 150/340] update headlines: let content begin at h1 (Issue #50) --- controllers/QuestsController.inc | 8 ++++++++ views/html/charactergroups/group.tpl | 13 +++++-------- views/html/charactergroups/groupsgroup.tpl | 9 ++++----- views/html/charactergroups/index.tpl | 5 ++--- views/html/characters/character.tpl | 13 +++++++------ views/html/characters/index.tpl | 4 ++-- views/html/error/index.tpl | 2 +- views/html/introduction/index.tpl | 2 +- views/html/questgroups/questgroup.tpl | 6 +++--- views/html/quests/quest.tpl | 6 ++---- views/html/quests/submission.tpl | 9 ++++++--- views/html/quests/submissions.tpl | 12 +++++++----- views/html/seminaries/create.tpl | 4 ++-- views/html/seminaries/delete.tpl | 4 ++-- views/html/seminaries/edit.tpl | 4 ++-- views/html/seminaries/index.tpl | 2 +- views/html/seminaries/seminary.tpl | 5 ++--- views/html/users/create.tpl | 4 ++-- views/html/users/delete.tpl | 4 ++-- views/html/users/edit.tpl | 4 ++-- views/html/users/index.tpl | 2 +- views/html/users/login.tpl | 4 ++-- views/html/users/user.tpl | 8 ++++---- 23 files changed, 70 insertions(+), 64 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 51d42cc8..51e1664c 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -265,6 +265,10 @@ // Get Questgroup $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } // Get Quest $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); @@ -314,6 +318,10 @@ // Get Questgroup $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } // Get Quest $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 9f5bd147..ff53d1cf 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -1,17 +1,14 @@ -
                +

                +

                -

                +

                Schweb wie ein Schmetterling! Stich wie eine Biene!
                  @@ -22,7 +19,7 @@
                -

                +

                • @@ -60,7 +57,7 @@
                -

                +

                • diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl index fdae0ed5..016742b6 100644 --- a/views/html/charactergroups/groupsgroup.tpl +++ b/views/html/charactergroups/groupsgroup.tpl @@ -1,7 +1,6 @@ -

                  -

                  -

                  -
                  +

                  +

                  +

                    @@ -10,7 +9,7 @@
                  -
                  +

                  • diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl index e1726ce7..0a2cd85b 100644 --- a/views/html/charactergroups/index.tpl +++ b/views/html/charactergroups/index.tpl @@ -1,6 +1,5 @@ -

                    -

                    -

                    +

                    +

                      diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index cb7ec385..9feff427 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -1,5 +1,6 @@

                      -

                      +

                      +

                      @@ -22,7 +23,7 @@

                      Platz

                      -

                      Belohnungen

                      +

                      Belohnungen

                      • Aktive Beteiligung

                        @@ -47,7 +48,7 @@
                      -

                      +

                      • @@ -65,7 +66,7 @@
                        -

                        Neue Achievements

                        +

                        Neue Achievements

                        • @@ -96,7 +97,7 @@
                        -

                        Ranking

                        +

                        Ranking

                        • @@ -128,7 +129,7 @@
                        -

                        Thematischer Fortschritt

                        +

                        Thematischer Fortschritt

                        • Klassifikation (3/5)

                          diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl index cd0fde8b..94759710 100644 --- a/views/html/characters/index.tpl +++ b/views/html/characters/index.tpl @@ -1,5 +1,5 @@ -

                          -

                          +

                          +

                            diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl index 7f2f0cbb..0f68bc07 100644 --- a/views/html/error/index.tpl +++ b/views/html/error/index.tpl @@ -1,2 +1,2 @@ -

                            +

                            :

                            diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index de8740da..90ca59f7 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -1,4 +1,4 @@ -

                            +

                            Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.

                            Entwickler:

                              diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index ff4af3ab..aa9bf3e5 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -3,14 +3,14 @@ -

                              +

                              -

                              :

                              +

                              :

                              -

                              +

                              diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 3d440d50..fe27c287 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -3,11 +3,9 @@ -

                              - +

                              - -

                              +

                              diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl index 17f986f2..6417f76e 100644 --- a/views/html/quests/submission.tpl +++ b/views/html/quests/submission.tpl @@ -1,7 +1,10 @@ -

                              - + +
                              + +
                              + +

                              - diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl index b53085d6..f14c2a8f 100644 --- a/views/html/quests/submissions.tpl +++ b/views/html/quests/submissions.tpl @@ -1,9 +1,11 @@ -

                              - + +
                              + +
                              + +

                              - - -

                              +

                              diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl index f50d2af0..823eec70 100644 --- a/views/html/seminaries/create.tpl +++ b/views/html/seminaries/create.tpl @@ -1,5 +1,5 @@ -

                              -

                              +

                              +

                              diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl index b9542006..d2253f14 100644 --- a/views/html/seminaries/delete.tpl +++ b/views/html/seminaries/delete.tpl @@ -1,5 +1,5 @@ -

                              -

                              +

                              +

                              diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl index 7905f17d..007acf15 100644 --- a/views/html/seminaries/edit.tpl +++ b/views/html/seminaries/edit.tpl @@ -1,5 +1,5 @@ -

                              -

                              +

                              +

                              diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index bd5c73e4..8ac8c8b0 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -1,4 +1,4 @@ -

                              +

                              diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 252e84f0..c85d0a92 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -4,12 +4,11 @@ -

                              -

                              +

                              -

                              +

                              • diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl index b4e22763..8536804d 100644 --- a/views/html/users/create.tpl +++ b/views/html/users/create.tpl @@ -1,5 +1,5 @@ -

                                -

                                +

                                +

                                diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl index ea23061f..10f280ab 100644 --- a/views/html/users/delete.tpl +++ b/views/html/users/delete.tpl @@ -1,5 +1,5 @@ -

                                -

                                +

                                +

                                diff --git a/views/html/users/edit.tpl b/views/html/users/edit.tpl index bb3655c7..ef47268c 100644 --- a/views/html/users/edit.tpl +++ b/views/html/users/edit.tpl @@ -1,5 +1,5 @@ -

                                -

                                +

                                +

                                diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl index f747e6a5..5d2883af 100644 --- a/views/html/users/index.tpl +++ b/views/html/users/index.tpl @@ -1,4 +1,4 @@ -

                                +

                                diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl index f6e0171a..36c13da4 100644 --- a/views/html/users/login.tpl +++ b/views/html/users/login.tpl @@ -1,5 +1,5 @@ -

                                -

                                +

                                +

                                diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 1ba66b0d..3a6ea526 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -1,5 +1,5 @@ -

                                -

                                +

                                +

                              - + 0) : ?>
                              -

                              +

                              + + +
                              -

                              -
                              - -
                                - -
                              • + +

                                + + +

                                + 0 || !empty($questtext['abort_text'])) : ?> +
                                  + +
                                • + + +
                                • + +
                                + - -
                              • - - -
                              • - -
                              - - - 1) : ?>< - / - > - +
                              @@ -64,7 +64,7 @@
                            • : - + From 6abb256ef644836ac7c08344b207e7cb7395672b Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 1 Apr 2014 13:44:55 +0200 Subject: [PATCH 153/340] update view for solved Quests --- controllers/QuestsController.inc | 2 +- views/html/quests/quest.tpl | 34 +++++++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 9218a912..26c85705 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -198,7 +198,7 @@ $nextQuests = null; $charactedHasChoosenNextQuest = false; $nextQuestgroup = null; - if($questtexttypeUrl == 'Epilog' || $solved) + if($questtexttypeUrl == 'Epilog')) { // Next Quest $nextQuests = $this->Quests->getNextQuests($quest['id']); diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 53a5bd06..ed6d5429 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -7,17 +7,6 @@

                              - -
                              - -

                              - -

                              - -

                              -
                              - - 0) : ?>

                              @@ -45,18 +34,35 @@
                              + +
                              + +

                              + +

                              + +

                              +
                              + +
                              -

                              +

                              + + +

                              :

                              + +
                              -

                              ()

                              -

                              0) : ?>
                                From bca78402d9e6aaf577a78d73862336ac30b0d41b Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 1 Apr 2014 13:45:09 +0200 Subject: [PATCH 154/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2801 -> 2828 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 91 +++++++++++--------- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 658b0ec007b9918a2cd8c72d6397e854cf392ae5..9d7d7bcfc0133013c263ec557d6b485f2d10812a 100644 GIT binary patch delta 1243 zcmYM!OGs2v9LMp~IO90iG&42RtjV%`P2KSUWoeLMw2(xEkmxblrKSc)X0ApCNkY^@ z3c4ggQZ3pAMMRq*YSl6cYFDn>^}vWWt@{3^QyuRAe(t&Fcpm?A$M4m1H8V?<;ai51 zB-RrTRv6Qd?M2)uJHy7T#9df{1Gp9sqk~yogVVSQuUqe8DfLIz=U7JlHOBBAMvR#; zOFU@cPpFB0{%J5FTBFoUkjqqXE5;hs#0{wa%~*on);{Di2e_&KATp#GvF}H%$I)Sa z&<5pmhY5u_LN8 zzsd5TiKb8!T~!0lanpbS-s4ynQ2qAsQVyby;uvZHQ&@$Uu@>i1DSwQs@dYYli>Rae zfHPXj7alaBmaBoIs0`GicCrOEL94BIqx$bg4Ybd;52G@26dBTFP=7y(>VMJJXKnpj zjQp$NhHaQfeTWO#gKtm^iZQ#aLwyr%s3Yq{O)!R9a2mCsNz_iSpcZn|zQ2PEX&zdi zIpn{Z7cXtc_o!4Y*$&I734hsoh*>y^LeWt*5F3alLJL<>|H{G>nM0S@ZtEJmo+xA+ z*XjIqhFghtVjH2Kp>~rXbbfzJjeXD=E6vJ?c3VemB2-%bU^ZK|xj4~E=%`iL%fHgA zG%6)Zmr|+HMks9?3HxuL@Ig^EA>AmAM03y-eieKUpD0R=p9>B~E(ME`#%L;&^HQfS z1j~{7@{z2c%ETvf+0!{MpAVekCmmf0HyKZM_H=c&xk=Y`h7Ki$y{U9QogGi?Px)R? zyu(e>>3_ecUyI8^L3!+KWGIvJr-Spc*&yWfMNfJ;FYV`py-t1UKsM*+$Ij%^z8{P_ Gw|@hY@p5Yb delta 1217 zcmX}rPe_zO7{~F+cGvdxPv)l9R{EBj+Pe9!|1iM_43sXKMTnQMVSRIj zP6q=fQB_z)J<&VV#y%ozn{7OVKah_JGpjaGhFZ85b-e-ggw4pubXa>)^A6bjB37}! z8Ki${O`0LgsYavSg zHQ){xQn-TsxQ>VM4{BpER@Xbwge};Py6-Y-0~4qxoIx$TXs_QwrZD%dkFkdQ8EV`c zhx)5T@9lss)Iy(azKxtfvFV785%oK1@m*0@<5fFtg?^Qx@0q9it)@pJB! qKb?2|d3Vvz Date: Tue, 1 Apr 2014 13:47:00 +0200 Subject: [PATCH 155/340] fix typo in QuestsController --- controllers/QuestsController.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 26c85705..4fcdedeb 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -198,7 +198,7 @@ $nextQuests = null; $charactedHasChoosenNextQuest = false; $nextQuestgroup = null; - if($questtexttypeUrl == 'Epilog')) + if($questtexttypeUrl == 'Epilog') { // Next Quest $nextQuests = $this->Quests->getNextQuests($quest['id']); From 25c2eab459ef5288e9646fefb1b2dbc60c7f560d Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 3 Apr 2014 01:16:43 +0200 Subject: [PATCH 156/340] implement multiple questions for Questtype ?multiple choice? --- .../MultiplechoiceQuesttypeController.inc | 179 +++++++++++++++--- .../MultiplechoiceQuesttypeModel.inc | 90 +++++++-- questtypes/multiplechoice/html/quest.tpl | 26 ++- questtypes/multiplechoice/html/submission.tpl | 19 +- 4 files changed, 248 insertions(+), 66 deletions(-) diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc index 57f60e82..14880743 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -34,14 +34,23 @@ */ public function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers) { - // Get questions - $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); foreach($questions as &$question) { - $answer = (array_key_exists(intval($question['pos'])-1, $answers)) ? true : false; - $this->Multiplechoice->setCharacterSubmission($question['id'], $character['id'], $answer); + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } } } @@ -57,27 +66,43 @@ */ public function matchAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers) { - // Get right answers - $tickQuestions = $this->Multiplechoice->getTickQuestionsOfQuest($quest['id']); + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); - // Match tick questions with user answers - $allSolved = true; - foreach($tickQuestions as &$tickQuestion) + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) { - $pos = intval($tickQuestion['pos'])-1; - if(!array_key_exists($pos, $answers) || $answers[$pos] != 'true') + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) { - $allSolved = false; - break; - } - else { - unset($answers[$pos]); + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } } } - // Return status - return ($allSolved && count($answers) == 0); + // All questions correct answerd + return true; } @@ -94,24 +119,54 @@ */ public function quest($seminary, $questgroup, $quest, $character) { - // Get questions - $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($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']); + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); } } - // Has Character already solved Quest? - $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } // Pass data to view - $this->set('questions', $questions); - $this->set('solved', $solved); + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); } @@ -129,8 +184,16 @@ { // Get questions $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); - foreach($questions as &$question) { - $question['answer'] = $this->Multiplechoice->getCharacterSubmission($question['id'], $character['id']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } } @@ -138,6 +201,60 @@ $this->set('questions', $questions); } + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + } ?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc index fef90bcb..6c4931b7 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -23,6 +23,30 @@ + /** + * Get the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + /** * Get all multiple choice questions of a Quest. * @@ -32,7 +56,7 @@ public function getQuestionsOfQuest($questId) { return $this->db->query( - 'SELECT id, pos, question, tick '. + 'SELECT id, pos, question '. 'FROM questtypes_multiplechoice '. 'WHERE quest_id = ?', 'i', @@ -42,20 +66,44 @@ /** - * Get all multiple choice questions of a Quest that should be - * ticked. + * Get one multiple choice question of a Quest. * * @param int $questId ID of Quest - * @return array Multiple choice questions that should be ticked + * @param int $pos Position of question + * @return array Question data */ - public function getTickQuestionsOfQuest($questId) + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) { return $this->db->query( - 'SELECT id, question, tick, pos '. - 'FROM questtypes_multiplechoice '. - 'WHERE quest_id = ? AND tick = True', + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', 'i', - $questId + $questionId ); } @@ -63,21 +111,21 @@ /** * 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 + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option */ - public function setCharacterSubmission($multipleChoiceId, $characterId, $answer) + public function setCharacterSubmission($answerId, $characterId, $answer) { $this->db->query( 'INSERT INTO questtypes_multiplechoice_characters '. - '(questtypes_multiplechoice_id, character_id, ticked) '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. 'VALUES '. '(?, ?, ?) '. 'ON DUPLICATE KEY UPDATE '. 'ticked = ?', 'iiii', - $multipleChoiceId, $characterId, $answer, $answer + $answerId, $characterId, $answer, $answer ); } @@ -85,18 +133,18 @@ /** * 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 + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false */ - public function getCharacterSubmission($multipleChoiscId, $characterId) + public function getCharacterSubmission($answerId, $characterId) { $data = $this->db->query( 'SELECT ticked '. 'FROM questtypes_multiplechoice_characters '. - 'WHERE questtypes_multiplechoice_id = ? AND character_id = ? ', + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', 'ii', - $multipleChoiscId, $characterId + $answerId, $characterId ); if(!empty($data)) { return $data[0]['ticked']; diff --git a/questtypes/multiplechoice/html/quest.tpl b/questtypes/multiplechoice/html/quest.tpl index 59c1743d..c2c32595 100644 --- a/questtypes/multiplechoice/html/quest.tpl +++ b/questtypes/multiplechoice/html/quest.tpl @@ -1,11 +1,21 @@ -
                                  - &$question) : ?> -
                                1. - /> - -
                                2. - -
                                +
                                + +

                                +
                                  + &$answer) : ?> +
                                1. + /> + +
                                2. + +
                                +
                                + + + + + + diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl index 0459b59b..ae14f199 100644 --- a/questtypes/multiplechoice/html/submission.tpl +++ b/questtypes/multiplechoice/html/submission.tpl @@ -1,9 +1,16 @@ -
                                  - +
                                    + &$question) : ?>
                                  1. - - × - +

                                    +
                                      + +
                                    1. + + × + +
                                    2. + +
                                  2. -
                                + From 0f6ba634babf8e0765d2e329f07ec4ba179b84e0 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 3 Apr 2014 01:16:52 +0200 Subject: [PATCH 157/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 2828 -> 3145 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 59 +++++++++++++++----- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 9d7d7bcfc0133013c263ec557d6b485f2d10812a..45facaa0014bb346c481d2538920488b4916d59b 100644 GIT binary patch delta 1484 zcmX|>Pe@cz6o>DW&iJn^Gb_t_W|NtYI+{j81wuh#VMH0!qRDwVL+aGb8!00ug2IIq zG}xx77OjfN7?4&PNknM13EYSaTZFVI+5|z+_nUcjc;|li+;{Ie=brOsMl0U0Pc4+? zjTvM&x(WU4G3F2)UCw}v=NVH1U&2*z60U=vU=jQZ*TA2!6ngV>tboPX+g-N671;Y= zA?$)YDRYuhj?X{^8pypc!_bF)-L*%c7yC9;;4!HEk6{6P?s5Y1m??(U@GaE2IY^FX z0rHqHS*DCxVj_p%VH5lfo8eAQ=?15v66}L=a2Z=F7LSO_h68H4`2uU2$fhN zS&5a9VwifU7Pmsh=`E)I%uSR*0h3TSz6q7kU3dLHR12O#IU0vt+Prf42Cl`Pg*vwg zRl#rW`#(?@@!?$lS2D=Y#uSr{OlqJU?uWF_Y(tfhoUml7&^A;_G@_DrRP9ng2{E#w zO6Ys|C+c~NYcxZ}sX+s%zCWX%x_p#&@yqRG>R%hoes5xE+f3M!G{zBoOSd z8XKE(&n3lju3WS;dkQ)|r3d4oK3ho+Wpm987Y02gB4~^w$J0-Xx;y))CldCc l$!&IxK3fyV*wMj0J8nmNBaRgsP`b(Ncp?%vNnYi4{RNO)tLy*( delta 1170 zcmX}sU5Lz47{~Ev#_Z0^dRb$QF$}X_cXt*u&2F)*WJRe-X(I_2Y*L%uV)nvbOq96T zkbm~Zh709FxR^*vlu~YNin}!zaw#df;l}qj`|O_n&;N7I{~YJ#IkP^bCsNgVGIGZ# zMPfDaFl07>-3xe8wnxks;tp)Z{kQ^;Vgg5S8D7FAc*DJiEwrDyuW%9V_ZY{|*ko3< zIu%X)9ktM(*$xZS8>JmX9&6>*j49N@S=9IrjA5_48+q&iFO4rFQ`(?^Kjxmo1nb)n zm2C_d#R5J+?fesJV_#7d{KOpoMjmTp_QjY%ZKxBqksefkKkB3d$YW)95S6jxYO}tL zP|-qTsD-Yn11ETC!T|4cEmNp*yZ9)}sH-@M+Q1mL;blzYBr4_4a4EhYJo0q_oBw{L`}5U`wycsasrvshEe~%f*L>W?JM5C9w-0m zxal2}s2}1K_Tfj=hT^O)GpOH09(83U)B>kb8$O5HPz80;tEi3K_V0I*DebZQJVE}K z^WlvT{DMkV-3RBUe0)!+Hl8Sif|o6al4pm8-@UkS(JJTXYX1RqEnOf0 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 6401b58d..88fa423d 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-01 13:43+0100\n" -"PO-Revision-Date: 2014-04-01 13:43+0100\n" +"POT-Creation-Date: 2014-04-03 01:16+0100\n" +"PO-Revision-Date: 2014-04-03 01:16+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -10,10 +10,53 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.6.4\n" -"X-Poedit-Basepath: .\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" +"X-Poedit-SearchPath-1: ../../../questtypes\n" + +#: ../../../questtypes/dragndrop/html/quest.tpl:16 +#: ../../../questtypes/multiplechoice/html/quest.tpl:19 +#: ../../../questtypes/submit/html/quest.tpl:12 +#: ../../../questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: ../../../questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: ../../../questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: ../../../questtypes/submit/html/quest.tpl:6 +msgid "Words" +msgstr "Wörter" + +#: ../../../questtypes/submit/html/quest.tpl:8 +msgid "Word" +msgstr "Wort" + +#: ../../../questtypes/submit/html/quest.tpl:10 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: ../../../questtypes/submit/html/submission.tpl:6 +#: ../../../questtypes/submit/html/submission.tpl:8 +#: ../../../views/html/quests/quest.tpl:40 +#: ../../../views/html/quests/submissions.tpl:24 +msgid "solved" +msgstr "gelöst" + +#: ../../../questtypes/submit/html/submission.tpl:9 +#: ../../../views/html/quests/quest.tpl:42 +#: ../../../views/html/quests/submissions.tpl:15 +msgid "unsolved" +msgstr "ungelöst" #: ../../../views/binary/error/index.tpl:1 #: ../../../views/html/error/index.tpl:1 @@ -113,16 +156,6 @@ msgstr "Login" msgid "Logout" msgstr "Logout" -#: ../../../views/html/quests/quest.tpl:40 -#: ../../../views/html/quests/submissions.tpl:24 -msgid "solved" -msgstr "gelöst" - -#: ../../../views/html/quests/quest.tpl:42 -#: ../../../views/html/quests/submissions.tpl:15 -msgid "unsolved" -msgstr "ungelöst" - #: ../../../views/html/quests/quest.tpl:50 msgid "Task" msgstr "Aufgabe" From eb9ea3b0ca4d3083aabc9fb640e29c2c03ad266d Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 00:57:05 +0200 Subject: [PATCH 158/340] implement functionality for Questtype ?Drag&Drop? --- .../DragndropQuesttypeController.inc | 77 +++++++++++++++---- .../dragndrop/DragndropQuesttypeModel.inc | 57 +++++++++++--- questtypes/dragndrop/html/quest.tpl | 10 +-- questtypes/dragndrop/html/submission.tpl | 22 ++++-- views/html/html.tpl | 1 + www/js/dnd.js | 54 +++++++++++++ 6 files changed, 183 insertions(+), 38 deletions(-) create mode 100644 www/js/dnd.js diff --git a/questtypes/dragndrop/DragndropQuesttypeController.inc b/questtypes/dragndrop/DragndropQuesttypeController.inc index fa801be2..1007a1f8 100644 --- a/questtypes/dragndrop/DragndropQuesttypeController.inc +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -49,9 +49,18 @@ // Save user answers foreach($drops as &$drop) { - if(!array_key_exists($drop['id'], $answers)) { - $this->Dragndrop->setCharacterSubmission($drop['id'], $character['id'], $answers[$drop['id']]); + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['id'], $character['id'], $answer); } } @@ -74,19 +83,16 @@ $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; + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; } } // Set status - return $allSolved; + return true; } @@ -107,17 +113,26 @@ $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) { + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { $drag['media'] = $this->Media->getMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; } - // Has Character already solved Quest? - $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + foreach($drops as &$drop) + { + // Get saved user answer + $drop['useranswer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['useranswer'])) + { + $drop['useranswer'] = $drags[$drop['useranswer']]; + unset($drags[$drop['useranswer']['id']]); + } + } // Pass data to view @@ -130,7 +145,6 @@ /** * Action: submission. - * TODO submission() * * Show the submission of a Character for a Quest. * @@ -141,6 +155,37 @@ */ public function submission($seminary, $questgroup, $quest, $character) { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + foreach($drops as &$drop) + { + // Get saved user answer + $drop['useranswer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['useranswer'])) + { + $drop['useranswer'] = $drags[$drop['useranswer']]; + unset($drags[$drop['useranswer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); } } diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc index 676e502f..1e772edf 100644 --- a/questtypes/dragndrop/DragndropQuesttypeModel.inc +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -84,7 +84,7 @@ /** - * Save Character’s submitted answer for one Drag&Drop-field. + * Save Character’s submitted answer for one Drop-field. * * @param int $dropId ID of Drop-field * @param int $characterId ID of Character @@ -92,16 +92,53 @@ */ 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 + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; } } diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl index 74e497c7..28d208b2 100644 --- a/questtypes/dragndrop/html/quest.tpl +++ b/questtypes/dragndrop/html/quest.tpl @@ -1,14 +1,14 @@
                                -
                                +
                                -
                                - +
                                +
                                -
                                +
                                - +
                                diff --git a/questtypes/dragndrop/html/submission.tpl b/questtypes/dragndrop/html/submission.tpl index dbc453c3..90148212 100644 --- a/questtypes/dragndrop/html/submission.tpl +++ b/questtypes/dragndrop/html/submission.tpl @@ -1,7 +1,15 @@ - &$text) : ?> - 0) : ?> - - - - - +
                                + +
                                + + + +
                                + +
                                + +
                                + + + +
                                diff --git a/views/html/html.tpl b/views/html/html.tpl index e36da721..da2c90b1 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -21,6 +21,7 @@ + diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} From 8c6a68509f9f351c78abfdcc9fb515fe138e5da3 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 01:09:38 +0200 Subject: [PATCH 159/340] only shows Character answer on request for Questtype ?Drag&Drop? --- .../DragndropQuesttypeController.inc | 27 +++++++++++-------- questtypes/dragndrop/html/quest.tpl | 4 +-- questtypes/dragndrop/html/submission.tpl | 4 +-- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/questtypes/dragndrop/DragndropQuesttypeController.inc b/questtypes/dragndrop/DragndropQuesttypeController.inc index 1007a1f8..85b96844 100644 --- a/questtypes/dragndrop/DragndropQuesttypeController.inc +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -123,14 +123,18 @@ // Get Drops $drops = $this->Dragndrop->getDrops($dndField['quest_id']); - foreach($drops as &$drop) + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') { - // Get saved user answer - $drop['useranswer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); - if(!is_null($drop['useranswer'])) + foreach($drops as &$drop) { - $drop['useranswer'] = $drags[$drop['useranswer']]; - unset($drags[$drop['useranswer']['id']]); + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } } } @@ -169,14 +173,15 @@ // Get Drops $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers foreach($drops as &$drop) { - // Get saved user answer - $drop['useranswer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); - if(!is_null($drop['useranswer'])) + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) { - $drop['useranswer'] = $drags[$drop['useranswer']]; - unset($drags[$drop['useranswer']['id']]); + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); } } diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl index 28d208b2..bf8dcfe2 100644 --- a/questtypes/dragndrop/html/quest.tpl +++ b/questtypes/dragndrop/html/quest.tpl @@ -1,8 +1,8 @@
                                -
                                - +
                                +
                                diff --git a/questtypes/dragndrop/html/submission.tpl b/questtypes/dragndrop/html/submission.tpl index 90148212..043d39ac 100644 --- a/questtypes/dragndrop/html/submission.tpl +++ b/questtypes/dragndrop/html/submission.tpl @@ -1,8 +1,8 @@
                                - - + +
                                From 0e557f2d0df2c539540a2e50d583af2d40a93638 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 01:28:22 +0200 Subject: [PATCH 160/340] remove PHP>=5.5 specifics --- models/QuestgroupsModel.inc | 2 +- views/html/menu/index.tpl | 2 +- views/html/seminarymenu/index.tpl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index 33a7d1b0..3e2ced21 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -264,7 +264,7 @@ } // Get next Quests - while(!is_null($currentQuest) && !empty($nextQuests = $this->Quests->getNextQuests($currentQuest['id']))) + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) { // Get choosed Quest $currentQuest = null; diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 393e4747..2c3299b4 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,6 +1,6 @@
                              • The Legend of Z
                              • -
                              • + 0) : ?>
                              • diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl index fa6d4457..ee29969f 100644 --- a/views/html/seminarymenu/index.tpl +++ b/views/html/seminarymenu/index.tpl @@ -1,6 +1,6 @@
                              • -
                              • + 0) : ?>
                              • From 49bed142149c319d5ada143930c6a0cd06eb9a87 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 01:41:08 +0200 Subject: [PATCH 161/340] use Hierarchy name of related Quest?s Questgroup for optional Questgroups --- controllers/QuestgroupshierarchypathController.inc | 8 ++++++++ views/html/questgroupshierarchypath/index.tpl | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc index 196aa928..fabf2bbb 100644 --- a/controllers/QuestgroupshierarchypathController.inc +++ b/controllers/QuestgroupshierarchypathController.inc @@ -56,11 +56,19 @@ } if(is_null($questgroup['hierarchy'])) { + // Get related Questgroup $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); $quest = $this->Quests->getQuestById($questtext['quest_id']); $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + array_unshift($parentQuestgroupshierarchy, $quest); array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); } diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl index 59b98429..abe15abe 100644 --- a/views/html/questgroupshierarchypath/index.tpl +++ b/views/html/questgroupshierarchypath/index.tpl @@ -5,7 +5,7 @@ - : + : From 9582ed7696b7cbb286aabc28f7774cf1ace4a74b Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 13:21:17 +0200 Subject: [PATCH 162/340] implement UploadsAgent for user uploads --- agents/intermediate/UploadsAgent.inc | 35 ++++++ configs/AppConfig.inc | 10 +- controllers/UploadsController.inc | 174 +++++++++++++++++++++++++++ models/UploadsModel.inc | 148 +++++++++++++++++++++++ uploads/empty | 0 views/binary/uploads/index.tpl | 1 + 6 files changed, 365 insertions(+), 3 deletions(-) create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 controllers/UploadsController.inc create mode 100644 models/UploadsModel.inc create mode 100644 uploads/empty create mode 100644 views/binary/uploads/index.tpl diff --git a/agents/intermediate/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 817b0114..541a51e2 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -62,7 +62,8 @@ 'locale' => 'locale', 'media' => 'media', 'questtypes' => 'questtypes', - 'temporary' => 'tmp' + 'temporary' => 'tmp', + 'uploads' => 'uploads' ); @@ -87,7 +88,9 @@ 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) + array('media/(.*)', 'media/$1?layout=binary', false), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) ); @@ -104,7 +107,8 @@ //array('seminaries/seminary/(.*)', '$1', false) array('characters/index/(.*)', 'characters/$1', true), array('charactergroups/index/(.*)', 'charactergroups/$1', true), - array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true) + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$1', true) ); diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..a1fb7701 --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> diff --git a/models/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> diff --git a/uploads/empty b/uploads/empty new file mode 100644 index 00000000..e69de29b diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/index.tpl @@ -0,0 +1 @@ + From b8b921ae6f629d3cdb41084c446c85952d28fd44 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 13:21:56 +0200 Subject: [PATCH 163/340] ignore uploaded files --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 822d53ff..7b6a4408 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,4 @@ syntax: glob media/* tmp/* +uploads/* From 76875f1c698184d5a588f8c4f9ea2337e824532b Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 13:26:49 +0200 Subject: [PATCH 164/340] 1) update Quest stati 2) add SubmissionNotValidException and -handling for Quests --- app/QuesttypeController.inc | 11 +-- .../SubmissionNotValidException.inc | 77 +++++++++++++++++ controllers/QuestsController.inc | 66 ++++++++------- models/CharactersModel.inc | 82 +++++++++++++++++++ models/QuestsModel.inc | 78 ++++++------------ .../DragndropQuesttypeController.inc | 11 +-- questtypes/dummy/DummyQuesttypeController.inc | 11 +-- .../MultiplechoiceQuesttypeController.inc | 11 +-- .../TextinputQuesttypeController.inc | 11 +-- views/html/quests/submission.tpl | 1 + views/html/quests/submissions.tpl | 17 +++- 11 files changed, 268 insertions(+), 108 deletions(-) create mode 100644 app/exceptions/SubmissionNotValidException.inc diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 6bbabdd2..25544ab2 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -59,12 +59,13 @@ * * 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 + * @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 Exception $exception Character submission exception */ - public abstract function quest($seminary, $questgroup, $quest, $character); + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); diff --git a/app/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 4fcdedeb..e755fe01 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -277,17 +277,14 @@ $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); } + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + // Get unsolved Character submissions - $unsolvedSubmissions = $this->Quests->getCharactersUnsolvedQuest($quest['id']); - foreach($unsolvedSubmissions as &$submission) { - $submission['character'] = $this->Characters->getCharacterById($submission['character_id']); - } + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); // Get solved Character submissions - $solvedSubmissions = $this->Quests->getCharactersSolvedQuest($quest['id']); - foreach($solvedSubmissions as &$submission) { - $submission['character'] = $this->Characters->getCharacterById($submission['character_id']); - } + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); // Pass data to view @@ -295,8 +292,9 @@ $this->set('questgroup', $questgroup); $this->set('quest', $quest); $this->set('media', $questmedia); - $this->set('unsolvedsubmissions', $unsolvedSubmissions); - $this->set('solvedsubmissions', $solvedSubmissions); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); } @@ -380,27 +378,39 @@ $answers = $this->request->getPostParam('answers'); // Save answers in database - if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { - $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); - } + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } - // Match answers with correct ones - $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); - if($status === true) - { - // Mark Quest as solved - $this->Quests->setQuestSolved($quest['id'], $character['id']); + // 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'))); + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } } - 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'))); + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); } } diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 7a3a5d6a..1f0c1f5e 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -254,6 +254,88 @@ return 1; } + + /** + * 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 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 EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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 EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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 EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + } ?> diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 0ea48fc1..fb54519d 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -24,19 +24,25 @@ * * @var int; */ - const QUEST_STATUS_ENTERED = 1; + const QUEST_STATUS_ENTERED = 0; /** - * Quest-status: Solved + * Quest-status: submitted * * @var int; */ - const QUEST_STATUS_SOLVED = 0; + const QUEST_STATUS_SUBMITTED = 1; /** * Quest-status: Unsolved * * @var int; */ - const QUEST_STATUS_UNSOLVED = -1; + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; @@ -184,14 +190,14 @@ /** - * Mark a Quest as solved for a Character. + * Mark a Quest as submitted for a Character. * - * @param int $questId ID of Quest to mark as solved - * @param int $characterId ID of Character that solved the Quest + * @param int $questId ID of Quest to mark as unsolved + * @param int $characterId ID of Character that unsolved the Quest */ - public function setQuestSolved($questId, $characterId) + public function setQuestSubmitted($questId, $characterId) { - $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SOLVED); + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SUBMITTED); } @@ -207,6 +213,18 @@ } + /** + * 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->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SOLVED, false); + } + + /** * Determine if the given Character has entered a Quest. * @@ -279,48 +297,6 @@ } - /** - * 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 = ?'. - ')', - 'ii', - $questId, - static::QUEST_STATUS_SOLVED - ); - } - - - /** - * 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 = ?'. - ')', - 'ii', - $questId, - static::QUEST_STATUS_SOLVED - ); - } - - /** diff --git a/questtypes/dragndrop/DragndropQuesttypeController.inc b/questtypes/dragndrop/DragndropQuesttypeController.inc index 85b96844..2fc0b2b2 100644 --- a/questtypes/dragndrop/DragndropQuesttypeController.inc +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -102,12 +102,13 @@ * 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 + * @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 Exception $exception Character submission exception */ - public function quest($seminary, $questgroup, $quest, $character) + public function quest($seminary, $questgroup, $quest, $character, $exception) { // Get Drag&Drop field $dndField = $this->Dragndrop->getDragndrop($quest['id']); diff --git a/questtypes/dummy/DummyQuesttypeController.inc b/questtypes/dummy/DummyQuesttypeController.inc index d9ed7aab..a478cda5 100644 --- a/questtypes/dummy/DummyQuesttypeController.inc +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -61,12 +61,13 @@ * * 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 + * @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 Exception $exception Character submission exception */ - public function quest($seminary, $questgroup, $quest, $character) + public function quest($seminary, $questgroup, $quest, $character, $exception) { // Nothing to do } diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc index 14880743..456db617 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -112,12 +112,13 @@ * 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 + * @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 Exception $exception Character submission exception */ - public function quest($seminary, $questgroup, $quest, $character) + public function quest($seminary, $questgroup, $quest, $character, $exception) { // Get count of questions $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc index 13e48612..5be23690 100644 --- a/questtypes/textinput/TextinputQuesttypeController.inc +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -90,12 +90,13 @@ * 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 + * @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 Exception $exception Character submission exception */ - public function quest($seminary, $questgroup, $quest, $character) + public function quest($seminary, $questgroup, $quest, $character, $exception) { // Get Task $task = $this->Textinput->getTextinputQuest($quest['id']); diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl index 6417f76e..9d13af65 100644 --- a/views/html/quests/submission.tpl +++ b/views/html/quests/submission.tpl @@ -5,6 +5,7 @@

                                +

                                diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl index f14c2a8f..538b8565 100644 --- a/views/html/quests/submissions.tpl +++ b/views/html/quests/submissions.tpl @@ -12,20 +12,29 @@
                                +

                                +
                                  + +
                                • + +
                                • + +
                                +

                                  - +
                                • - +

                                  - +
                                • - +
                                From 16e5f0c53238cd4b646347f32cdc85051035995e Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 13:27:25 +0200 Subject: [PATCH 165/340] provide a localized number formatter --- app/Controller.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/Controller.inc b/app/Controller.inc index 42881662..5731aeed 100644 --- a/app/Controller.inc +++ b/app/Controller.inc @@ -67,7 +67,7 @@ // Create linker $this->linker = new \nre\core\Linker($this->request); - // Create date and time formatter + // Create date and time and number formatter $this->set('dateFormatter', new \IntlDateFormatter( \nre\core\Config::getDefault('locale'), \IntlDateFormatter::MEDIUM, @@ -80,6 +80,10 @@ \IntlDateFormatter::SHORT, NULL )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); } From 8abf29b2b0b1df89fa4543f0402dade73a8fdab0 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 13:32:31 +0200 Subject: [PATCH 166/340] implement uploading a file as answer for Questtype ?Submit? --- app/exceptions/MaxFilesizeException.inc | 51 ++++++++++++++++++ app/exceptions/WrongFiletypeException.inc | 51 ++++++++++++++++++ .../submit/SubmitQuesttypeController.inc | 54 ++++++++++++++----- questtypes/submit/SubmitQuesttypeModel.inc | 52 ++++++++++++++---- questtypes/submit/html/quest.tpl | 31 +++++++---- questtypes/submit/html/submission.tpl | 2 +- 6 files changed, 207 insertions(+), 34 deletions(-) create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..a3bfbe41 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> diff --git a/questtypes/submit/SubmitQuesttypeController.inc b/questtypes/submit/SubmitQuesttypeController.inc index 32193bb0..42f98aeb 100644 --- a/questtypes/submit/SubmitQuesttypeController.inc +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -32,6 +32,7 @@ /** * Save the answers of a Character for a Quest. * + * @throws SubmissionNotValidException * @param array $seminary Current Seminary data * @param array $questgroup Current Questgroup data * @param array $quest Current Quest data @@ -44,8 +45,34 @@ $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]); + if(is_null($characterSubmission) && array_key_exists('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + + // Check file + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException() + ); + } + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + $this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer); } } @@ -73,30 +100,29 @@ * 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 + * @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 Exception $exception Character submission exception */ - public function quest($seminary, $questgroup, $quest, $character) + public function quest($seminary, $questgroup, $quest, $character, $exception) { // 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']); + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + // Pass data to view $this->set('submission', $characterSubmission); - $this->set('wordcount', $wordcount); $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); } diff --git a/questtypes/submit/SubmitQuesttypeModel.inc b/questtypes/submit/SubmitQuesttypeModel.inc index 4f361533..2d0a17e3 100644 --- a/questtypes/submit/SubmitQuesttypeModel.inc +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -19,32 +19,48 @@ */ class SubmitQuesttypeModel extends \hhu\z\QuesttypeModel { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); /** - * Save Character’s submitted text. + * Save Character’s submitted upload. * * @param int $questId ID of Quest * @param int $characterId ID of Character - * @param string $text Submitted text + * @param array $file Submitted upload */ - public function setCharacterSubmission($questId, $characterId, $text) + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record $this->db->query( 'INSERT INTO questtypes_submit_characters '. - '(quest_id, character_id, text) '. + '(quest_id, character_id, upload_id) '. 'VALUES '. '(?, ?, ?) ', - 'iis', - $questId, $characterId, $text + 'iii', + $questId, $characterId, $uploadId ); + + + return true; } /** - * Get text submitted by Character. + * Get upload submitted by Character. * * @param int $questId ID of Quest * @param int $characterId ID of Character @@ -53,20 +69,38 @@ public function getCharacterSubmission($questId, $characterId) { $data = $this->db->query( - 'SELECT created, text '. + 'SELECT upload_id '. 'FROM questtypes_submit_characters '. 'WHERE quest_id = ? AND character_id = ?', 'ii', $questId, $characterId ); if(!empty($data)) { - return $data[0]; + return $this->Uploads->getUploadById($data[0]['upload_id']); } return null; } + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + } ?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl index b170ef97..34e30f7c 100644 --- a/questtypes/submit/html/quest.tpl +++ b/questtypes/submit/html/quest.tpl @@ -1,14 +1,25 @@ - -
                                - - - 1) : ?> -
                                + +

                                + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + -
                                + getNestedException()->getMessage()?> - (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) - +

                                + + + +
                                + Erlaubte Dateiformate: +
                                  + +
                                • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                                • + +
                                - + + (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 index 77403dc9..b3d89275 100644 --- a/questtypes/submit/html/submission.tpl +++ b/questtypes/submit/html/submission.tpl @@ -1,5 +1,5 @@
                                - + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>)

                                From 1a11d220a9da1616efb8975370cf83965249e182 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 13:51:24 +0200 Subject: [PATCH 167/340] add FileUploadException --- app/exceptions/FileUploadException.inc | 75 +++++++++++++++++++ .../submit/SubmitQuesttypeController.inc | 17 ++++- questtypes/submit/html/quest.tpl | 2 + 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 app/exceptions/FileUploadException.inc diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/questtypes/submit/SubmitQuesttypeController.inc b/questtypes/submit/SubmitQuesttypeController.inc index 42f98aeb..eecafb3b 100644 --- a/questtypes/submit/SubmitQuesttypeController.inc +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -49,6 +49,13 @@ { $answer = $_FILES['answers']; + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + // Check mimetype $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); $answerMimetype = null; @@ -58,13 +65,13 @@ break; } } - - // Check file if(is_null($answerMimetype)) { throw new \hhu\z\exceptions\SubmissionNotValidException( new \hhu\z\exceptions\WrongFiletypeException() ); } + + // Check file size if($answer['size'] > $answerMimetype['size']) { throw new \hhu\z\exceptions\SubmissionNotValidException( new \hhu\z\exceptions\MaxFilesizeException() @@ -72,7 +79,11 @@ } // Save file - $this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer); + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } } } diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl index 34e30f7c..47b45bd4 100644 --- a/questtypes/submit/html/quest.tpl +++ b/questtypes/submit/html/quest.tpl @@ -4,6 +4,8 @@ getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> getNestedException()->getMessage()?> From 992ce8a37c5797419255da5bfca16aa8aef3b043 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 14:00:01 +0200 Subject: [PATCH 168/340] add type to WrongFiletypeException --- app/exceptions/WrongFiletypeException.inc | 30 +++++++++++++++++-- .../submit/SubmitQuesttypeController.inc | 2 +- questtypes/submit/html/quest.tpl | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc index a3bfbe41..131356b8 100644 --- a/app/exceptions/WrongFiletypeException.inc +++ b/app/exceptions/WrongFiletypeException.inc @@ -30,7 +30,14 @@ * * @var string */ - const MESSAGE = 'File has wrong type'; + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; @@ -38,12 +45,29 @@ /** * Construct a new exception. */ - function __construct($message=self::MESSAGE, $code=self::CODE) + function __construct($type, $message=self::MESSAGE, $code=self::CODE) { parent::__construct( $message, - $code + $code, + $type ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; } } diff --git a/questtypes/submit/SubmitQuesttypeController.inc b/questtypes/submit/SubmitQuesttypeController.inc index eecafb3b..18e55d1b 100644 --- a/questtypes/submit/SubmitQuesttypeController.inc +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -67,7 +67,7 @@ } if(is_null($answerMimetype)) { throw new \hhu\z\exceptions\SubmissionNotValidException( - new \hhu\z\exceptions\WrongFiletypeException() + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) ); } diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl index 47b45bd4..06dc273d 100644 --- a/questtypes/submit/html/quest.tpl +++ b/questtypes/submit/html/quest.tpl @@ -1,7 +1,7 @@

                                getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> - + getNestedException()->getType())?> getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> From 06e0bbc5ab128a3d02a8da86126b2743e70dc0dc Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 4 Apr 2014 14:00:31 +0200 Subject: [PATCH 169/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 3145 -> 3473 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 47 +++++++++++++++----- questtypes/submit/html/quest.tpl | 2 +- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 45facaa0014bb346c481d2538920488b4916d59b..e430458c2b82f7fb475e746707128fa76f22b40d 100644 GIT binary patch delta 1661 zcmZvcOKenC7{^bsWoQcm6$-XK4%8OPORykx97AeaFoZ{9WZ{A|!^~;thC4HO?+jED zost;bxS*K`nlKU#1lZ`JQ6V%TrbdIBz=n+rHH{k#2@91S3BUi$Az|T{`<>r8=brOD z@664WQ{9!{JDNW+$ZP0x=)Xol06#%F_zOM@|MUBc=Qje`0NFBwFadY^^=`;x z_Aw}+x1j3JE-;8*ZvcnxlbccC)tA_@iE z2X%itv@iu_=Ma1vW}xguPyxNK_xLxbm{1DyF~p`h<5`0$=>@;O3gzGil*1qV`VN$% z-=G}*31#O#l-&nVm6TUG?qc|bHuu1a4Bh9{avdN%tvjK5y9O%aoeaz19=H;YLiKP0 zE`_I{O8qHR?=M0H{tZ+hH(@ut4Q2lx?1T^7sQ(%!%c!GDnPN~TZ$i$P{eJxpl%Y{5 z2V?&IF{q4BLS=Ru>i%a?=gvdj``Yh+>(?`$v+dMh7k*}A3%m`Ja31L>($_urL#k*- zp}v7}sC#E2S4<7c;T6bZt}!T}AN~FBR{_BZ&=ihk4Ttv(R-d@b6Gsx6z*=TT+2 z9eph8{Y0f#)~eVx6yJ5xUx&Vvm(W2}73)JSDtWO1)1%-2lcJt?p=yP)mMGg7Q2jwk zcA)d>Gyl;tSLGxd(O1z|&{gPGRHBbf|1IlKRZTL0w#ZXIlTE1ltyY+!OfGQ8T;3H* zu^9@(;&GR?V?pR_>Aj<_I@A0`CW?xY&6cB}Fy44mJ{lI&*`)1_&B)y3)V_2OZXC*H zBNxYJm#(^rjC0x8#=!|^^XZ8oU(Pq)~Ol|g0W(hht*W;^~x@n z3teOnyCC0q`TxNxR%eC-Z@~s}$(|_N@u)a^-V9@)ZkQMe@pFZEu70EtWO60z8s5!F mP#Bw?$wlSDxYecpvMk0T=AFTE)?0W=3oY#{8 delta 1357 zcmYM!OGs2v9LMo9r8DDIJ&XyXTS0|T^2ZF>y;wC|$^o41+jrokcD)&utsNL-kui=4e)s%Y5@p z8nZ@434h`t{DXB^O)stB9BP7ns07zBg13-Y%@fSVDO8>mYC?1N_xGq3e?s+Nvd@>% z&-i9l6Ep>r~%7R16HB>HRDcfLk--8A34VJsQxvKF56H?cM3Jp ziYC;qC`6JX3yg((IK^ARZS>NCe+KZ@utEe6PVSoRNT1bH8 z%Ad_mc}kL0N~n~h5*|a2&g>&JAtm9F*+m>CG>LRQHNLK$S{D|BO@)5PQ5DXNY@=JfcR=+IuebJJ*~cXE@U5WJ?-Qw
                                - Erlaubte Dateiformate: + :

                                • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                                • From 9e0dd674b26e3b1e705f0388dd428208318458b7 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:26:27 +0200 Subject: [PATCH 170/340] implement component ?Validation? for validating user input --- .../components/ValidationComponent.inc | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 controllers/components/ValidationComponent.inc diff --git a/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..8034e47d --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,113 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param string $name Name of the field to validate against + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $name) + { + $settings = $this->config[$name]; + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + $param = $params[$index]; + $check = $this->validate($param, $index); + if($check !== true) { + $validation[$index] = $check; + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + } + +?> From 5dea05942971b2e61c83b38ce499af8e95d8d4cb Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:28:46 +0200 Subject: [PATCH 171/340] load Controller Components of parent classes --- core/Controller.inc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/Controller.inc b/core/Controller.inc index c2814e99..941e7523 100644 --- a/core/Controller.inc +++ b/core/Controller.inc @@ -325,6 +325,15 @@ if(!is_array($components)) { $components = array($components); } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } // Load components foreach($components as &$component) From f3602d7a9e526c3e7c070b782af6b70917fb1a5a Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:29:49 +0200 Subject: [PATCH 172/340] implement user registration and improve user handling --- configs/AppConfig.inc | 34 ++++++++++++- controllers/UsersController.inc | 76 +++++++++++++++++++++++++++- models/UsersModel.inc | 61 +++++++++++++++++------ views/html/users/create.tpl | 8 ++- views/html/users/edit.tpl | 8 ++- views/html/users/login.tpl | 13 +++-- views/html/users/register.tpl | 88 +++++++++++++++++++++++++++++++++ 7 files changed, 260 insertions(+), 28 deletions(-) create mode 100644 views/html/users/register.tpl diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 541a51e2..eade2271 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -67,6 +67,38 @@ ); + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ) + ); + + /** * Routes * @@ -76,7 +108,7 @@ 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('users/(?!(index|login|register|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/ diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index f6203cc6..04d1f016 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -37,6 +37,12 @@ * @var array */ public $models = array('users', 'characters'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); @@ -102,12 +108,12 @@ $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)); } } @@ -119,6 +125,68 @@ } + /** + * Action: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + /** * Action: logout. * @@ -146,6 +214,8 @@ // Create new user $userId = $this->Users->createUser( $this->request->getPostParam('username'), + $this->request->getPostParam('prename'), + $this->request->getPostParam('surname'), $this->request->getPostParam('email'), $this->request->getPostParam('password') ); @@ -180,6 +250,8 @@ $this->Users->editUser( $user['id'], $this->request->getPostParam('username'), + $this->request->getPostParam('prename'), + $this->request->getPostParam('surname'), $this->request->getPostParam('email'), $this->request->getPostParam('password') ); diff --git a/models/UsersModel.inc b/models/UsersModel.inc index d42db7c7..88a7ed2d 100644 --- a/models/UsersModel.inc +++ b/models/UsersModel.inc @@ -132,22 +132,48 @@ * @param string $password Password of the user to create * @return int ID of the newly created user */ - public function createUser($username, $email, $password) + public function createUser($username, $prename, $surname, $email, $password) { - $this->db->query( - 'INSERT INTO users '. - '(username, url, email, password) '. - 'VALUES '. - '(?, ?, ?, ?)', - 'ssss', - $username, - \nre\core\Linker::createLinkParam($username), - $email, - $this->hash($password) - ); + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); - return $this->db->getInsertId(); + return $userId; } @@ -160,17 +186,20 @@ * @param string $email Changed e‑mail-address of user * @param string $password Changed plaintext password of user */ - public function editUser($userId, $username, $email, $password) + public function editUser($userId, $username, $prename, $surname, $email, $password) { + $this->db->setAutocommit(false); try { // Update user data $this->db->query( 'UPDATE users '. - 'SET username = ?, url = ?, email = ? '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. 'WHERE id = ?', - 'sssi', + 'sssssi', $username, \nre\core\Linker::createLinkParam($username), + $prename, + $surname, $email, $userId ); diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl index 8536804d..d4f38bb7 100644 --- a/views/html/users/create.tpl +++ b/views/html/users/create.tpl @@ -5,8 +5,12 @@

                                  - -
                                  + +
                                  + +
                                  + +

                                  diff --git a/views/html/users/edit.tpl b/views/html/users/edit.tpl index ef47268c..56ba5d7b 100644 --- a/views/html/users/edit.tpl +++ b/views/html/users/edit.tpl @@ -5,8 +5,12 @@

                                  - -
                                  + +
                                  + +
                                  + +

                                  diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl index 36c13da4..932b4c20 100644 --- a/views/html/users/login.tpl +++ b/views/html/users/login.tpl @@ -1,12 +1,15 @@

                                  -

                                  +

                                  + +

                                  .

                                  +
                                  - -
                                  - -
                                  + +
                                  + +
                                  diff --git a/views/html/users/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..5ba5671e --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,88 @@ +

                                  + +

                                  + +
                                    + &$settings) : ?> + +
                                  • +
                                      + $value) : ?> +
                                    • + getMessage(); + break; + } ?> +
                                    • + +
                                    +
                                  • + + +
                                  + +
                                  +
                                  + + />
                                  + + />
                                  + + />
                                  + + />
                                  + + />
                                  +
                                  + +
                                  From 1126a4267fdf8d14ce572f3564a0a96ea9e0bb8c Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:30:19 +0200 Subject: [PATCH 173/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 3473 -> 5155 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 322 +++++++++++++------- 2 files changed, 205 insertions(+), 117 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index e430458c2b82f7fb475e746707128fa76f22b40d..0316091a023312436b2b5ba0812a310a08c95cbb 100644 GIT binary patch literal 5155 zcmai$ZHQe*8ONt@T32f{wzk!@PHVSjt#@CNw23#ycC&A;*(A4{O)Zfs=iW1W@3`ll zb2~FQ@f!Ga!CqG4SIc*Nee>z#JR~zYKET7r^&| zFM-_G-$VVsAmt@CLH%dIw}VGOuKO5xJs5)!6&j>o2BiEGfoDMK_c?G3{1P}0{tTqu z{tZ&U>mWpZZU(8(E|B^>6zUa_`Z$p5J`GCnS&-}f1bi#_B8Z>(4PMmik08gr3UZyB zuqf+yg0#aF$nhG4Dk1~9zqP;(kmvqXs6PX8{jY(X@9UxdLy+_Q7^EJXAm{lt$ayb- zJm}!)a~-e+RPu50L)%Kal&m6_ef!-VM@k_JimW6_Drh36S=0 zfwbFaKU$pC4SpM>-miev z`=5ck5G1Jg95@CZ2RZ)}AnkSrq@HI%>h&D>0q{HEo#5}mQSegW2o8zsJ_>SN1kzs~ z5BxMpzc>R@?irB&cOHaF;+r7le*|*BFM?e67a;Y#0P@`b9QYbY`P<=4a6j=8l)F&q z3&&BmKEr6>xnh{Np9J~bfkJz8?gvo#94jC`2-0_Xu6t3AqVO35f`e+ltnko%!e9tmw91xHY52R^hveVpgbv*iQxizyVw zWZH2SrG&zV=edAFf28es{(Qy?_&4w3bg1)O_|V>+=RtfNK)D}f9%Ua2AKu*=6rLgD z9Pi4tDD)lr@Oz9wr8tyWc2rOt}UB+1$;mdiR((yg_W6;qAKL^Y?3 zoHJS8+TI`^&XvX3t{^^*ikye^dg!w?qgzgAshEki z>q+^VmzgmpGs3@RoEx3i3$623l0|V@4qGvEapP<=(us`X*eJWbLF#lhN^~q{xnFg< zrc`XD)$2+&qtm*XH;bM|WM$iASb{c@7dOrg+lw1t5%Z}tS)A9rq6@HMA{KPjaJvgx zU8lucE<*=jRkPP%rOPszP^H~abyN<=vWAng zBYmR^A=}7|gOpA$lQJreno3j(%37AXNT*inBvJJ!@w54Jw_TZzwK`+;AT7eYLFURz zc+V03jicrsvSqxB7AqE`bVV$xy0&n?@L?iv?8PjBZ!YG1pD0{R^RtoVNi1EX=UCm- zl5ol-S(8;IWAA(MgZ=U}eNT4xupPO|fVPZM+lE3X@@i9COU z?bZj_bc@WDZMqcM6~UK`66`o$lQyr_;Qh-vcW<*~6~4HnU82NN=5W$2lhyt8I0+Nf zi%ZT=zn9L(z|C}pI=ksL4A{$sSDKGY zk3daKQ`_&l^b zm3JMI6C($E^PI?XQ&Y?`R=Kh)^y{v2LPe(5s9>Gacv-G$)wX>@dz}+y`8e~HPi{lZ zA9;$ivIw5bum~EGD6b+h@uc)J6a8wHEVCr3Efb8bp3S!H8C+BMTbJ|zo=^+Etp^=_dl6sF+;sn|da*AW~Ed00u7XLceYKt87v)3CAg}0EeMc_^1;KNsb4==;h~?IcJwA8)jf1 KT+(~F6aNS889bE$ delta 1422 zcmYk+OGs2v9LMpa`IyleAERk$n&!K7)O=5dvENaX?LaL&j2`TB4x;*>!gS_0 zXQ*(QQEqVMUB&qDYzdsPCsfvC-ryc zH^Wp&g*k_eX-1t%9_Qhgb9_BT)qzk^!HOU%Vt)cD^p z3;!gM|1v6>2BRaC#*?)hEU ze(0P|CjaX2iU+kgiy@3RXD5Qns5TS%oH~*=(&6T7IhS# zoO7s*e~(bni(c}nvkjn9nvLpEf!aYG@~R1;7SfJNX_xab>ix6G(JmAnfu1cD^h2^D>8`fXWD zC~GQpM7)+?K_y7&ygl|=!WUo1-a~y=n|gZs4-5?0H;KD#qbF!LdrRzT?*%)SwA~&_ zexF!hP+J#jXto``LRW9GgTBYM)xXah3|22*_KH96Uu*J#KR?F)PT6Bmq=xKtTCt5y NAGhs+Ub{16<}dVKd)5E| diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 750490c6..d02466d2 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-04 13:58+0100\n" -"PO-Revision-Date: 2014-04-04 13:59+0100\n" +"POT-Creation-Date: 2014-04-05 13:30+0100\n" +"PO-Revision-Date: 2014-04-05 13:30+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -10,303 +10,394 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.6.4\n" -"X-Poedit-Basepath: ./\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" -"X-Poedit-SearchPath-1: ../../../questtypes\n" +"X-Poedit-SearchPath-0: views\n" +"X-Poedit-SearchPath-1: questtypes\n" -#: ../../../questtypes/dragndrop/html/quest.tpl:16 -#: ../../../questtypes/multiplechoice/html/quest.tpl:19 -#: ../../../questtypes/submit/html/quest.tpl:23 -#: ../../../questtypes/textinput/html/quest.tpl:10 +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 msgid "solve" msgstr "lösen" -#: ../../../questtypes/multiplechoice/html/quest.tpl:3 +#: questtypes/multiplechoice/html/quest.tpl:3 #, php-format msgid "Question %d of %d" msgstr "Frage %d von %d" -#: ../../../questtypes/multiplechoice/html/quest.tpl:17 +#: questtypes/multiplechoice/html/quest.tpl:17 msgid "solve Question" msgstr "Frage lösen" -#: ../../../questtypes/submit/html/quest.tpl:4 +#: questtypes/submit/html/quest.tpl:4 #, php-format msgid "File has wrong type “%s”" msgstr "Der Dateityp „%s“ ist nicht erlaubt" -#: ../../../questtypes/submit/html/quest.tpl:6 +#: questtypes/submit/html/quest.tpl:6 msgid "File exceeds size maximum" msgstr "Die Datei ist zu groß" -#: ../../../questtypes/submit/html/quest.tpl:8 +#: questtypes/submit/html/quest.tpl:8 #, php-format msgid "Error during file upload: %s" msgstr "Fehler beim Dateiupload: %s" -#: ../../../questtypes/submit/html/quest.tpl:17 +#: questtypes/submit/html/quest.tpl:17 msgid "Allowed file types" msgstr "Erlaubte Dateiformate" -#: ../../../questtypes/submit/html/quest.tpl:20 +#: questtypes/submit/html/quest.tpl:20 msgid "max." msgstr "max." -#: ../../../questtypes/submit/html/quest.tpl:26 -#: ../../../questtypes/submit/html/submission.tpl:2 +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 #, php-format msgid "submitted at %s on %s h" msgstr "eingereicht am %s um %s Uhr" -#: ../../../questtypes/submit/html/submission.tpl:6 -#: ../../../questtypes/submit/html/submission.tpl:8 -#: ../../../views/html/quests/quest.tpl:40 -#: ../../../views/html/quests/submissions.tpl:33 +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:40 +#: views/html/quests/submissions.tpl:33 msgid "solved" msgstr "gelöst" -#: ../../../questtypes/submit/html/submission.tpl:9 -#: ../../../views/html/quests/quest.tpl:42 -#: ../../../views/html/quests/submissions.tpl:24 +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:42 +#: views/html/quests/submissions.tpl:24 msgid "unsolved" msgstr "ungelöst" -#: ../../../views/binary/error/index.tpl:1 -#: ../../../views/html/error/index.tpl:1 +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 msgid "Error" msgstr "Fehler" -#: ../../../views/html/charactergroups/group.tpl:5 -#: ../../../views/html/charactergroups/groupsgroup.tpl:2 -#: ../../../views/html/charactergroups/index.tpl:2 -#: ../../../views/html/characters/character.tpl:51 -#: ../../../views/html/seminarymenu/index.tpl:4 +#: views/html/charactergroups/group.tpl:5 +#: views/html/charactergroups/groupsgroup.tpl:2 +#: views/html/charactergroups/index.tpl:2 +#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:4 msgid "Character Groups" msgstr "Charaktergruppen" -#: ../../../views/html/charactergroups/group.tpl:22 -#: ../../../views/html/characters/character.tpl:2 -#: ../../../views/html/characters/index.tpl:2 -#: ../../../views/html/seminarymenu/index.tpl:3 -#: ../../../views/html/users/user.tpl:11 +#: views/html/charactergroups/group.tpl:22 +#: views/html/characters/character.tpl:2 views/html/characters/index.tpl:2 +#: views/html/seminarymenu/index.tpl:3 views/html/users/user.tpl:11 msgid "Characters" msgstr "Charaktere" -#: ../../../views/html/charactergroups/group.tpl:60 -#: ../../../views/html/questgroups/questgroup.tpl:43 +#: views/html/charactergroups/group.tpl:60 +#: views/html/questgroups/questgroup.tpl:43 msgid "Quests" msgstr "Quests" -#: ../../../views/html/charactergroups/groupsgroup.tpl:12 -#: ../../../views/html/charactergroupsquests/quest.tpl:3 +#: views/html/charactergroups/groupsgroup.tpl:12 +#: views/html/charactergroupsquests/quest.tpl:3 msgid "Character Groups Quests" msgstr "Charactergruppen-Quests" -#: ../../../views/html/charactergroupsquests/quest.tpl:12 +#: views/html/charactergroupsquests/quest.tpl:12 msgid "Description" msgstr "Beschreibung" -#: ../../../views/html/charactergroupsquests/quest.tpl:15 +#: views/html/charactergroupsquests/quest.tpl:15 msgid "Rules" msgstr "Regeln" -#: ../../../views/html/charactergroupsquests/quest.tpl:22 +#: views/html/charactergroupsquests/quest.tpl:22 msgid "Won Quest" msgstr "Gewonnene Quest" -#: ../../../views/html/charactergroupsquests/quest.tpl:28 +#: views/html/charactergroupsquests/quest.tpl:28 msgid "Lost Quest" msgstr "Verlorene Quest" -#: ../../../views/html/characters/character.tpl:11 +#: views/html/characters/character.tpl:11 msgid "Total progress" msgstr "Fortschritt" -#: ../../../views/html/characters/character.tpl:15 -#: ../../../views/html/characters/index.tpl:6 -#: ../../../views/html/users/user.tpl:14 +#: views/html/characters/character.tpl:15 views/html/characters/index.tpl:6 +#: views/html/users/user.tpl:14 msgid "Level" msgstr "Level" -#: ../../../views/html/characters/character.tpl:23 +#: views/html/characters/character.tpl:23 msgid "Rank" msgstr "Platz" -#: ../../../views/html/characters/character.tpl:26 -#: ../../../views/html/seminarymenu/index.tpl:5 +#: views/html/characters/character.tpl:26 views/html/seminarymenu/index.tpl:5 msgid "Achievements" msgstr "Errungenschaften" -#: ../../../views/html/introduction/index.tpl:1 +#: views/html/introduction/index.tpl:1 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 +#: 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/register.tpl:1 views/html/users/user.tpl:1 msgid "Users" msgstr "Benutzer" -#: ../../../views/html/menu/index.tpl:4 +#: views/html/menu/index.tpl:4 msgid "Usergroups" msgstr "Benutzergruppen" -#: ../../../views/html/menu/index.tpl:5 -#: ../../../views/html/seminaries/create.tpl:1 -#: ../../../views/html/seminaries/delete.tpl:1 -#: ../../../views/html/seminaries/edit.tpl:1 -#: ../../../views/html/seminaries/index.tpl:1 +#: views/html/menu/index.tpl:5 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:7 ../../../views/html/users/login.tpl:2 -#: ../../../views/html/users/login.tpl:11 +#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 msgid "Login" msgstr "Login" -#: ../../../views/html/menu/index.tpl:9 +#: views/html/menu/index.tpl:9 msgid "Logout" msgstr "Logout" -#: ../../../views/html/quests/quest.tpl:50 +#: views/html/quests/quest.tpl:50 msgid "Task" msgstr "Aufgabe" -#: ../../../views/html/quests/quest.tpl:55 +#: views/html/quests/quest.tpl:55 msgid "Task already successfully solved" msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" -#: ../../../views/html/quests/quest.tpl:57 +#: views/html/quests/quest.tpl:57 msgid "Show answer" msgstr "Lösung anzeigen" -#: ../../../views/html/quests/quest.tpl:71 -#: ../../../views/html/quests/quest.tpl:80 +#: views/html/quests/quest.tpl:71 views/html/quests/quest.tpl:80 msgid "Quest" msgstr "Quest" -#: ../../../views/html/quests/submission.tpl:14 +#: views/html/quests/submission.tpl:14 #, php-format msgid "Submission of %s" msgstr "Lösungen von %s" -#: ../../../views/html/quests/submissions.tpl:15 +#: views/html/quests/submissions.tpl:15 msgid "submitted" msgstr "eingereicht am %s um %s Uhr" -#: ../../../views/html/seminaries/create.tpl:2 +#: 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 +#: 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 +#: views/html/seminaries/create.tpl:9 views/html/users/create.tpl:17 msgid "create" msgstr "erstellen" -#: ../../../views/html/seminaries/delete.tpl:2 -#: ../../../views/html/seminaries/seminary.tpl:39 +#: views/html/seminaries/delete.tpl:2 views/html/seminaries/seminary.tpl:39 msgid "Delete seminary" msgstr "Kurs löschen" -#: ../../../views/html/seminaries/delete.tpl:4 +#: 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 +#: 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 +#: 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:38 +#: views/html/seminaries/edit.tpl:2 views/html/seminaries/seminary.tpl:38 msgid "Edit seminary" msgstr "Kurs bearbeiten" -#: ../../../views/html/seminaries/edit.tpl:9 -#: ../../../views/html/users/edit.tpl:13 +#: views/html/seminaries/edit.tpl:9 views/html/users/edit.tpl:17 msgid "save" msgstr "speichern" -#: ../../../views/html/seminaries/index.tpl:3 +#: 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:42 +#: views/html/seminaries/index.tpl:10 views/html/seminaries/seminary.tpl:42 #, php-format msgid "created by %s on %s" msgstr "erstellt von %s am %s" -#: ../../../views/html/seminarymenu/index.tpl:6 +#: views/html/seminarymenu/index.tpl:6 msgid "Library" msgstr "Bibliothek" -#: ../../../views/html/users/create.tpl:2 +#: 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 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 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" +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:80 views/html/users/register.tpl:81 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +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 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 msgid "Password" msgstr "Passwort" -#: ../../../views/html/users/delete.tpl:2 ../../../views/html/users/user.tpl:5 +#: 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 +#: 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 +#: views/html/users/edit.tpl:2 views/html/users/user.tpl:4 msgid "Edit user" msgstr "Benutzer bearbeiten" -#: ../../../views/html/users/index.tpl:3 +#: 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 +#: 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 +#: views/html/users/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:15 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:17 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:19 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:21 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:26 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:28 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:30 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:32 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:37 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:39 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:41 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:43 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:48 views/html/users/register.tpl:50 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:55 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:57 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:59 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:87 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:18 msgid "Roles" msgstr "Rollen" +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + #~ msgid "Words" #~ msgstr "Wörter" @@ -340,6 +431,3 @@ msgstr "Rollen" #~ msgid "created by %s on %s at %s" #~ msgstr "erstellt von %s am %s um %s Uhr" - -#~ msgid "registered on" -#~ msgstr "registriert seit" From d63cf049081e4dbadfa39dc1cf4fa043ee9c72f1 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:31:55 +0200 Subject: [PATCH 174/340] set accepted mimetypes and required-flag for file uploads for Questtype ?Submit? --- questtypes/submit/html/quest.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl index d768dbd3..0c72c1de 100644 --- a/questtypes/submit/html/quest.tpl +++ b/questtypes/submit/html/quest.tpl @@ -13,7 +13,7 @@
                                  -
                                  +
                                  :
                                    From 9854f124f0ab6308554e6d859a119ee3dc014db2 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:40:38 +0200 Subject: [PATCH 175/340] add login form and registration link to introduction page --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 5155 -> 5238 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 56 +++++++++++--------- views/html/introduction/index.tpl | 15 +++++- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 0316091a023312436b2b5ba0812a310a08c95cbb..c7c0f88cbd25e2f99a9e433c21011571a283ba5a 100644 GIT binary patch delta 1865 zcmX}sOKenC7{KwbT3fZHl)fwoc6dpVN2|6}YDHd!f(5a0LtL03gA{3+OiLoN7!$!& zUDSq<_zW>YOn5B1z!-yA6BZ3Y!oui+CL}(H1Op3M7()2}XHGqt@BYrYbLX7zJnnqe ze78A2Ra1I2D1F3S;?zSC1K9Nl7s~Cjh)TSJHFzJZaaMW66Sx2$$4;!pZk&lbll@ph z`(W}2&Y(Stk45C;ODc}_4Vu7Nbbw3Ph}Up7-c8?EKUz#^1^T;Ak~gz*M)8N$=s@kMy$M~3chC+-k(}ZfI>Y11ljz=`P3@o11SXN= z6hEUYa~19X_w@b073{wq|Hln8bA9Z1COS|9y7w70(N4Tj60rg8sFWYoV?7pD2tAZt z=nP-SI(!p7OT*~l`v^_w(<;`VzTz}DT*~jznM|UIOyQIG2Rc9r4=unNY`}J0g1yKb zVi=v!`)C3mruHZ{(LRRudk*=;`8-!2yo`(yzo9d|fphQ; zaVefk{(>grH0dJB(19D!`}5F==ND3ONm|nbYm(jQgT44H_F*R;M*5B)l2_1#uVXX* zjV4~ppM?ppOzuPzKa4Fng7lw{@2R+S6KJNF(affBIo`ylu#tUPi5czxMgk!As{dbT}gjw35`?-#oE?ueODlk@|tl{dcUnw>V*Nt3# z-L?=e{ZeAO_4|66;!qYWXtPTxRK#f-n>Wzl=Ui#fTKSTQCI0 z4>A-?3Dk*2=m%F2NF{pbLLSc#uv6@Ht(zlkPPR6O=xIdUs1Q@a-ZejburEJ71p zp6+)dpV*W$i+F>I18&E;_!idT`)J3P@JYOi&g}2hzK=d%%QT!oCq9Pj(Sf&MA#O(= zjUF_yUFh?fGJf<^F~ft{gh#O%ze1PpUo^o29x|b+XhPL!LaS4|8%=08I`A>9#ShQ{ zzre|O1^L7eTrxBJg^DlUL3Udm%I*3FK-=HyJnpV0Pg^bkHoXEdE&r{Wy+ z?6e_YMK`)NThT4pfv()X$*jMbAL53IoWz;Od1% zm)fu56SOy@{q8|N(Z|K-4kKN~iHwRf{TOH9CpaI!OOB#@UPm?#ybxWX*RT+>4%# ze)PHH=ph_La*B`8=P#lYzJjHUAKy?h(-CyQD0d)OKqut~g~=?z)QWQerW&j%Xz;{)RgV_tAeme=_-m2gt+u3{gPL lB~}oY1w*&S-6$ORqvT9p^T2`9`n=|$3#F~&2kuwwc?5?iiLU?v diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index d02466d2..3ea25979 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-05 13:30+0100\n" -"PO-Revision-Date: 2014-04-05 13:30+0100\n" +"POT-Creation-Date: 2014-04-05 13:37+0100\n" +"PO-Revision-Date: 2014-04-05 13:39+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -133,6 +133,36 @@ msgstr "Errungenschaften" msgid "Introduction" msgstr "Einführung" +#: views/html/introduction/index.tpl:4 views/html/introduction/index.tpl:12 +#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:7 views/html/introduction/index.tpl:8 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:9 views/html/introduction/index.tpl:10 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:13 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:13 +msgid "register yourself" +msgstr "registriere dich" + #: 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 @@ -150,11 +180,6 @@ msgstr "Benutzergruppen" msgid "Seminaries" msgstr "Kurse" -#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 -#: views/html/users/login.tpl:14 -msgid "Login" -msgstr "Login" - #: views/html/menu/index.tpl:9 msgid "Logout" msgstr "Logout" @@ -239,13 +264,6 @@ msgstr "Bibliothek" 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:9 views/html/users/login.tpl:10 -#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 -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 #: views/html/users/register.tpl:78 views/html/users/register.tpl:79 @@ -264,13 +282,6 @@ msgstr "Nachname" msgid "E‑mail address" msgstr "E‑Mail-Adresse" -#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 -#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 -#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 -#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 -msgid "Password" -msgstr "Passwort" - #: views/html/users/delete.tpl:2 views/html/users/user.tpl:5 msgid "Delete user" msgstr "Benutzer löschen" @@ -401,9 +412,6 @@ msgstr "Rollen" #~ msgid "Words" #~ msgstr "Wörter" -#~ msgid "Word" -#~ msgstr "Wort" - #~ msgid "Go on" #~ msgstr "Hier geht es weiter" diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index 90ca59f7..007895e4 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -1,6 +1,19 @@

                                    Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.

                                    -

                                    Entwickler:

                                    + +

                                    + +
                                    + +
                                    + +
                                    +
                                    + + + + +

                                    Entwickler

                                    • Oliver Hanraths
                                      From 2204289c4dff6bc62552e6a65eb30f80368c70c0 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 13:47:15 +0200 Subject: [PATCH 176/340] show login form on introduction page only when user is not logged in --- controllers/IntroductionController.inc | 2 ++ views/html/introduction/index.tpl | 2 ++ 2 files changed, 4 insertions(+) diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc index 5fead9cf..4607afb5 100644 --- a/controllers/IntroductionController.inc +++ b/controllers/IntroductionController.inc @@ -28,6 +28,8 @@ */ public function index() { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); } } diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index 007895e4..ef919187 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -1,6 +1,7 @@

                                      Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.

                                      +

                                      @@ -12,6 +13,7 @@ +

                                      Entwickler

                                        From 04e0cfc81e96fb561d0efe0d80eefa94d5e96de1 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 15:37:11 +0200 Subject: [PATCH 177/340] split media and Seminary media --- configs/AppConfig.inc | 11 +-- .../CharactergroupsquestsController.inc | 2 +- controllers/MediaController.inc | 75 ++++++++++++++++--- controllers/QuestgroupsController.inc | 2 +- controllers/QuestsController.inc | 6 +- controllers/SeminariesController.inc | 2 +- models/CharactersModel.inc | 30 ++++---- models/MediaModel.inc | 64 ++++++++++++++-- .../DragndropQuesttypeController.inc | 8 +- questtypes/dragndrop/html/quest.tpl | 4 +- questtypes/dragndrop/html/submission.tpl | 6 +- seminarymedia/empty | 0 views/html/charactergroupsquests/quest.tpl | 2 +- views/html/characters/character.tpl | 3 +- views/html/questgroups/questgroup.tpl | 2 +- views/html/quests/quest.tpl | 4 +- views/html/seminaries/seminary.tpl | 4 +- 17 files changed, 164 insertions(+), 61 deletions(-) create mode 100644 seminarymedia/empty diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index eade2271..f33044de 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -59,11 +59,12 @@ * @var array */ public static $dirs = array( - 'locale' => 'locale', - 'media' => 'media', - 'questtypes' => 'questtypes', - 'temporary' => 'tmp', - 'uploads' => 'uploads' + 'locale' => 'locale', + 'media' => 'media', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' ); diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc index 52dc3435..fb9d18c8 100644 --- a/controllers/CharactergroupsquestsController.inc +++ b/controllers/CharactergroupsquestsController.inc @@ -74,7 +74,7 @@ // Media $questmedia = null; if(!is_null($quest['questsmedia_id'])) { - $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); } diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 897f2701..3c70a832 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -25,7 +25,8 @@ * @var array */ public $permissions = array( - 'index' => array('admin', 'moderator', 'user') + 'index' => array('admin', 'moderator', 'user', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest') ); /** * User seminary permissions @@ -33,7 +34,7 @@ * @var array */ public $seminaryPermissions = array( - 'index' => array('admin', 'moderator', 'user') + 'seminary' => array('admin', 'moderator', 'user', 'guest') ); /** * Required models @@ -64,21 +65,16 @@ /** - * Action: index. + * Action: index * - * Display a medium without processing. + * Display a medium. * - * @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) + public function index($mediaUrl, $action=null) { - // Get Seminary - $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - // Get Media - $media = $this->Media->getMediaByUrl($seminary['id'], $mediaUrl); + $media = $this->Media->getMediaByUrl($mediaUrl); // Get format $format = explode('/', $media['mimetype']); @@ -98,6 +94,63 @@ 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; + default: + throw new ParamsNotValidException($action); + break; + } + + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($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['seminarymedia'].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) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 92c86be2..d7b8acf1 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -115,7 +115,7 @@ $picture = null; if(!is_null($questgroup['questgroupspicture_id'])) { - $picture = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); } diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index e755fe01..e930cbd6 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -70,7 +70,7 @@ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); $questgroup['picture'] = null; if(!is_null($questgroup['questgroupspicture_id'])) { - $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); } @@ -151,7 +151,7 @@ { // Questtext media if(!is_null($questtext['questsmedia_id'])) { - $questtext['media'] = $this->Media->getMediaById($questtext['questsmedia_id']); + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); } // Related Questgroups @@ -177,7 +177,7 @@ // Quest media $questmedia = null; if(!is_null($quest['questsmedia_id'])) { - $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); } // Task diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 61b83a67..21a781ff 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -127,7 +127,7 @@ // Get Media $questgroup['picture'] = null; try { - $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); } catch(\nre\exceptions\IdNotFoundException $e) { } diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 1f0c1f5e..43406b0a 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -132,12 +132,12 @@ 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 '. + '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, seminarymedia.url AS avatar_url, seminarymedia.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 '. + 'LEFT JOIN avatarpictures ON avatarpictures.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE charactertypes.seminary_id = ? AND characters.url = ?', 'is', $seminaryId, $characterUrl @@ -161,12 +161,12 @@ 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 '. + '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, seminarymedia.url AS avatar_url, seminarymedia.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 '. + 'LEFT JOIN avatarpictures ON avatarpictures.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE characters.id = ?', 'i', $characterId @@ -264,12 +264,12 @@ public function getCharactersSolvedQuest($questId) { return $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 '. + '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, seminarymedia.url AS avatar_url, seminarymedia.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 '. + 'LEFT JOIN avatarpictures ON avatarpictures.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE EXISTS ('. 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. ')', @@ -289,12 +289,12 @@ public function getCharactersUnsolvedQuest($questId) { return $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 '. + '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, seminarymedia.url AS avatar_url, seminarymedia.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 '. + 'LEFT JOIN avatarpictures ON avatarpictures.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE EXISTS ('. 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. ') AND NOT EXISTS ('. @@ -316,12 +316,12 @@ public function getCharactersSubmittedQuest($questId) { return $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 '. + '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, seminarymedia.url AS avatar_url, seminarymedia.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 '. + 'LEFT JOIN avatarpictures ON avatarpictures.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE EXISTS ('. 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. ') AND NOT EXISTS ('. diff --git a/models/MediaModel.inc b/models/MediaModel.inc index 6165f7df..34e0c0e8 100644 --- a/models/MediaModel.inc +++ b/models/MediaModel.inc @@ -35,19 +35,18 @@ /** - * Get a Medium by its URL. + * 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) + public function getMediaByUrl($mediaUrl) { $data = $this->db->query( 'SELECT id, name, url, description, mimetype '. 'FROM media '. - 'WHERE media.url = ?', + 'WHERE url = ?', 's', $mediaUrl ); @@ -61,10 +60,9 @@ /** - * Get a Medium by its ID. + * 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 */ @@ -73,7 +71,59 @@ $data = $this->db->query( 'SELECT id, name, url, description, mimetype '. 'FROM media '. - 'WHERE media.id = ?', + 'WHERE id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary 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 getSeminaryMediaByUrl($seminaryId, $mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $mediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary 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 getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE id = ?', 'i', $mediaId ); diff --git a/questtypes/dragndrop/DragndropQuesttypeController.inc b/questtypes/dragndrop/DragndropQuesttypeController.inc index 2fc0b2b2..90843218 100644 --- a/questtypes/dragndrop/DragndropQuesttypeController.inc +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -112,13 +112,13 @@ { // Get Drag&Drop field $dndField = $this->Dragndrop->getDragndrop($quest['id']); - $dndField['media'] = $this->Media->getMediaById($dndField['questmedia_id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); // Get Drags $drags = array(); $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); foreach($dragsByIndex as &$drag) { - $drag['media'] = $this->Media->getMediaById($drag['questmedia_id']); + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); $drags[$drag['id']] = $drag; } @@ -162,13 +162,13 @@ { // Get Drag&Drop field $dndField = $this->Dragndrop->getDragndrop($quest['id']); - $dndField['media'] = $this->Media->getMediaById($dndField['questmedia_id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); // Get Drags $drags = array(); $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); foreach($dragsByIndex as &$drag) { - $drag['media'] = $this->Media->getMediaById($drag['questmedia_id']); + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); $drags[$drag['id']] = $drag; } diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl index bf8dcfe2..1af4dd6a 100644 --- a/questtypes/dragndrop/html/quest.tpl +++ b/questtypes/dragndrop/html/quest.tpl @@ -1,5 +1,5 @@
                                        -
                                        +
                                        @@ -8,7 +8,7 @@
                                        - +
                                        diff --git a/questtypes/dragndrop/html/submission.tpl b/questtypes/dragndrop/html/submission.tpl index 043d39ac..c136fe1e 100644 --- a/questtypes/dragndrop/html/submission.tpl +++ b/questtypes/dragndrop/html/submission.tpl @@ -1,8 +1,8 @@ -
                                        +
                                        - +
                                        @@ -10,6 +10,6 @@
                                        - +
                                        diff --git a/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl index c3424996..c02a4558 100644 --- a/views/html/charactergroupsquests/quest.tpl +++ b/views/html/charactergroupsquests/quest.tpl @@ -4,7 +4,7 @@
                                        - +
                                        diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index 765b3760..b621cff0 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -40,9 +40,8 @@
                                -
                                - <?=$character['avatar_description']?> + <?=$character['avatar_description']?>
                              diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index aa9bf3e5..4d3ce2b9 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -1,6 +1,6 @@
                              - +

                              diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index ed6d5429..26911951 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -1,6 +1,6 @@
                              - +

                              @@ -16,7 +16,7 @@

                              - +

                              0 || !empty($questtext['abort_text'])) : ?> diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index c85d0a92..2f7638f7 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,6 +1,6 @@
                              - +
                              @@ -13,7 +13,7 @@
                            • - +

                              : From f5f25ad7d2b6083608297bcab082a0eaa434b371 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 15:37:32 +0200 Subject: [PATCH 178/340] ignore uploaded Seminary media --- .hgignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.hgignore b/.hgignore index 7b6a4408..1ce7ce9e 100644 --- a/.hgignore +++ b/.hgignore @@ -2,3 +2,4 @@ syntax: glob media/* tmp/* uploads/* +seminarymedia/* From bfdfa59f65e7ee3170ad0204b4ad76443f9bb29c Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 5 Apr 2014 15:37:54 +0200 Subject: [PATCH 179/340] add Seminary media to list of Seminaries --- controllers/SeminariesController.inc | 5 +++++ models/SeminariesModel.inc | 2 +- views/html/seminaries/index.tpl | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 21a781ff..8294c0f7 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -66,6 +66,11 @@ { // Created user $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Media + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } } diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index eaf778b5..364e82e2 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -49,7 +49,7 @@ { // Get seminaries return $this->db->query( - 'SELECT id, created, created_user_id, title, url, description '. + 'SELECT id, created, created_user_id, title, url, description, media_id '. 'FROM seminaries '. 'ORDER BY created DESC' ); diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index 8ac8c8b0..611fdaf8 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -5,6 +5,9 @@

                              • + + +

                                format(new \DateTime($seminary['created'])))?> From cc048f0b762a2285abc6fbb35f3e042c12da8e57 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 7 Apr 2014 20:54:46 +0200 Subject: [PATCH 180/340] menu mobile fix --- views/html/html.tpl | 10 ++++++---- views/html/menu/index.tpl | 20 +++++++++----------- views/html/seminarymenu/index.tpl | 13 ++++++------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index da2c90b1..91bd00b8 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -38,10 +38,12 @@ - - - - + + + + + +
                                diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 2c3299b4..aff23551 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,11 +1,9 @@ - -
                              • The Legend of Z
                              • - 0) : ?>
                              • -
                              • -
                              • - -
                              • - -
                              • - -
                                +
                              • The Legend of Z
                              • + 0) : ?>
                              • +
                              • +
                              • + +
                              • + +
                              • + diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl index ee29969f..cd1941d1 100644 --- a/views/html/seminarymenu/index.tpl +++ b/views/html/seminarymenu/index.tpl @@ -1,7 +1,6 @@ - -
                              • - 0) : ?>
                              • -
                              • -
                              • -
                              • -
                                +
                              • + 0) : ?>
                              • +
                              • +
                              • +
                              • + From 17946fbcbf3abc9333d93d7889e12192df641d88 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 7 Apr 2014 23:10:15 +0200 Subject: [PATCH 181/340] correctly set and use logged user --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 ++++++++++++ agents/bottomlevel/MenuAgent.inc | 35 + .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 ++ agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 68 ++ apis/WebApi.inc | 250 ++++++++ app/Controller.inc | 103 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 267 ++++++++ app/QuesttypeController.inc | 349 ++++++++++ app/QuesttypeModel.inc | 154 +++++ app/QuesttypeView.inc | 76 +++ app/ToplevelAgent.inc | 36 ++ app/Utils.inc | 83 +++ app/controllers/IntermediateController.inc | 168 +++++ app/controllers/SeminaryRoleController.inc | 136 ++++ app/exceptions/FileUploadException.inc | 75 +++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 +++ .../QuesttypeAgentNotValidException.inc | 77 +++ .../QuesttypeControllerNotFoundException.inc | 77 +++ .../QuesttypeControllerNotValidException.inc | 77 +++ .../QuesttypeModelNotFoundException.inc | 77 +++ .../QuesttypeModelNotValidException.inc | 77 +++ .../SubmissionNotValidException.inc | 77 +++ app/exceptions/WrongFiletypeException.inc | 75 +++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 163 +++++ configs/CoreConfig.inc | 167 +++++ controllers/BinaryController.inc | 37 ++ controllers/CharactergroupsController.inc | 143 +++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 112 ++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 ++ controllers/MediaController.inc | 293 +++++++++ controllers/MenuController.inc | 50 ++ controllers/QuestgroupsController.inc | 179 ++++++ .../QuestgroupshierarchypathController.inc | 90 +++ controllers/QuestsController.inc | 595 +++++++++++++++++ controllers/SeminariesController.inc | 253 ++++++++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 +++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 310 +++++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ .../components/ValidationComponent.inc | 113 ++++ core/Agent.inc | 607 ++++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 433 +++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 5238 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 441 +++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 36 ++ models/CharactergroupsModel.inc | 147 +++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 341 ++++++++++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 140 ++++ models/QuestgroupsModel.inc | 589 +++++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 ++++ models/QuestsModel.inc | 344 ++++++++++ models/QuesttextsModel.inc | 114 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 193 ++++++ models/SeminarycharacterfieldsModel.inc | 58 ++ models/UploadsModel.inc | 148 +++++ models/UserrolesModel.inc | 77 +++ models/UsersModel.inc | 278 ++++++++ models/UserseminaryrolesModel.inc | 78 +++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 199 ++++++ .../dragndrop/DragndropQuesttypeModel.inc | 146 +++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 261 ++++++++ .../MultiplechoiceQuesttypeModel.inc | 159 +++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 +++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 +++++ .../textinput/TextinputQuesttypeModel.inc | 117 ++++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 ++++++++++++ responses/WebResponse.inc | 250 ++++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/charactergroups/group.tpl | 72 +++ views/html/charactergroups/groupsgroup.tpl | 17 + views/html/charactergroups/index.tpl | 8 + views/html/charactergroupsquests/quest.tpl | 46 ++ views/html/characters/character.tpl | 160 +++++ views/html/characters/index.tpl | 8 + views/html/error/index.tpl | 2 + views/html/html.tpl | 52 ++ views/html/introduction/index.tpl | 36 ++ views/html/menu/index.tpl | 11 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 96 +++ views/html/quests/submission.tpl | 17 + views/html/quests/submissions.tpl | 41 ++ views/html/seminaries/create.tpl | 10 + views/html/seminaries/delete.tpl | 8 + views/html/seminaries/edit.tpl | 10 + views/html/seminaries/index.tpl | 17 + views/html/seminaries/seminary.tpl | 43 ++ views/html/seminarymenu/index.tpl | 7 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 88 +++ views/html/users/user.tpl | 19 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 224 +++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 ++++ 207 files changed, 18910 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..1ce7ce9e --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: glob +media/* +tmp/* +uploads/* +seminarymedia/* 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/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..0648160e --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,68 @@ + + * @copyright 2014 Heinrich-Heine-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 menus + $this->addSubAgent('Menu'); + $this->addSubAgent('Seminarymenu'); + } + + + + + 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..5731aeed --- /dev/null +++ b/app/Controller.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; + + + /** + * 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 and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..5b980299 --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,168 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + + // Determine user roles + self::$user['roles'] = array(); + $roles = $this->Userroles->getUserrolesForUserById(self::$user['id']); + foreach($roles as &$role) { + self::$user['roles'][] = $role['name']; + } + + // Character + $controller = $this->agent->controller; + if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) + { + $seminaryUrl = $this->request->getParam(3); + self::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..36a9b439 --- /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\controllers\IntermediateController + { + /** + * 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/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..f33044de --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,163 @@ + + * @copyright 2014 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ) + ); + + + /** + * 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|register|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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..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..fb9d18c8 --- /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->getSeminaryMediaById($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..e64cb63f --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,112 @@ + + * @copyright 2014 Heinrich-Heine-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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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..7bf8859e --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedCharacter', IntermediateController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..3c70a832 --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,293 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * 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. + * + * @param string $mediaUrl URL-name of the medium + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($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; + default: + throw new ParamsNotValidException($action); + break; + } + + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($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['seminarymedia'].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..df01f2ff --- /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', IntermediateController::$user); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..d7b8acf1 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,179 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && $currentQuest['solved']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..e930cbd6 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,595 @@ + + * @copyright 2014 Heinrich-Heine-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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + //$questtexttypesUrls = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) + { + switch($questStatus) + { + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; + } + } + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('queststatustext', $questStatusText); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } + + // 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 + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..8294c0f7 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,253 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + // Media + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + } + } + + // Medium + $media = null; + if(!is_null($seminary['media_id'])) { + $media = $this->Media->getMediaById($seminary['media_id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroupshierarchy', $questgroupshierarchy); + $this->set('media', $media); + } + + + /** + * Action: create. + * + * Create a new seminary. + */ + public function create() + { + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Create new seminary + $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/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..d827425d --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..b1ee5f56 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,310 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..8034e47d --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,113 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param string $name Name of the field to validate against + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $name) + { + $settings = $this->config[$name]; + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + $param = $params[$index]; + $check = $this->validate($param, $index); + if($check !== true) { + $validation[$index] = $check; + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                                \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..c7c0f88cbd25e2f99a9e433c21011571a283ba5a GIT binary patch literal 5238 zcmai$U2I%O700I#S}>(e(>8>UlIe#bCS7}-k0#y3ZJpRoLY>sLH-(mm>h9jz-CN(i zcbS=M$9}Rz%TstTQdJ4kR!B&+QcE6?0HLTrEb&mGf_OmiK%_=|R8*k$0Ul6=`2Xk5 z-rd-ad$n_acV^DaoH=vmoV~JR+w%g?EXwCmUi*j;ZSd&F@WS)njY8ZEz7OsI{|9ad zZ@Wo|PlNY>17HQb9h?Mj1)ubcz)zySt6&p|8Kxu;GeoI}hFmz6cJ2mqE_^ z4tOv49>{&&0i{{L52XA_kb1Pi+du3#VSZUeix+t4?SN8so#&m5%3ML z4E_tG-FBms`t1d&&ry);Pk_{?T?0)y1xP?_-l~s{T;jo{3nQ?_y8~JbrTlh zxa}axF_xTR^S@3=E%izseoIC{b{A(cBeFmg`z5@<{ zuX+9fq#oQPrVuxRTz4nP_Papt`(BXtDEt10JSRcQO@pK0ELZ`b1u?bwh3D@;>iagh z2mA*}y>Ca5puWSNPlD9@S#ShA4|4uXAnkS;q@Hhq)awd(Klm>A1#lM*Wf(l<*#Noj z^B~8)1kzuA zIRjEY2H}q4qbPj17e0q`xHCQDzj@AsC}SvG<3SXzF@-`MIUmn*9Hsjl^&hkWmU7QS zcyrH>=2iEez?=8%6bkJ=gwlO@uOCM_?i;w@BPfg?W#2~M_F%Z>Rer^&`1+4E7=atMeVGe_p|~d7j=pyXcKvL<-(x3y7Hnp zl~|pIS=}i*4J*cCI;v@IcRFoGN&c1%=%7MY(#!(J<~9>&`VGX2+u2ND%wnoT(fugY z>@^r^(^SS(X%AE#mV=?J6*3v{G#$6(!e5X_W+RC1!HUdYafEN{o!+ST%## z&F0SC6f#|Fb;m?oY2oMfGMg*mJV&@Uj+%Q&8+hj}M$87uyqHzZ$iV&lhYp;vXVVzI zIh*l4m%CWR&sLhnp|q`@V-=?*;goT_Bx_2B&iBHH`{il+p6s4MGjyE+i!w;eA{3g- zYVF7vYEHGWdZwLCJ-4)JSEg&zqP~Sq*U4;d(`sPm1z$3VvEy(_nyg-j_ct=`-lXvY ze6bqYScz(Canc=~Hr@3&3llW+OU_TXm+r@wn<<34yXiG-v7Ploo9~BGMtl`U&b_4` zBz4@gx(hKLp{y<803?0Li2QT~uZ2K}(B6UMQkrR_;zrSeK$nj&!GaRbGWk1SiqXfL z%kZeJh2e@X8Q5aUt1A~66}bP?6O#j{Po19ZDL6V@9ypE|fHY95BFI&wRp;zTCq_^>l7|*$ z$Ie!#N{4%LT&|&XX)>v&AtG=^9-8L1ObX?Vp?K>j#&#^s;yn9TJ4jt*uGlw&+E2bW!Y(?h! z6|2mtK-XI{Sf^C3$c0EPn!cgE&e4ke29ub}cmqr-S%R}Nh^P%%1PyVJ)sU@tQc(j6 zf_cK5M`&v5tkZ#5kpX=}$$6~Lj1*Le_?b%Nqi{qC*RTq~Ht~(eRFc_c1fgDslZ)4W z0!1KJ2=uj}N2{m?b%hhIVN*2~QRzawuWd@TVnnVSh=fr)-_p9Ic^V0;H12Yy5)+Y< z%%uH|5+PY)(X~sljharxnZ~s*XA)6l?Pa8Rolg(DBDB85qCh+kIZO>$kPO3pTQ&~j&^yFoze&#%*P81kVQhCL2Oy>CgKSG zob*+AdPvK3Xyj}aC(a|4m_r*vPJRqMH&~4Hd<^SJh&m6q5=k{w39@o`MEA6Mqg-Cz zV(2ucqK5>@vWjqpg>S&6b6YXuOi*vR%trfnD<66Qh2X{$xTb6CBUmqboiiBE8qJ`F zo0m!a^RzOg^XF~2v$*e;(W8;ZNZUONS|)X5M#N9cuz-A3#DH#u>Ke2#K!3xB8sQq@ zjfU9Jy|pnm6Tgl@x}xHQT1=CqcfW&LO)D21dqLBGS%2U%X!B1&5i|3@1@K4zH$c;m zVd8qLufh6JrsFQSPTY97Q+_cx87jHwTA`5ocr1)*ZZ(;4O?kGZx9o$XI^VbA{|}!` AMF0Q* literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..3ea25979 --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,441 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-05 13:37+0100\n" +"PO-Revision-Date: 2014-04-05 13:39+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:40 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:42 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/charactergroups/group.tpl:5 +#: views/html/charactergroups/groupsgroup.tpl:2 +#: views/html/charactergroups/index.tpl:2 +#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:4 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:22 +#: views/html/characters/character.tpl:2 views/html/characters/index.tpl:2 +#: views/html/seminarymenu/index.tpl:3 views/html/users/user.tpl:11 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:60 +#: views/html/questgroups/questgroup.tpl:43 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:12 +#: views/html/charactergroupsquests/quest.tpl:3 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:12 +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:11 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:15 views/html/characters/index.tpl:6 +#: views/html/users/user.tpl:14 +msgid "Level" +msgstr "Level" + +#: views/html/characters/character.tpl:23 +msgid "Rank" +msgstr "Platz" + +#: views/html/characters/character.tpl:26 views/html/seminarymenu/index.tpl:5 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:4 views/html/introduction/index.tpl:12 +#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:7 views/html/introduction/index.tpl:8 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:9 views/html/introduction/index.tpl:10 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:13 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:13 +msgid "register yourself" +msgstr "registriere dich" + +#: 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:4 +msgid "Usergroups" +msgstr "Benutzergruppen" + +#: views/html/menu/index.tpl:5 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:9 +msgid "Logout" +msgstr "Logout" + +#: views/html/quests/quest.tpl:50 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/quest.tpl:55 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:57 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:71 views/html/quests/quest.tpl:80 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:14 +#, php-format +msgid "Submission of %s" +msgstr "Lösungen von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht am %s um %s Uhr" + +#: 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:17 +msgid "create" +msgstr "erstellen" + +#: views/html/seminaries/delete.tpl:2 views/html/seminaries/seminary.tpl:39 +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:38 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:9 views/html/users/edit.tpl:17 +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:42 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminarymenu/index.tpl:6 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:80 views/html/users/register.tpl:81 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: 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/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:15 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:17 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:19 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:21 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:26 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:28 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:30 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:32 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:37 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:39 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:41 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:43 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:48 views/html/users/register.tpl:50 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:55 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:57 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:59 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:87 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:18 +msgid "Roles" +msgstr "Rollen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..4490168d --- /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, xps '. + '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..43406b0a --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,341 @@ + + * @copyright 2014 Heinrich-Heine-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, 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + } + +?> 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..34e0c0e8 --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,140 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary 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 getSeminaryMediaByUrl($seminaryId, $mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $mediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary 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 getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE 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..3e2ced21 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,589 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..fb54519d --- /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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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 quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, static::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + static::QUEST_STATUS_ENTERED, static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + static::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..1fd82319 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..364e82e2 --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, media_id '. + '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, media_id '. + '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, media_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..88a7ed2d --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,278 @@ + + * @copyright 2014 Heinrich-Heine-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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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..90843218 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Match drops with user answers + foreach($drops as &$drop) + { + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..1e772edf --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,146 @@ + + * @copyright 2014 Heinrich-Heine-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 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..1af4dd6a --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                                + +
                                + + + +
                                + +
                                + +
                                + + + +
                                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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..456db617 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,261 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) + { + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..c2c32595 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                                +
                                + +

                                +
                                  + &$answer) : ?> +
                                1. + /> + +
                                2. + +
                                +
                                + + + + + + + +
                                diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ae14f199 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                                  + &$question) : ?> +
                                1. +

                                  +
                                    + +
                                  1. + + × + +
                                  2. + +
                                  +
                                2. + +
                                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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..0c72c1de --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                                + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                                + + +
                                +
                                + : +
                                  + +
                                • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                                • + +
                                + +
                                + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                                + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                                + + + + + + + +
                                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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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..ff53d1cf --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,72 @@ +
                                + +
                                +

                                +

                                +
                                +
                                + +
                                +
                                +

                                + Schweb wie ein Schmetterling! Stich wie eine Biene! +
                                +
                                  +
                                • 7. Platz
                                • +
                                • XP
                                • +
                                • 6 Mitglieder
                                • +
                                +
                                + +
                                +

                                +
                                  + +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • + +
                                +
                                + +
                                +

                                +
                                  + +
                                • +

                                  + format(new \DateTime($quest['created']))?> + + / XP +

                                  +
                                • + +
                                +
                                diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..016742b6 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,17 @@ +

                                +

                                +

                                + + + + +

                                +
                                  + +
                                • + +
                                diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..0a2cd85b --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,8 @@ +

                                +

                                + +
                                  + +
                                • + +
                                diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..c02a4558 --- /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..b621cff0 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,160 @@ +

                                +

                                +

                                + +
                                +
                                +
                                +
                                + +
                                +

                                :  %

                                +
                                +
                                +

                                +

                                +
                                +
                                +

                                +

                                XP

                                +
                                +
                                +

                                .

                                +

                                +
                                + +

                                +
                                  +
                                • +

                                  Aktive Beteiligung

                                  +
                                • +
                                • +

                                  + 0,3 Notenbonus

                                  +
                                • +
                                • +

                                  + 0,7 Notenbonus (noch 300 XP)

                                  +
                                • +
                                • +

                                  + 1,0 Notenbonus (noch 500 XP)

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

                                +
                                  + +
                                • + +

                                   XPs

                                  +
                                • + +
                                +
                                + +
                                +
                                +

                                Neue Achievements

                                + +
                                + +
                                +

                                Ranking

                                +
                                  +
                                • + +

                                  7. Anduin

                                  +

                                  Level 27 (1500 XP)

                                  +
                                • +
                                • + +

                                  8. Jaina

                                  +

                                  Level 26 (1400 XP)

                                  +
                                • +
                                • + +

                                  9. Uther

                                  +

                                  Level 25 (1300 XP)

                                  +
                                • +
                                • + +

                                  10. Lothar

                                  +

                                  Level 24 (1200 XP)

                                  +
                                • +
                                • + +

                                  11. Morris

                                  +

                                  Level 23 (1100 XP)

                                  +
                                • +
                                +
                                +
                                + +
                                +

                                Thematischer Fortschritt

                                + +
                                + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..94759710 --- /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..0f68bc07 --- /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..da2c90b1 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,52 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                                + +
                                +
                                + +
                                + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..ef919187 --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                                +

                                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..2c3299b4 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,11 @@ + +
                              • The Legend of Z
                              • + 0) : ?>
                              • +
                              • +
                              • + +
                              • + +
                              • + +
                                diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..4d3ce2b9 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
                                + +
                                + +

                                + + + + +

                                :

                                + +

                                + + +

                                + + + + + 0) : ?> +

                                +
                                  + +
                                • + +
                                  +
                                  +

                                  Fortschritt:

                                  +
                                  + +
                                  +

                                  / XP

                                  +
                                  +
                                • + +
                                + + + + + +

                                + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + 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..26911951 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,96 @@ + +
                                + +
                                + +

                                + +

                                + + 0) : ?> +
                                +

                                + + + +
                                + +

                                + + +

                                + 0 || !empty($questtext['abort_text'])) : ?> +
                                  + +
                                • + + +
                                • + +
                                + + +
                                +
                                + + + +
                                + +

                                + +

                                + +

                                +
                                + + + +
                                +

                                +

                                + + + +

                                :

                                + + +
                                + + + +
                                + 0) : ?> +
                                  + + +
                                • + : + + + + + +
                                • + +
                                • + : + + + + + +
                                • + + +
                                + + : + + Spiel vorbei + +
                                + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..9d13af65 --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,17 @@ + +
                                + +
                                + +

                                + +

                                + + + + + +

                                +
                                + +
                                diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..538b8565 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,41 @@ + +
                                + +
                                + +

                                + +

                                + + + + + +
                                +

                                +
                                  + +
                                • + +
                                • + +
                                + +

                                +
                                  + +
                                • + +
                                • + +
                                + +

                                +
                                  + +
                                • + +
                                • + +
                                +
                                diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..823eec70 --- /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..d2253f14 --- /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..007acf15 --- /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..611fdaf8 --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,17 @@ +

                                + +
                                  + +
                                • + + + +

                                  +
                                  + 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..2f7638f7 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,43 @@ + +
                                + +
                                + + +

                                +

                                + + +

                                + + + + +

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

                                diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..ee29969f --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,7 @@ + +
                              • + 0) : ?>
                              • +
                              • +
                              • +
                              • +
                                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..d4f38bb7 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                                +

                                + +
                                +
                                + +
                                + +
                                + +
                                + +
                                + +
                                +
                                + +
                                diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..56ba5d7b --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                                +

                                + +
                                +
                                + +
                                + +
                                + +
                                + +
                                + +
                                +
                                + +
                                diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..5d2883af --- /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..932b4c20 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                                + +

                                + +

                                .

                                + +
                                +
                                + +
                                + +
                                +
                                + +
                                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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..5ba5671e --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,88 @@ +

                                + +

                                + +
                                  + &$settings) : ?> + +
                                • +
                                    + $value) : ?> +
                                  • + getMessage(); + break; + } ?> +
                                  • + +
                                  +
                                • + + +
                                + +
                                +
                                + + />
                                + + />
                                + + />
                                + + />
                                + + />
                                +
                                + +
                                diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..3a6ea526 --- /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..dcce96dd --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,224 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +#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} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:75%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +body{background:#eae8e4} +.wrap{width:800px;max-width:800px} +article{background:#f7f5f2} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:auto} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Mon, 7 Apr 2014 23:10:44 +0200 Subject: [PATCH 182/340] correct permissions on user pages --- controllers/UsersController.inc | 6 ++++++ views/html/users/user.tpl | 2 ++ 2 files changed, 8 insertions(+) diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index b1ee5f56..4ba97dab 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -67,6 +67,7 @@ * Show a user and its details. * * @throws IdNotFoundException + * @throws AccessDeniedException * @param string $userUrl URL-Username of an user */ public function user($userUrl) @@ -74,6 +75,11 @@ // Get user $user = $this->Users->getUserByUrl($userUrl); + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + // Get Characters $characters = $this->Characters->getCharactersForUser($user['id']); diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index 3a6ea526..e40d2fb0 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -1,9 +1,11 @@

                                + 0) : ?> +

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

                                From de82c353d140ed34d453d5095eda7900eb92326f Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 7 Apr 2014 23:11:01 +0200 Subject: [PATCH 183/340] correct access of static methods of MediaController --- controllers/MediaController.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 3c70a832..0afe93fb 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -161,12 +161,12 @@ $file = file_get_contents($media['filename']); break; case 'questgroup': - if(!in_array(strtoupper($format), static::getImageTypes())) { + if(!in_array(strtoupper($format), self::getImageTypes())) { $file = file_get_contents($media['filename']); } else { - $file = static::resizeImage( + $file = self::resizeImage( $media['filename'], $format, 480 From a557703ce7cd2d07206cce71094c490f4e0494fe Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 8 Apr 2014 19:02:34 +0200 Subject: [PATCH 184/340] sub menu design update --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 ++++++++++++ agents/bottomlevel/MenuAgent.inc | 35 + .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 ++ agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 68 ++ apis/WebApi.inc | 250 ++++++++ app/Controller.inc | 103 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 267 ++++++++ app/QuesttypeController.inc | 349 ++++++++++ app/QuesttypeModel.inc | 154 +++++ app/QuesttypeView.inc | 76 +++ app/ToplevelAgent.inc | 36 ++ app/Utils.inc | 83 +++ app/controllers/IntermediateController.inc | 168 +++++ app/controllers/SeminaryRoleController.inc | 136 ++++ app/exceptions/FileUploadException.inc | 75 +++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 +++ .../QuesttypeAgentNotValidException.inc | 77 +++ .../QuesttypeControllerNotFoundException.inc | 77 +++ .../QuesttypeControllerNotValidException.inc | 77 +++ .../QuesttypeModelNotFoundException.inc | 77 +++ .../QuesttypeModelNotValidException.inc | 77 +++ .../SubmissionNotValidException.inc | 77 +++ app/exceptions/WrongFiletypeException.inc | 75 +++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 163 +++++ configs/CoreConfig.inc | 167 +++++ controllers/BinaryController.inc | 37 ++ controllers/CharactergroupsController.inc | 143 +++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 112 ++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 ++ controllers/MediaController.inc | 293 +++++++++ controllers/MenuController.inc | 50 ++ controllers/QuestgroupsController.inc | 179 ++++++ .../QuestgroupshierarchypathController.inc | 90 +++ controllers/QuestsController.inc | 595 +++++++++++++++++ controllers/SeminariesController.inc | 253 ++++++++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 +++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 316 +++++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ .../components/ValidationComponent.inc | 113 ++++ core/Agent.inc | 607 ++++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 433 +++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 5238 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 441 +++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 36 ++ models/CharactergroupsModel.inc | 147 +++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 341 ++++++++++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 140 ++++ models/QuestgroupsModel.inc | 589 +++++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 ++++ models/QuestsModel.inc | 344 ++++++++++ models/QuesttextsModel.inc | 114 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 193 ++++++ models/SeminarycharacterfieldsModel.inc | 58 ++ models/UploadsModel.inc | 148 +++++ models/UserrolesModel.inc | 77 +++ models/UsersModel.inc | 278 ++++++++ models/UserseminaryrolesModel.inc | 78 +++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 199 ++++++ .../dragndrop/DragndropQuesttypeModel.inc | 146 +++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 261 ++++++++ .../MultiplechoiceQuesttypeModel.inc | 159 +++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 +++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 +++++ .../textinput/TextinputQuesttypeModel.inc | 117 ++++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 ++++++++++++ responses/WebResponse.inc | 250 ++++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/charactergroups/group.tpl | 72 +++ views/html/charactergroups/groupsgroup.tpl | 17 + views/html/charactergroups/index.tpl | 8 + views/html/charactergroupsquests/quest.tpl | 46 ++ views/html/characters/character.tpl | 160 +++++ views/html/characters/index.tpl | 8 + views/html/error/index.tpl | 2 + views/html/html.tpl | 54 ++ views/html/introduction/index.tpl | 36 ++ views/html/menu/index.tpl | 9 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 96 +++ views/html/quests/submission.tpl | 17 + views/html/quests/submissions.tpl | 41 ++ views/html/seminaries/create.tpl | 10 + views/html/seminaries/delete.tpl | 8 + views/html/seminaries/edit.tpl | 10 + views/html/seminaries/index.tpl | 17 + views/html/seminaries/seminary.tpl | 43 ++ views/html/seminarymenu/index.tpl | 6 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 88 +++ views/html/users/user.tpl | 21 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 226 +++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 ++++ 207 files changed, 18919 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..1ce7ce9e --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: glob +media/* +tmp/* +uploads/* +seminarymedia/* 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/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..0648160e --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,68 @@ + + * @copyright 2014 Heinrich-Heine-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 menus + $this->addSubAgent('Menu'); + $this->addSubAgent('Seminarymenu'); + } + + + + + 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..5731aeed --- /dev/null +++ b/app/Controller.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; + + + /** + * 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 and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..5b980299 --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,168 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + + // Determine user roles + self::$user['roles'] = array(); + $roles = $this->Userroles->getUserrolesForUserById(self::$user['id']); + foreach($roles as &$role) { + self::$user['roles'][] = $role['name']; + } + + // Character + $controller = $this->agent->controller; + if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) + { + $seminaryUrl = $this->request->getParam(3); + self::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..36a9b439 --- /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\controllers\IntermediateController + { + /** + * 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/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..f33044de --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,163 @@ + + * @copyright 2014 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ) + ); + + + /** + * 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|register|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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..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..fb9d18c8 --- /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->getSeminaryMediaById($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..e64cb63f --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,112 @@ + + * @copyright 2014 Heinrich-Heine-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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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..7bf8859e --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedCharacter', IntermediateController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..0afe93fb --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,293 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * 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. + * + * @param string $mediaUrl URL-name of the medium + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($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; + default: + throw new ParamsNotValidException($action); + break; + } + + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($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['seminarymedia'].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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::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..df01f2ff --- /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', IntermediateController::$user); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..d7b8acf1 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,179 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && $currentQuest['solved']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..e930cbd6 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,595 @@ + + * @copyright 2014 Heinrich-Heine-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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + //$questtexttypesUrls = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) + { + switch($questStatus) + { + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; + } + } + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('queststatustext', $questStatusText); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + } + + // 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 + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..8294c0f7 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,253 @@ + + * @copyright 2014 Heinrich-Heine-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']); + + // Media + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + } + } + + // Medium + $media = null; + if(!is_null($seminary['media_id'])) { + $media = $this->Media->getMediaById($seminary['media_id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroupshierarchy', $questgroupshierarchy); + $this->set('media', $media); + } + + + /** + * Action: create. + * + * Create a new seminary. + */ + public function create() + { + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Create new seminary + $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/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..d827425d --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..4ba97dab --- /dev/null +++ b/controllers/UsersController.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\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..8034e47d --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,113 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param string $name Name of the field to validate against + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $name) + { + $settings = $this->config[$name]; + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + $param = $params[$index]; + $check = $this->validate($param, $index); + if($check !== true) { + $validation[$index] = $check; + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                                \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..c7c0f88cbd25e2f99a9e433c21011571a283ba5a GIT binary patch literal 5238 zcmai$U2I%O700I#S}>(e(>8>UlIe#bCS7}-k0#y3ZJpRoLY>sLH-(mm>h9jz-CN(i zcbS=M$9}Rz%TstTQdJ4kR!B&+QcE6?0HLTrEb&mGf_OmiK%_=|R8*k$0Ul6=`2Xk5 z-rd-ad$n_acV^DaoH=vmoV~JR+w%g?EXwCmUi*j;ZSd&F@WS)njY8ZEz7OsI{|9ad zZ@Wo|PlNY>17HQb9h?Mj1)ubcz)zySt6&p|8Kxu;GeoI}hFmz6cJ2mqE_^ z4tOv49>{&&0i{{L52XA_kb1Pi+du3#VSZUeix+t4?SN8so#&m5%3ML z4E_tG-FBms`t1d&&ry);Pk_{?T?0)y1xP?_-l~s{T;jo{3nQ?_y8~JbrTlh zxa}axF_xTR^S@3=E%izseoIC{b{A(cBeFmg`z5@<{ zuX+9fq#oQPrVuxRTz4nP_Papt`(BXtDEt10JSRcQO@pK0ELZ`b1u?bwh3D@;>iagh z2mA*}y>Ca5puWSNPlD9@S#ShA4|4uXAnkS;q@Hhq)awd(Klm>A1#lM*Wf(l<*#Noj z^B~8)1kzuA zIRjEY2H}q4qbPj17e0q`xHCQDzj@AsC}SvG<3SXzF@-`MIUmn*9Hsjl^&hkWmU7QS zcyrH>=2iEez?=8%6bkJ=gwlO@uOCM_?i;w@BPfg?W#2~M_F%Z>Rer^&`1+4E7=atMeVGe_p|~d7j=pyXcKvL<-(x3y7Hnp zl~|pIS=}i*4J*cCI;v@IcRFoGN&c1%=%7MY(#!(J<~9>&`VGX2+u2ND%wnoT(fugY z>@^r^(^SS(X%AE#mV=?J6*3v{G#$6(!e5X_W+RC1!HUdYafEN{o!+ST%## z&F0SC6f#|Fb;m?oY2oMfGMg*mJV&@Uj+%Q&8+hj}M$87uyqHzZ$iV&lhYp;vXVVzI zIh*l4m%CWR&sLhnp|q`@V-=?*;goT_Bx_2B&iBHH`{il+p6s4MGjyE+i!w;eA{3g- zYVF7vYEHGWdZwLCJ-4)JSEg&zqP~Sq*U4;d(`sPm1z$3VvEy(_nyg-j_ct=`-lXvY ze6bqYScz(Canc=~Hr@3&3llW+OU_TXm+r@wn<<34yXiG-v7Ploo9~BGMtl`U&b_4` zBz4@gx(hKLp{y<803?0Li2QT~uZ2K}(B6UMQkrR_;zrSeK$nj&!GaRbGWk1SiqXfL z%kZeJh2e@X8Q5aUt1A~66}bP?6O#j{Po19ZDL6V@9ypE|fHY95BFI&wRp;zTCq_^>l7|*$ z$Ie!#N{4%LT&|&XX)>v&AtG=^9-8L1ObX?Vp?K>j#&#^s;yn9TJ4jt*uGlw&+E2bW!Y(?h! z6|2mtK-XI{Sf^C3$c0EPn!cgE&e4ke29ub}cmqr-S%R}Nh^P%%1PyVJ)sU@tQc(j6 zf_cK5M`&v5tkZ#5kpX=}$$6~Lj1*Le_?b%Nqi{qC*RTq~Ht~(eRFc_c1fgDslZ)4W z0!1KJ2=uj}N2{m?b%hhIVN*2~QRzawuWd@TVnnVSh=fr)-_p9Ic^V0;H12Yy5)+Y< z%%uH|5+PY)(X~sljharxnZ~s*XA)6l?Pa8Rolg(DBDB85qCh+kIZO>$kPO3pTQ&~j&^yFoze&#%*P81kVQhCL2Oy>CgKSG zob*+AdPvK3Xyj}aC(a|4m_r*vPJRqMH&~4Hd<^SJh&m6q5=k{w39@o`MEA6Mqg-Cz zV(2ucqK5>@vWjqpg>S&6b6YXuOi*vR%trfnD<66Qh2X{$xTb6CBUmqboiiBE8qJ`F zo0m!a^RzOg^XF~2v$*e;(W8;ZNZUONS|)X5M#N9cuz-A3#DH#u>Ke2#K!3xB8sQq@ zjfU9Jy|pnm6Tgl@x}xHQT1=CqcfW&LO)D21dqLBGS%2U%X!B1&5i|3@1@K4zH$c;m zVd8qLufh6JrsFQSPTY97Q+_cx87jHwTA`5ocr1)*ZZ(;4O?kGZx9o$XI^VbA{|}!` AMF0Q* literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..3ea25979 --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,441 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-05 13:37+0100\n" +"PO-Revision-Date: 2014-04-05 13:39+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:40 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:42 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/charactergroups/group.tpl:5 +#: views/html/charactergroups/groupsgroup.tpl:2 +#: views/html/charactergroups/index.tpl:2 +#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:4 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:22 +#: views/html/characters/character.tpl:2 views/html/characters/index.tpl:2 +#: views/html/seminarymenu/index.tpl:3 views/html/users/user.tpl:11 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:60 +#: views/html/questgroups/questgroup.tpl:43 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:12 +#: views/html/charactergroupsquests/quest.tpl:3 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:12 +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:11 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:15 views/html/characters/index.tpl:6 +#: views/html/users/user.tpl:14 +msgid "Level" +msgstr "Level" + +#: views/html/characters/character.tpl:23 +msgid "Rank" +msgstr "Platz" + +#: views/html/characters/character.tpl:26 views/html/seminarymenu/index.tpl:5 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:4 views/html/introduction/index.tpl:12 +#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:7 views/html/introduction/index.tpl:8 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:9 views/html/introduction/index.tpl:10 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:13 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:13 +msgid "register yourself" +msgstr "registriere dich" + +#: 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:4 +msgid "Usergroups" +msgstr "Benutzergruppen" + +#: views/html/menu/index.tpl:5 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:9 +msgid "Logout" +msgstr "Logout" + +#: views/html/quests/quest.tpl:50 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/quest.tpl:55 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:57 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:71 views/html/quests/quest.tpl:80 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:14 +#, php-format +msgid "Submission of %s" +msgstr "Lösungen von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht am %s um %s Uhr" + +#: 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:17 +msgid "create" +msgstr "erstellen" + +#: views/html/seminaries/delete.tpl:2 views/html/seminaries/seminary.tpl:39 +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:38 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:9 views/html/users/edit.tpl:17 +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:42 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminarymenu/index.tpl:6 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:80 views/html/users/register.tpl:81 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: 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/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:15 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:17 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:19 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:21 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:26 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:28 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:30 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:32 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:37 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:39 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:41 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:43 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:48 views/html/users/register.tpl:50 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:55 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:57 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:59 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:87 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:18 +msgid "Roles" +msgstr "Rollen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..4490168d --- /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, xps '. + '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..43406b0a --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,341 @@ + + * @copyright 2014 Heinrich-Heine-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, 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + } + +?> 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..34e0c0e8 --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,140 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary 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 getSeminaryMediaByUrl($seminaryId, $mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $mediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary 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 getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE 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..3e2ced21 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,589 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..fb54519d --- /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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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 quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, static::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + static::QUEST_STATUS_ENTERED, static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + static::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..1fd82319 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..364e82e2 --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, media_id '. + '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, media_id '. + '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, media_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..88a7ed2d --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,278 @@ + + * @copyright 2014 Heinrich-Heine-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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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..90843218 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Match drops with user answers + foreach($drops as &$drop) + { + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..1e772edf --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,146 @@ + + * @copyright 2014 Heinrich-Heine-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 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..1af4dd6a --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                                + +
                                + + + +
                                + +
                                + +
                                + + + +
                                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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..456db617 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,261 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) + { + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..c2c32595 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                                +
                                + +

                                +
                                  + &$answer) : ?> +
                                1. + /> + +
                                2. + +
                                +
                                + + + + + + + +
                                diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ae14f199 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                                  + &$question) : ?> +
                                1. +

                                  +
                                    + +
                                  1. + + × + +
                                  2. + +
                                  +
                                2. + +
                                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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..0c72c1de --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                                + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                                + + +
                                +
                                + : +
                                  + +
                                • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                                • + +
                                + +
                                + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                                + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                                + + + + + + + +
                                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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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..ff53d1cf --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,72 @@ +
                                + +
                                +

                                +

                                +
                                +
                                + +
                                +
                                +

                                + Schweb wie ein Schmetterling! Stich wie eine Biene! +
                                +
                                  +
                                • 7. Platz
                                • +
                                • XP
                                • +
                                • 6 Mitglieder
                                • +
                                +
                                + +
                                +

                                +
                                  + +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • +
                                • +

                                  +

                                  +

                                  XP

                                  +
                                • + +
                                +
                                + +
                                +

                                +
                                  + +
                                • +

                                  + format(new \DateTime($quest['created']))?> + + / XP +

                                  +
                                • + +
                                +
                                diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..016742b6 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,17 @@ +

                                +

                                +

                                + + + + +

                                +
                                  + +
                                • + +
                                diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..0a2cd85b --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,8 @@ +

                                +

                                + +
                                  + +
                                • + +
                                diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..c02a4558 --- /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..b621cff0 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,160 @@ +

                                +

                                +

                                + +
                                +
                                +
                                +
                                + +
                                +

                                :  %

                                +
                                +
                                +

                                +

                                +
                                +
                                +

                                +

                                XP

                                +
                                +
                                +

                                .

                                +

                                +
                                + +

                                +
                                  +
                                • +

                                  Aktive Beteiligung

                                  +
                                • +
                                • +

                                  + 0,3 Notenbonus

                                  +
                                • +
                                • +

                                  + 0,7 Notenbonus (noch 300 XP)

                                  +
                                • +
                                • +

                                  + 1,0 Notenbonus (noch 500 XP)

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

                                +
                                  + +
                                • + +

                                   XPs

                                  +
                                • + +
                                +
                                + +
                                +
                                +

                                Neue Achievements

                                + +
                                + +
                                +

                                Ranking

                                +
                                  +
                                • + +

                                  7. Anduin

                                  +

                                  Level 27 (1500 XP)

                                  +
                                • +
                                • + +

                                  8. Jaina

                                  +

                                  Level 26 (1400 XP)

                                  +
                                • +
                                • + +

                                  9. Uther

                                  +

                                  Level 25 (1300 XP)

                                  +
                                • +
                                • + +

                                  10. Lothar

                                  +

                                  Level 24 (1200 XP)

                                  +
                                • +
                                • + +

                                  11. Morris

                                  +

                                  Level 23 (1100 XP)

                                  +
                                • +
                                +
                                +
                                + +
                                +

                                Thematischer Fortschritt

                                + +
                                + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..94759710 --- /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..0f68bc07 --- /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..91bd00b8 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,54 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                                + +
                                +
                                + +
                                + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..ef919187 --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                                +

                                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..aff23551 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
                              • The Legend of Z
                              • + 0) : ?>
                              • +
                              • +
                              • + +
                              • + +
                              • + diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..4d3ce2b9 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
                                + +
                                + +

                                + + + + +

                                :

                                + +

                                + + +

                                + + + + + 0) : ?> +

                                +
                                  + +
                                • + +
                                  +
                                  +

                                  Fortschritt:

                                  +
                                  + +
                                  +

                                  / XP

                                  +
                                  +
                                • + +
                                + + + + + +

                                + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + 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..26911951 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,96 @@ + +
                                + +
                                + +

                                + +

                                + + 0) : ?> +
                                +

                                + + + +
                                + +

                                + + +

                                + 0 || !empty($questtext['abort_text'])) : ?> +
                                  + +
                                • + + +
                                • + +
                                + + +
                                +
                                + + + +
                                + +

                                + +

                                + +

                                +
                                + + + +
                                +

                                +

                                + + + +

                                :

                                + + +
                                + + + +
                                + 0) : ?> +
                                  + + +
                                • + : + + + + + +
                                • + +
                                • + : + + + + + +
                                • + + +
                                + + : + + Spiel vorbei + +
                                + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..9d13af65 --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,17 @@ + +
                                + +
                                + +

                                + +

                                + + + + + +

                                +
                                + +
                                diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..538b8565 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,41 @@ + +
                                + +
                                + +

                                + +

                                + + + + + +
                                +

                                +
                                  + +
                                • + +
                                • + +
                                + +

                                +
                                  + +
                                • + +
                                • + +
                                + +

                                +
                                  + +
                                • + +
                                • + +
                                +
                                diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..823eec70 --- /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..d2253f14 --- /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..007acf15 --- /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..611fdaf8 --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,17 @@ +

                                + +
                                  + +
                                • + + + +

                                  +
                                  + 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..2f7638f7 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,43 @@ + +
                                + +
                                + + +

                                +

                                + + +

                                + + + + +

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

                                diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..041c4f43 --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,6 @@ +
                              • + 0) : ?>
                              • +
                              • +
                              • +
                              • + 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..d4f38bb7 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                                +

                                + +
                                +
                                + +
                                + +
                                + +
                                + +
                                + +
                                +
                                + +
                                diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..56ba5d7b --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                                +

                                + +
                                +
                                + +
                                + +
                                + +
                                + +
                                + +
                                +
                                + +
                                diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..5d2883af --- /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..932b4c20 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                                + +

                                + +

                                .

                                + +
                                +
                                + +
                                + +
                                +
                                + +
                                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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..5ba5671e --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,88 @@ +

                                + +

                                + +
                                  + &$settings) : ?> + +
                                • +
                                    + $value) : ?> +
                                  • + getMessage(); + break; + } ?> +
                                  • + +
                                  +
                                • + + +
                                + +
                                +
                                + + />
                                + + />
                                + + />
                                + + />
                                + + />
                                +
                                + +
                                diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..e40d2fb0 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,21 @@ +

                                +

                                + 0) : ?> + + +

                                + 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..d632d2c7 --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,226 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:75%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +body{background:#eae8e4} +.wrap{width:800px;max-width:800px} +article{background:#f7f5f2} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:auto} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Tue, 8 Apr 2014 19:22:49 +0200 Subject: [PATCH 185/340] home icon for meta system home link --- views/html/menu/index.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index aff23551..db402226 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,4 +1,4 @@ -
                              • The Legend of Z
                              • +
                              • The Legend of Z
                              • 0) : ?>
                              • From 723ea4f75084fe6a9bbc81810b7e4c09cc035174 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 8 Apr 2014 23:47:39 +0200 Subject: [PATCH 186/340] pass validation settings directly to method ?validation? of ValiadionComponent --- .../components/ValidationComponent.inc | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc index 8034e47d..dbe5b0b3 100644 --- a/controllers/components/ValidationComponent.inc +++ b/controllers/components/ValidationComponent.inc @@ -44,13 +44,12 @@ /** * Validate an user input. * - * @param mixed $input User input to validate - * @param string $name Name of the field to validate against - * @return mixed True or the settings the validation fails on + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on */ - public function validate($input, $name) + public function validate($input, $settings) { - $settings = $this->config[$name]; $validation = array(); // Min string length @@ -93,10 +92,13 @@ } // Check parameter - $param = $params[$index]; - $check = $this->validate($param, $index); - if($check !== true) { - $validation[$index] = $check; + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } } } From b88d39b3c025c636658335aeca4984b164a32869 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:01:29 +0200 Subject: [PATCH 187/340] implement Character registration --- configs/AppConfig.inc | 9 +- controllers/CharactersController.inc | 132 +++++++++++++++++++++++- models/CharactersModel.inc | 64 ++++++++++-- models/CharactertypesModel.inc | 57 ++++++++++ models/SeminarycharacterfieldsModel.inc | 20 ++++ views/html/characters/register.tpl | 72 +++++++++++++ 6 files changed, 343 insertions(+), 11 deletions(-) create mode 100644 models/CharactertypesModel.inc create mode 100644 views/html/characters/register.tpl diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index f33044de..45ccb0fe 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -96,6 +96,11 @@ 'password' => array( 'minlength' => 5, 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' ) ); @@ -109,7 +114,7 @@ public static $routes = array( array('css/?(.*)', 'css/$1?layout=stylesheet', true), array('users/([^/]+)/(edit|delete)', 'users/$2/$1', true), - array('users/(?!(index|login|register|logout|create|edit|delete))', 'users/user/$1', true), + array('users/(?!(index|login|register|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/ @@ -118,7 +123,7 @@ 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('characters/(?!(index|character|register))', '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), diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index e64cb63f..8486a2fc 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -19,6 +19,18 @@ */ class CharactersController extends \hhu\z\controllers\SeminaryRoleController { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); /** * User permissions * @@ -26,14 +38,18 @@ */ public $permissions = array( 'index' => array('admin', 'moderator'), - 'character' => array('admin', 'moderator', 'user') + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') ); /** - * Required models + * User seminary permissions * * @var array */ - public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'seminarycharacterfields'); + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); @@ -50,6 +66,9 @@ { // Get Seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } // Get registered Characters $characters = $this->Characters->getCharactersForSeminary($seminary['id']); @@ -59,6 +78,12 @@ { // Level $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } } @@ -82,6 +107,9 @@ // Get Seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } // Get Character $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); @@ -107,6 +135,104 @@ $this->set('groups', $groups); } + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + // Medium + $media = null; + if(!is_null($seminary['media_id'])) { + $media = $this->Media->getMediaById($seminary['media_id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('media', $media); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + } ?> diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 43406b0a..a502916a 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -43,7 +43,7 @@ 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 '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_id, seminaries.url 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 '. @@ -63,7 +63,7 @@ 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 '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, 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 '. @@ -83,7 +83,7 @@ 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, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.avatar_id, 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 '. @@ -105,7 +105,7 @@ 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 '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, 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 = ?', @@ -132,7 +132,7 @@ 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, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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 '. @@ -161,7 +161,7 @@ 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, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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 '. @@ -336,6 +336,58 @@ ); } + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + } ?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/models/SeminarycharacterfieldsModel.inc b/models/SeminarycharacterfieldsModel.inc index c881df2c..efad3bde 100644 --- a/models/SeminarycharacterfieldsModel.inc +++ b/models/SeminarycharacterfieldsModel.inc @@ -34,6 +34,26 @@ + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + /** * Get Seminary Character fields of a Character. * diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..f6d2b15e --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,72 @@ + +
                                + +
                                + +

                                +

                                + +
                                + +
                                  + &$settings) : ?> +
                                • +
                                    + $value) : ?> +
                                  • + +
                                  • + +
                                  +
                                • + +
                                + +
                                + + +
                                + + +
                                + + +
                                  + &$settings) : ?> +
                                • + +
                                + +
                                + + + + + required="required"/> + + + + +
                                + +
                                From b40305631ac2d21cc7f4d4df170e6bc34eb52ba5 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:06:02 +0200 Subject: [PATCH 188/340] set mood-pictures for Seminary-related pages and small avatar pictures --- controllers/CharactergroupsController.inc | 24 +++++-- .../CharactergroupsquestsController.inc | 3 + controllers/UsersController.inc | 8 +++ models/AvatarsModel.inc | 62 +++++++++++++++++++ views/html/charactergroups/group.tpl | 42 ++++--------- views/html/charactergroups/groupsgroup.tpl | 5 ++ views/html/charactergroups/index.tpl | 5 ++ views/html/charactergroupsquests/quest.tpl | 13 ++-- views/html/characters/character.tpl | 5 ++ views/html/characters/index.tpl | 13 +++- 10 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 models/AvatarsModel.inc diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc index 1028d28c..594cba12 100644 --- a/controllers/CharactergroupsController.inc +++ b/controllers/CharactergroupsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'charactergroups', 'charactergroupsquests'); + public $models = array('seminaries', 'charactergroups', 'charactergroupsquests', 'avatars', 'media'); /** * User permissions * @@ -57,6 +57,9 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } // Get Character groups-groups $groupsgroups = $this->Charactergroups->getGroupsroupsForSeminary($seminary['id']); @@ -82,6 +85,9 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); @@ -116,15 +122,26 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); // Get Character group $group = $this->Charactergroups->getGroupByUrl($groupsgroup['id'], $groupUrl); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); - // Get Characters - $characters = $this->Characters->getCharactersForGroup($group['id']); + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } // Get Character groups Quests $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); @@ -134,7 +151,6 @@ $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 index fb9d18c8..478d8700 100644 --- a/controllers/CharactergroupsquestsController.inc +++ b/controllers/CharactergroupsquestsController.inc @@ -61,6 +61,9 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + if(!is_null($seminary['media_id'])) { + $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); + } // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 4ba97dab..19d0e8d0 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -88,6 +88,14 @@ { // Level $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } } diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..115d1fb2 --- /dev/null +++ b/models/AvatarsModel.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 Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + } + +?> diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index ff53d1cf..426e9a8e 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -1,8 +1,11 @@ +
                                - +
                                +

                                +

                                @@ -12,43 +15,20 @@ Schweb wie ein Schmetterling! Stich wie eine Biene!
                                  -
                                • 7. Platz
                                • -
                                • XP
                                • -
                                • 6 Mitglieder
                                • +
                                • .
                                • +
                                •  XPs
                                • +
                                • 1) ? _('Members') : _('Member')?>

                                  - +
                                • -

                                  -

                                  -

                                  XP

                                  -
                                • -
                                • -

                                  -

                                  -

                                  XP

                                  -
                                • -
                                • -

                                  -

                                  -

                                  XP

                                  -
                                • -
                                • -

                                  -

                                  -

                                  XP

                                  -
                                • -
                                • -

                                  -

                                  -

                                  XP

                                  -
                                • -
                                • -

                                  + +

                                  +

                                  XP

                                • diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl index 016742b6..c604328a 100644 --- a/views/html/charactergroups/groupsgroup.tpl +++ b/views/html/charactergroups/groupsgroup.tpl @@ -1,3 +1,8 @@ + +
                                  + +
                                  +

                                  diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl index 0a2cd85b..483c0f2e 100644 --- a/views/html/charactergroups/index.tpl +++ b/views/html/charactergroups/index.tpl @@ -1,3 +1,8 @@ + +
                                  + +
                                  +

                                  diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl index c02a4558..6837cfff 100644 --- a/views/html/charactergroupsquests/quest.tpl +++ b/views/html/charactergroupsquests/quest.tpl @@ -1,7 +1,12 @@ -

                                  -

                                  -

                                  -
                                  + +
                                  + +
                                  + +

                                  +

                                  +

                                  +

                                  diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index b621cff0..c347a9d0 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -1,3 +1,8 @@ + +
                                  + +
                                  +

                                  diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl index 94759710..ee1c4454 100644 --- a/views/html/characters/index.tpl +++ b/views/html/characters/index.tpl @@ -1,8 +1,19 @@ + +
                                  + +
                                  +

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

                                    + +

                                    +

                                    XP

                                    +
                                  From fbc377f25a77d59a8f2da10cee4956efd41ddc95 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:07:37 +0200 Subject: [PATCH 189/340] disable links based on permissions --- controllers/SeminariesController.inc | 11 ++++++++++- views/html/characters/index.tpl | 2 +- views/html/seminaries/index.tpl | 20 ++++++++++++++++++-- views/html/users/user.tpl | 16 ++++++++++++++-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 8294c0f7..c45aaad2 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'users', 'questgroupshierarchy', 'questgroups', 'media'); + public $models = array('seminaries', 'users', 'userseminaryroles', 'questgroupshierarchy', 'questgroups', 'media'); /** * User permissions * @@ -71,6 +71,15 @@ if(!is_null($seminary['media_id'])) { $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); } + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['id']); + } diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl index ee1c4454..478c0a65 100644 --- a/views/html/characters/index.tpl +++ b/views/html/characters/index.tpl @@ -6,7 +6,7 @@

                                  -
                                    +
                                    • diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index 611fdaf8..4ad92986 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -1,16 +1,32 @@

                                      + 0) : ?> +
                                      • -

                                        +

                                        + 0) : ?> + + + + +

                                        - format(new \DateTime($seminary['created'])))?> + +
                                        + format(new \DateTime($seminary['created'])))?>
                                        + + + + + +
                                      • diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index e40d2fb0..f5615f3e 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -11,9 +11,21 @@

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

                                            + +

                                            + 0) : ?> + + + + +

                                            +

                                            +
                                          From 56f6e799074f293beac6ac0541fe9d0fe5477165 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:08:05 +0200 Subject: [PATCH 190/340] set motto and rank for Character groups --- models/CharactergroupsModel.inc | 29 ++++++++++++++++++++++++++-- views/html/charactergroups/group.tpl | 2 +- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc index ce6e8b61..e88b1826 100644 --- a/models/CharactergroupsModel.inc +++ b/models/CharactergroupsModel.inc @@ -88,7 +88,7 @@ public function getGroupsForGroupsgroup($groupsgroupId) { return $this->db->query( - 'SELECT id, name, url, xps '. + 'SELECT id, name, url, xps, motto '. 'FROM v_charactergroups '. 'WHERE charactergroupsgroup_id = ?', 'i', @@ -128,7 +128,7 @@ public function getGroupByUrl($groupsgroupId, $groupUrl) { $data = $this->db->query( - 'SELECT id, name, url, xps '. + 'SELECT id, name, url, xps, motto '. 'FROM v_charactergroups '. 'WHERE charactergroupsgroup_id = ? AND url = ?', 'is', @@ -142,6 +142,31 @@ return $data[0]; } + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + } ?> diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 426e9a8e..9a66595d 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -12,7 +12,7 @@

                                          - Schweb wie ein Schmetterling! Stich wie eine Biene! +
                                          • .
                                          • From 5217392d985818a510b9829c67c9b0791b924a22 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:08:32 +0200 Subject: [PATCH 191/340] set description abstract for seminaries in listing --- controllers/SeminariesController.inc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index c45aaad2..50d1aac5 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -64,6 +64,8 @@ // Get additional data foreach($seminaries as &$seminary) { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + // Created user $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); From c9863c0554671ea42707d0ee6fe7a97a607ece47 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:08:56 +0200 Subject: [PATCH 192/340] few template fixes for user registration --- views/html/users/register.tpl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/views/html/users/register.tpl b/views/html/users/register.tpl index 5ba5671e..184e0a0c 100644 --- a/views/html/users/register.tpl +++ b/views/html/users/register.tpl @@ -1,10 +1,9 @@

                                            - +
                                              &$settings) : ?> -
                                              • $value) : ?> @@ -67,7 +66,6 @@
                                            • -
                                            From 46f4fb8f9cb9f432c535f89073cf253418b3adff Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:09:30 +0200 Subject: [PATCH 193/340] set Seminary roles for user --- controllers/UsersController.inc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 19d0e8d0..535dcbbe 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -36,7 +36,7 @@ * * @var array */ - public $models = array('users', 'characters'); + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); /** * Required components * @@ -86,6 +86,10 @@ // Additional Character information foreach($characters as &$character) { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + // Level $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); From f52b8fa27f15a6d62283480e67f51fa0fb9d6aa3 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 9 Apr 2014 00:22:38 +0200 Subject: [PATCH 194/340] add Seminary menu as submenu of mainmenu (Issue #43) --- agents/bottomlevel/MenuAgent.inc | 2 ++ agents/toplevel/HtmlAgent.inc | 3 +-- controllers/MenuController.inc | 1 + views/html/html.tpl | 3 --- views/html/menu/index.tpl | 1 + 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/agents/bottomlevel/MenuAgent.inc b/agents/bottomlevel/MenuAgent.inc index 77d60d8d..49512791 100644 --- a/agents/bottomlevel/MenuAgent.inc +++ b/agents/bottomlevel/MenuAgent.inc @@ -28,6 +28,8 @@ */ public function index(\nre\core\Request $request, \nre\core\Response $response) { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); } } diff --git a/agents/toplevel/HtmlAgent.inc b/agents/toplevel/HtmlAgent.inc index 0648160e..63ed4525 100644 --- a/agents/toplevel/HtmlAgent.inc +++ b/agents/toplevel/HtmlAgent.inc @@ -37,9 +37,8 @@ */ public function index(\nre\core\Request $request, \nre\core\Response $response) { - // Add menus + // Add menu $this->addSubAgent('Menu'); - $this->addSubAgent('Seminarymenu'); } diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc index df01f2ff..2be5ea8c 100644 --- a/controllers/MenuController.inc +++ b/controllers/MenuController.inc @@ -35,6 +35,7 @@ // Set userdata $this->set('loggedUser', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); } diff --git a/views/html/html.tpl b/views/html/html.tpl index 91bd00b8..2e8cc9e9 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -40,9 +40,6 @@ - - - diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index db402226..8c61c817 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -2,6 +2,7 @@ 0) : ?>
                                          • +
                                          • From 5b67ba380f3922ff5dfd257f209f297d585eab28 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 9 Apr 2014 15:36:02 +0200 Subject: [PATCH 195/340] aside information (char, quest, group) for 1600px+ --- views/html/html.tpl | 47 +++++++++++++++++++++++++++++++++++++++++++++ www/css/desktop.css | 12 +++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 2e8cc9e9..bc09734a 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -46,6 +46,53 @@
                                            + diff --git a/www/css/desktop.css b/www/css/desktop.css index d632d2c7..292504cb 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -86,6 +86,7 @@ menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-b #navicon{display:block;color:#d9e5e7} article{padding:70px 0 30px} +aside{display:none} .moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} .moodpic img{width:100vw} @@ -220,7 +221,16 @@ article{padding:20px 40px 80px 40px} @media only screen and (min-width:1366px){ body{background:#eae8e4} .wrap{width:800px;max-width:800px} -article{background:#f7f5f2} +article{background:#f7f5f2;float:left} .moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} .moodpic img{width:auto} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:30px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px;list-style-type:square} +aside .charstats li{font-size:.875em;padding:2px 0 2px 6px} +aside .cranks li:nth-child(odd){background:#f7f5f2} } \ No newline at end of file From 8e0290889bc17b93d06dd1ee7b2085f1bff365fc Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 9 Apr 2014 15:51:58 +0200 Subject: [PATCH 196/340] fixed list icon display bug in IE --- views/html/html.tpl | 8 ++++---- www/css/desktop.css | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index bc09734a..73ee7b85 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -51,10 +51,10 @@

                                            Zyrendaniel

                                diff --git a/www/css/desktop.css b/www/css/desktop.css index 292504cb..1def7cdf 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -229,8 +229,8 @@ article{background:#f7f5f2;float:left} @media only screen and (min-width:1600px){ aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} aside section{margin:20px 0 40px 0} -aside .char{width:120px;float:left;margin-right:30px} -aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px;list-style-type:square} -aside .charstats li{font-size:.875em;padding:2px 0 2px 6px} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} aside .cranks li:nth-child(odd){background:#f7f5f2} } \ No newline at end of file From f1979327048679395813d6b6aa3ce65961091e00 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 9 Apr 2014 16:42:25 +0200 Subject: [PATCH 197/340] aside update: last achievement + rank wording --- views/html/html.tpl | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 73ee7b85..d290c0d8 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -53,7 +53,7 @@
                                @@ -61,6 +61,16 @@

                                Aktuelle Quest

                                Die verwunschene Stadt

                              +
                              +

                              Letzte Errungenschaft

                              + +

                              Wille und die Majas

                                From 8cefeec0d4fc9197a97a8c16c2825c8149d41500 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 9 Apr 2014 16:44:57 +0200 Subject: [PATCH 198/340] simple wording change --- views/html/html.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index d290c0d8..1796e72a 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -53,7 +53,7 @@
                                • Level 1
                                • 1500 XP
                                • -
                                • Platz 11
                                • +
                                • 11. Platz
                                • Zum Profil
                              From 5a3e713f327f301230b83b726db4ab06d7bb644c Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 9 Apr 2014 16:48:09 +0200 Subject: [PATCH 199/340] last checkpoint wording --- views/html/html.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/html.tpl b/views/html/html.tpl index 1796e72a..c2bc1804 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -58,7 +58,7 @@
                        -

                        Aktuelle Quest

                        +

                        Letzter Speicherpunkt

                        Die verwunschene Stadt

                        From 6075ccccd55f5a4880d7eb177b0212f3262ad318 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 9 Apr 2014 18:18:32 +0200 Subject: [PATCH 200/340] article-tag scaling height fix --- www/css/desktop.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/www/css/desktop.css b/www/css/desktop.css index 1def7cdf..b00b193d 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -219,8 +219,9 @@ article{padding:20px 40px 80px 40px} } @media only screen and (min-width:1366px){ -body{background:#eae8e4} -.wrap{width:800px;max-width:800px} +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} article{background:#f7f5f2;float:left} .moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} .moodpic img{width:auto} From f4c1771d11c7c5edda529c10c7cbf720b16657ab Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 11 Apr 2014 14:48:54 +0200 Subject: [PATCH 201/340] move ?aside? to Agent ?Seminarybar? (Issue #55) --- agents/bottomlevel/SeminarybarAgent.inc | 35 +++++++++++++++ agents/toplevel/HtmlAgent.inc | 3 ++ controllers/SeminarybarController.inc | 53 ++++++++++++++++++++++ views/html/html.tpl | 60 +++---------------------- views/html/seminarybar/index.tpl | 55 +++++++++++++++++++++++ 5 files changed, 151 insertions(+), 55 deletions(-) create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 views/html/seminarybar/index.tpl diff --git a/agents/bottomlevel/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * 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 index 63ed4525..049ba904 100644 --- a/agents/toplevel/HtmlAgent.inc +++ b/agents/toplevel/HtmlAgent.inc @@ -39,6 +39,9 @@ { // Add menu $this->addSubAgent('Menu'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); } diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..7fd5d8fa --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,53 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + + + + + /** + * 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedCharacter', IntermediateController::$character); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/views/html/html.tpl b/views/html/html.tpl index c2bc1804..9cfcf8f3 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -47,61 +47,11 @@
                diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..b6fabcc2 --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,55 @@ +
                +

                Zyrendaniel

                + + +
                +
                +

                Letzter Speicherpunkt

                +

                Die verwunschene Stadt

                +
                +
                +

                Letzte Errungenschaft

                + +
                +
                +

                Wille und die Majas

                +
                  +
                • + +

                  Anduin

                  +

                  Level 27 (1500 XP)

                  +
                • +
                • + +

                  Jaina

                  +

                  Level 26 (1400 XP)

                  +
                • +
                • + +

                  Uther

                  +

                  Level 25 (1300 XP)

                  +
                • +
                • + +

                  Lothar

                  +

                  Level 24 (1200 XP)

                  +
                • +
                • + +

                  Morris

                  +

                  Level 23 (1100 XP)

                  +
                • +
                +

                Gildenprofil ansehen

                +
                From 96c15c04e1c9af84c793e69e7815e0f79ae63abc Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 11 Apr 2014 16:16:09 +0200 Subject: [PATCH 202/340] set values for Seminarybar --- controllers/SeminarybarController.inc | 44 +++++++++++++++++---------- models/CharactersModel.inc | 5 ++- models/QuestsModel.inc | 28 +++++++++++++++++ views/html/seminarybar/index.tpl | 22 ++++++++------ 4 files changed, 73 insertions(+), 26 deletions(-) diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc index 7fd5d8fa..04963719 100644 --- a/controllers/SeminarybarController.inc +++ b/controllers/SeminarybarController.inc @@ -20,25 +20,14 @@ */ class SeminarybarController extends \hhu\z\Controller { - - - - /** - * Prefilter. + * Required models * - * @param Request $request Current request - * @param Response $response Current response + * @var array */ - public function preFilter(\nre\core\Request $request, \nre\core\Response $response) - { - parent::preFilter($request, $response); - - // Set userdata - $this->set('loggedUser', IntermediateController::$user); - $this->set('loggedSeminary', IntermediateController::$seminary); - $this->set('loggedCharacter', IntermediateController::$character); - } + public $models = array('characters', 'quests', 'questgroups'); + + /** @@ -46,6 +35,29 @@ */ public function index() { + if(is_null(IntermediateController::$seminary)) { + return; + } + + // Get Seminary + $seminary = IntermediateController::$seminary; + + // Get Character + $character = IntermediateController::$character; + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); } } diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index a502916a..c659afec 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -105,9 +105,12 @@ 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE characters.user_id = ? AND charactertypes.seminary_id = ?', 'ii', $userId, $seminaryId diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index fb54519d..8a042196 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -297,6 +297,34 @@ } + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + /** diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl index b6fabcc2..8b2db16f 100644 --- a/views/html/seminarybar/index.tpl +++ b/views/html/seminarybar/index.tpl @@ -1,19 +1,23 @@
                -

                Zyrendaniel

                - +

                +
                + +
                -

                Letzter Speicherpunkt

                -

                Die verwunschene Stadt

                +

                +

                + +
                -

                Letzte Errungenschaft

                +

                • From bbe2135c4d5eea197a055841e62515beba4d7940 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 11 Apr 2014 16:16:37 +0200 Subject: [PATCH 203/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 5238 -> 6347 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 198 +++++++++++++------- 2 files changed, 132 insertions(+), 66 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index c7c0f88cbd25e2f99a9e433c21011571a283ba5a..7b3e368689c4e32b4f25e568e170f72901381a29 100644 GIT binary patch literal 6347 zcmai$dyE}b8NiP`R92CKJQNiVLa{B}Ww+K^Ze2^u?v~0HmUdexia7V)v-b`=cjh{e zU3L>6G=2iVgmlYGiPS*zAlqF z^P6+#JLi1o`@ZwdId#EV-&R~6<-?RK&s6Guc+ER`aP4@vQtyS=!3*Fd+yIZjOW@t` zeef9E0*}Ls;R#p&BfOgWoA5%o<(yo87aXE~GqmGD8UJqhet0iD4<2{?0elbjCmf%K z=Td(ja;ukkoDKg5WgV|U8TTwE`2f5Keh>~r`F~@4)e4 zDEs3@hzZpzQ0((Ml=b`%O1z%OW{~}|2}*qo%J_#NBB}+Y|2!1`+z+Myx1g-&$M8e& zr%?2K0e%d=3}yZ|;m6@cbQ1rML0Q*6DE7GvieARmdr;Oh4`uxCK$-UuD0cb@6!|9| zpM#i8z39Hb3@@erD%=9!a@@iqCEgA}(dSMm_J06whL1Tu4@IwkL7DdrD0-ZO($c;G ziajodVy7#hd>?h(0Yz>X6g?;4D4d3Mcnpf2o_72Vly&_TZi0V@qW}3APxQaW@ir*> zAA{oOuR+;2zko8|DJb@R6^h=kL(x-$<{EG=ybRXiFg)V;6)5vR1*PA!a4Y;BlsG+; zpb(x5QB7R}MZN}QA8dEr31wZ^Ly4PPq4aA)u}>F@J}H!S+y^CYzUufm6#3^M+rVC* z;wieyMHt!ZGd!=O$XK`A7Nx%ED0UW`N&FPo0bV{!*+`Lm{0T~N-NefdSHWENy3z3q zaGD}(xrHLO7Js9j{m%e3MiHCH^-0Q^6!D>4vIlnCxBMY{bst51D|XV9JrubB+ML61g>OqNPiTzJgwo!^}f|p4OE>s68GZcw+iIbZta@|R} zjk1d}MUm^O9M(T)@cwzqPS+;BzLLUrDz964yMrR}BA3L4#L(v`68oDeavh@VwQs}= zlsN3a?xG^TKTMHWk$62qvDfZq$2aq)Yr-^9yMrKFFfBdn2S%rhJ(H*#I$rEGQxogG zag_B|H|Se4lhC&k485*dQPs^TOg%qLv>ybf?FG77jJvX@_QQEE@LMZK@DrUzkq)A; ztv7YOdxrJKmK`P8ypCwniQ*JlKU`b37e_r4r~a~4%BrjJP%buJYIM1}qQP6;6q*H{ z7^d^$#X?Nlvc$w{+ysVk%I~i3z9r4r@1=ees)?4LR#LWRWhUY{ij{rpRu=nM!Fnm{ z1(DaP>y3$;IDPbZ*YgAIwOX-BRyUNUofQ9?dz!|y5}o){)DW>^RWIg-l`NvOd7ZD`eKT@r;+uDS2owV8yLPon*}>`(!qg)hAIfPpHmd zq2 zx(#3CL3Sj{;wpDli<3!aJR9vK}OL9lkf%=w0L)3)`93Hnkb!b^GddHr8F^6GQv= z?VqR=9337Rx`F>9zuwvmiMFm&b5DA0FW^VHGyk=jbnMW~WNk+!C)3TExHb_sqZWUG zy57<7)1ljIdrfG_38KezWaKY0I=p>&8(;EjZI73jo|ks&dU*KS{2rPPve*l1lTqAF z>N@P%YBIKUr_Nu;Hii1~F+I9@XEn}2eA+bfLrhFs*SUV>EC-DjH#^hJQyZ!4dEYD~ zeO;@MqjmiyIXKu;#*qBjvJhV-oC{{L2n~UkHTYwSr~FwIg!zwojwINQvtAFe^1tjF zYBtE-nL-5ZCLAY&S;?YBWAo0*9|oyr){`fK)NdPohw+;o##}8;-mL`J`M#9t0QXmC z6F1!E_2uWXc&R#{l=7vf2CXWKl+Bo|GDM$EG?AYg&R1f+{7oA_WILB#`O#+7Z(>*W zN#~7wOqiuhSgIPv$^|E%#48M13iP$`B{qHoD=I78z{3sWiw8^bzP3rz3HU+eAl3NY z+`lVMG5a|-)OOoLj8Wsh(Q>ruZ{%;f%p-`{II-<`f-HRUF>83+jKxTFkIE_LVJi~i z>6C@q7*i8|ID7I$M}R&^dR=5!H5wUH-6i z)eBy%^MU80I5yHPKlhN5f{0^)aVdDepSA;^%vQW$67fN79phdL^A(4mRE?9Nr}HCG zy3znEKMjb+HAL45#KTdwecb!oZk$4Hg@agI|Ca&kt(IIzyZm=H8Iy12z%(!amGZ*Lx5Zc%^_Bo3)-qm1|(9+EsBr_L`kClNFV>^+hST+8N6}4fe)L z(RJLhJDY8L4Yq5hIZC9uC3_=pW22KKQb{?pQNUTLvS1$7k~+ljmC2TC%xR1B2Y;zl z@(rhgGPxB0fx_sek@_3n)=&akL@(qzH^lpo{CBNF)bS87^L!EfwH{#!JsZ{+R;<1f9 delta 1884 zcmX}sU1-&19KiACpHt_Y+uYomrtPRRUA}B{&gE(OzPW7K9NkDnBqXLT5WP@|PSEsf zU93MMm=;}W*4(VpXd3X|9PI@^YTC6 zwmw*t8?7!m8Yun5Y~sulAq-&WL@tzjlR_xR`&f;Su?nY84&h0hhh><>8tlSK+!lQs zr_w$cJ&e<6595>&a^X`dj`ca3z}lE=~KPbuSvvsBD5kIi@mTktP*=^FUb1Q(+Tb)pGuL=)N>+Xv8uPM`x{ z!wi0n4)_OF;Gf7RJmQiwv&oF&53A6D+GD#LU5VXj2g68C;TSr@&!VT%y+0q@*UxaR$IyN+AfIq0$JGaKA!CG}(V5=HS$H3x#d1d14)hFcM+e@8 zuFyx=glD2ZqKP<7x(Jidf$PxwbI^(B=2LM=TH*t%qh08Oz1WKVn8iV)?{GExJ(};#-*x96KOy*UxJ>6712KQ`8~*) z3=;^8ld(Kkq_C}9V%@D?K)gUW#wy=`2k9ov(hl9vb;Nk-j1^abu?l51S7-fFv01oo zUa-f+Ut6FL7#TH;MlsAXx9|F17o^VF{=e7)Qji~DVPh43)W zwLw`+bP+3vImBkdGvVL+8p2o6v(@kZd#GGq%VNSM_SJH)mJ^mt;aWV1?bO}ZX9#wz zPeXneJ{%To&lAl}Nl%#v7 zT}ofC7%OdQ&$PB>+d9(j%FK9URsNmIFOuY~{KD$SBz?1{H-D{WFv<7T?n=_py1&y2 H_22vlx!#+r diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 3ea25979..696f6da2 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-05 13:37+0100\n" -"PO-Revision-Date: 2014-04-05 13:39+0100\n" +"POT-Creation-Date: 2014-04-11 16:16+0100\n" +"PO-Revision-Date: 2014-04-11 16:16+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -73,108 +73,165 @@ msgstr "ungelöst" msgid "Error" msgstr "Fehler" -#: views/html/charactergroups/group.tpl:5 -#: views/html/charactergroups/groupsgroup.tpl:2 -#: views/html/charactergroups/index.tpl:2 -#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:4 +#: views/html/achievements/index.tpl:7 views/html/characters/character.tpl:31 +#: views/html/seminarymenu/index.tpl:4 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/charactergroups/group.tpl:7 +#: views/html/charactergroups/groupsgroup.tpl:7 +#: views/html/charactergroups/index.tpl:7 +#: views/html/characters/character.tpl:55 views/html/seminarymenu/index.tpl:3 msgid "Character Groups" msgstr "Charaktergruppen" -#: views/html/charactergroups/group.tpl:22 -#: views/html/characters/character.tpl:2 views/html/characters/index.tpl:2 -#: views/html/seminarymenu/index.tpl:3 views/html/users/user.tpl:11 +#: views/html/charactergroups/group.tpl:18 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/charactergroups/group.tpl:20 +msgid "Members" +msgstr "Mitglieder" + +#: views/html/charactergroups/group.tpl:20 +msgid "Member" +msgstr "Mitglied" + +#: views/html/charactergroups/group.tpl:25 +#: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:13 msgid "Characters" msgstr "Charaktere" -#: views/html/charactergroups/group.tpl:60 +#: views/html/charactergroups/group.tpl:40 #: views/html/questgroups/questgroup.tpl:43 msgid "Quests" msgstr "Quests" -#: views/html/charactergroups/groupsgroup.tpl:12 -#: views/html/charactergroupsquests/quest.tpl:3 +#: views/html/charactergroups/groupsgroup.tpl:17 +#: views/html/charactergroupsquests/quest.tpl:7 msgid "Character Groups Quests" msgstr "Charactergruppen-Quests" -#: views/html/charactergroupsquests/quest.tpl:12 +#: views/html/charactergroupsquests/quest.tpl:17 msgid "Description" msgstr "Beschreibung" -#: views/html/charactergroupsquests/quest.tpl:15 +#: views/html/charactergroupsquests/quest.tpl:20 msgid "Rules" msgstr "Regeln" -#: views/html/charactergroupsquests/quest.tpl:22 +#: views/html/charactergroupsquests/quest.tpl:27 msgid "Won Quest" msgstr "Gewonnene Quest" -#: views/html/charactergroupsquests/quest.tpl:28 +#: views/html/charactergroupsquests/quest.tpl:33 msgid "Lost Quest" msgstr "Verlorene Quest" -#: views/html/characters/character.tpl:11 +#: views/html/characters/character.tpl:16 msgid "Total progress" msgstr "Fortschritt" -#: views/html/characters/character.tpl:15 views/html/characters/index.tpl:6 -#: views/html/users/user.tpl:14 +#: views/html/characters/character.tpl:20 views/html/users/user.tpl:27 msgid "Level" msgstr "Level" -#: views/html/characters/character.tpl:23 -msgid "Rank" -msgstr "Platz" +#: views/html/characters/register.tpl:7 +msgid "Create Character" +msgstr "Charakter erstellen" -#: views/html/characters/character.tpl:26 views/html/seminarymenu/index.tpl:5 -msgid "Achievements" -msgstr "Errungenschaften" +#: views/html/characters/register.tpl:20 +#, php-format +msgid "Character name is too short (min. %d chars)" +msgstr "Der Charaktername ist zu kurz (min. %d Zeichen)" + +#: views/html/characters/register.tpl:22 +#, php-format +msgid "Character name is too long (max. %d chars)" +msgstr "Der Charaktername ist zu lang (max. %d Zeichen)" + +#: views/html/characters/register.tpl:24 +msgid "Character name contains illegal characters" +msgstr "Der Charaktername enthält ungültige Zeichen" + +#: views/html/characters/register.tpl:26 +msgid "Character name invalid" +msgstr "Der Charaktername ist ungültig" + +#: views/html/characters/register.tpl:38 +msgid "Character properties" +msgstr "Charaktereigenschaften" + +#: views/html/characters/register.tpl:39 views/html/characters/register.tpl:40 +msgid "Character name" +msgstr "Charaktername" + +#: views/html/characters/register.tpl:41 +msgid "Character type" +msgstr "Charaktertyp" + +#: views/html/characters/register.tpl:52 +#, php-format +msgid "The Seminary field “%s” is invalid" +msgstr "Das Kursfeld „%s“ ist ungültig" + +#: views/html/characters/register.tpl:57 +msgid "Seminary fields" +msgstr "Kursfelder" + +#: views/html/characters/register.tpl:71 views/html/seminaries/create.tpl:9 +#: views/html/users/create.tpl:17 +msgid "create" +msgstr "erstellen" #: views/html/introduction/index.tpl:1 msgid "Introduction" msgstr "Einführung" -#: views/html/introduction/index.tpl:4 views/html/introduction/index.tpl:12 +#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 #: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 #: views/html/users/login.tpl:14 msgid "Login" msgstr "Login" -#: views/html/introduction/index.tpl:7 views/html/introduction/index.tpl:8 +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 #: 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:9 views/html/users/login.tpl:10 -#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +#: views/html/users/register.tpl:74 views/html/users/register.tpl:75 msgid "Username" msgstr "Benutzername" -#: views/html/introduction/index.tpl:9 views/html/introduction/index.tpl:10 +#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 #: views/html/users/create.tpl:14 views/html/users/create.tpl:15 #: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 #: views/html/users/login.tpl:11 views/html/users/login.tpl:12 -#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 msgid "Password" msgstr "Passwort" -#: views/html/introduction/index.tpl:13 +#: views/html/introduction/index.tpl:14 msgid "or" msgstr "oder" -#: views/html/introduction/index.tpl:13 +#: views/html/introduction/index.tpl:14 msgid "register yourself" msgstr "registriere dich" -#: views/html/menu/index.tpl:3 views/html/users/create.tpl:1 +#: views/html/menu/index.tpl:2 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/register.tpl:1 views/html/users/user.tpl:1 msgid "Users" msgstr "Benutzer" -#: views/html/menu/index.tpl:4 +#: views/html/menu/index.tpl:3 msgid "Usergroups" msgstr "Benutzergruppen" -#: views/html/menu/index.tpl:5 views/html/seminaries/create.tpl:1 +#: 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" @@ -218,10 +275,6 @@ msgstr "Neuer Kurs" msgid "Title" msgstr "Titel" -#: views/html/seminaries/create.tpl:9 views/html/users/create.tpl:17 -msgid "create" -msgstr "erstellen" - #: views/html/seminaries/delete.tpl:2 views/html/seminaries/seminary.tpl:39 msgid "Delete seminary" msgstr "Kurs löschen" @@ -247,16 +300,29 @@ msgstr "Kurs bearbeiten" msgid "save" msgstr "speichern" -#: views/html/seminaries/index.tpl:3 +#: views/html/seminaries/index.tpl:4 msgid "Create new seminary" msgstr "Neuen Kurs erstellen" -#: views/html/seminaries/index.tpl:10 views/html/seminaries/seminary.tpl:42 +#: views/html/seminaries/index.tpl:23 views/html/seminaries/seminary.tpl:42 #, php-format msgid "created by %s on %s" msgstr "erstellt von %s am %s" -#: views/html/seminarymenu/index.tpl:6 +#: views/html/seminaries/index.tpl:25 +msgid "Create a Character" +msgstr "Erstelle einen Charakter" + +#: views/html/seminaries/index.tpl:27 +#, php-format +msgid "Your Character “%s” has not been activated yet" +msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" + +#: views/html/seminarybar/index.tpl:14 +msgid "Last Quest" +msgstr "Letzter Speicherpunkt" + +#: views/html/seminarymenu/index.tpl:5 msgid "Library" msgstr "Bibliothek" @@ -266,23 +332,23 @@ msgstr "Neuer Benutzer" #: views/html/users/create.tpl:8 views/html/users/create.tpl:9 #: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 -#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 msgid "Prename" msgstr "Vorname" #: 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/register.tpl:80 views/html/users/register.tpl:81 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 msgid "Surname" msgstr "Nachname" #: views/html/users/create.tpl:12 views/html/users/create.tpl:13 #: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 -#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 msgid "E‑mail address" msgstr "E‑Mail-Adresse" -#: views/html/users/delete.tpl:2 views/html/users/user.tpl:5 +#: views/html/users/delete.tpl:2 views/html/users/user.tpl:6 msgid "Delete user" msgstr "Benutzer löschen" @@ -291,7 +357,7 @@ msgstr "Benutzer löschen" 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 +#: views/html/users/edit.tpl:2 views/html/users/user.tpl:5 msgid "Edit user" msgstr "Benutzer bearbeiten" @@ -299,7 +365,7 @@ msgstr "Benutzer bearbeiten" msgid "Create new user" msgstr "Neuen Benutzer erstellen" -#: views/html/users/index.tpl:10 views/html/users/user.tpl:8 +#: views/html/users/index.tpl:10 views/html/users/user.tpl:10 #, php-format msgid "registered on %s" msgstr "registriert am %s" @@ -312,85 +378,85 @@ msgstr "Die Anmeldung war nicht korrekt" msgid "Registration" msgstr "Registrierung" -#: views/html/users/register.tpl:15 +#: views/html/users/register.tpl:14 #, php-format msgid "Username is too short (min. %d chars)" msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:17 +#: views/html/users/register.tpl:16 #, php-format msgid "Username is too long (max. %d chars)" msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:19 +#: views/html/users/register.tpl:18 msgid "Username contains illegal characters" msgstr "Der Benutzername enthält ungültige Zeichen" -#: views/html/users/register.tpl:21 +#: views/html/users/register.tpl:20 msgid "Username invalid" msgstr "Der Benutzername ist ungültig" -#: views/html/users/register.tpl:26 +#: views/html/users/register.tpl:25 #, php-format msgid "Prename is too short (min. %d chars)" msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:28 +#: views/html/users/register.tpl:27 #, php-format msgid "Prename is too long (max. %d chars)" msgstr "Der Vorname ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:30 +#: views/html/users/register.tpl:29 #, php-format msgid "Prename contains illegal characters" msgstr "Der Vorname enthält ungültige Zeichen" -#: views/html/users/register.tpl:32 +#: views/html/users/register.tpl:31 msgid "Prename invalid" msgstr "Der Vorname ist ungültig" -#: views/html/users/register.tpl:37 +#: views/html/users/register.tpl:36 #, php-format msgid "Surname is too short (min. %d chars)" msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:39 +#: views/html/users/register.tpl:38 #, php-format msgid "Surname is too long (max. %d chars)" msgstr "Der Nachname ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:41 +#: views/html/users/register.tpl:40 #, php-format msgid "Surname contains illegal characters" msgstr "Der Nachname enthält ungültige Zeichen" -#: views/html/users/register.tpl:43 +#: views/html/users/register.tpl:42 msgid "Surname invalid" msgstr "Der Nachname ist ungültig" -#: views/html/users/register.tpl:48 views/html/users/register.tpl:50 +#: views/html/users/register.tpl:47 views/html/users/register.tpl:49 msgid "E‑mail address invalid" msgstr "Die E‑Mail-Adresse ist ungültig" -#: views/html/users/register.tpl:55 +#: views/html/users/register.tpl:54 #, php-format msgid "Password is too short (min. %d chars)" msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:57 +#: views/html/users/register.tpl:56 #, php-format msgid "Password is too long (max. %d chars)" msgstr "Das Passwort ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:59 +#: views/html/users/register.tpl:58 msgid "Password invalid" msgstr "Das Passwort ist ungültig" -#: views/html/users/register.tpl:87 +#: views/html/users/register.tpl:85 msgid "Register" msgstr "Registrieren" -#: views/html/users/user.tpl:18 +#: views/html/users/user.tpl:32 msgid "Roles" msgstr "Rollen" From da2cf9c38b3fd3297da1e53334bd62c7f137483e Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 11 Apr 2014 22:36:59 +0200 Subject: [PATCH 204/340] correct access of static properties of QuestsModel --- models/QuestsModel.inc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 8a042196..87cadc9b 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -185,7 +185,7 @@ */ public function setQuestEntered($questId, $characterId) { - $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_ENTERED, false); + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); } @@ -197,7 +197,7 @@ */ public function setQuestSubmitted($questId, $characterId) { - $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SUBMITTED); + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); } @@ -209,7 +209,7 @@ */ public function setQuestUnsolved($questId, $characterId) { - $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_UNSOLVED); + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); } @@ -221,7 +221,7 @@ */ public function setQuestSolved($questId, $characterId) { - $this->setQuestStatus($questId, $characterId, static::QUEST_STATUS_SOLVED, false); + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); } @@ -241,7 +241,7 @@ 'iiiii', $questId, $characterId, - static::QUEST_STATUS_ENTERED, static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED ); @@ -265,7 +265,7 @@ 'iiii', $questId, $characterId, - static::QUEST_STATUS_SOLVED, static::QUEST_STATUS_UNSOLVED + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED ); @@ -289,7 +289,7 @@ 'iii', $questId, $characterId, - static::QUEST_STATUS_SOLVED + self::QUEST_STATUS_SOLVED ); From 7157edb732ad467a9268521b61c7c4fd78607648 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 11 Apr 2014 22:37:37 +0200 Subject: [PATCH 205/340] correctly fetch media in QuestsController and -View --- controllers/QuestsController.inc | 8 ++++---- views/html/quests/submission.tpl | 4 ++-- views/html/quests/submissions.tpl | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index e930cbd6..b630a134 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -265,7 +265,7 @@ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); $questgroup['picture'] = null; if(!is_null($questgroup['questgroupspicture_id'])) { - $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); } // Get Quest @@ -274,7 +274,7 @@ // Media $questmedia = null; if(!is_null($quest['questsmedia_id'])) { - $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); } // Get submitted Character submissions @@ -316,7 +316,7 @@ $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); $questgroup['picture'] = null; if(!is_null($questgroup['questgroupspicture_id'])) { - $questgroup['picture'] = $this->Media->getMediaById($questgroup['questgroupspicture_id']); + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); } // Get Quest @@ -328,7 +328,7 @@ // Media $questmedia = null; if(!is_null($quest['questsmedia_id'])) { - $questmedia = $this->Media->getMediaById($quest['questsmedia_id']); + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); } // Questtype diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl index 9d13af65..a29700db 100644 --- a/views/html/quests/submission.tpl +++ b/views/html/quests/submission.tpl @@ -1,6 +1,6 @@
                  - +

                  @@ -8,7 +8,7 @@

                  - +

                  diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl index 538b8565..e2a17579 100644 --- a/views/html/quests/submissions.tpl +++ b/views/html/quests/submissions.tpl @@ -1,6 +1,6 @@
                  - +

                  @@ -8,7 +8,7 @@

                  - +
                  From da5f54cdb68aae57131280314a4188627754dc0b Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 11 Apr 2014 22:38:10 +0200 Subject: [PATCH 206/340] implement Questtype ?Choiceinput? --- .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 +++ .../ChoiceinputQuesttypeController.inc | 157 ++++++++++++++++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 +++++++++++++++++ questtypes/choiceinput/html/quest.tpl | 15 ++ questtypes/choiceinput/html/submission.tpl | 12 ++ 5 files changed, 361 insertions(+) create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..08029501 --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
                  + &$text) : ?> + 0) : ?> + + + + + +

                  + +
                  diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..200b3c7f --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
                  + &$text) : ?> + 0) : ?> + + + + +
                  From aeaa7a2c6239e5c71a405a5a81d3592dfd0e23ed Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 12 Apr 2014 03:25:18 +0200 Subject: [PATCH 207/340] implement Questtype ?crossword? --- .../crossword/CrosswordQuesttypeAgent.inc | 24 ++ .../CrosswordQuesttypeController.inc | 355 ++++++++++++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 +++++ questtypes/crossword/html/quest.tpl | 26 ++ questtypes/crossword/html/submission.tpl | 23 ++ 5 files changed, 521 insertions(+) create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..e08e1c29 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,355 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null, + 'right' => false + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null, + 'right' => false + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..3965b69c --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,26 @@ +
                  + + + + + + + + + + +
                  + + + +
                  + +
                    + +
                  • + +
                  + +

                  + +
                  diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..60f239c3 --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,23 @@ +
                  + + + + + + + + + + +
                  + + + +
                  + +
                    + +
                  • + +
                  +
                  From d256ad3c45c1d669de7ae335498313cad1588620 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 12 Apr 2014 12:21:06 +0200 Subject: [PATCH 208/340] remove link to user groups (Issue #2) --- views/html/menu/index.tpl | 1 - 1 file changed, 1 deletion(-) diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index 8c61c817..c2662789 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,6 +1,5 @@
                • The Legend of Z
                • 0) : ?>
                • -
                • From 632382844aa93b1d7f12fdcefb7cc7f515f58929 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 12 Apr 2014 13:07:55 +0200 Subject: [PATCH 209/340] add method for Seminary header media (with lower permissions) --- controllers/CharactergroupsController.inc | 9 - .../CharactergroupsquestsController.inc | 3 - controllers/CharactersController.inc | 13 -- controllers/MediaController.inc | 173 ++++++++++-------- controllers/SeminariesController.inc | 12 -- models/SeminariesModel.inc | 6 +- views/html/charactergroups/group.tpl | 4 +- views/html/charactergroups/groupsgroup.tpl | 4 +- views/html/charactergroups/index.tpl | 4 +- views/html/charactergroupsquests/quest.tpl | 4 +- views/html/characters/character.tpl | 4 +- views/html/characters/index.tpl | 4 +- views/html/characters/register.tpl | 4 +- views/html/seminaries/create.tpl | 5 + views/html/seminaries/delete.tpl | 5 + views/html/seminaries/edit.tpl | 5 + views/html/seminaries/index.tpl | 4 +- views/html/seminaries/seminary.tpl | 4 +- 18 files changed, 136 insertions(+), 131 deletions(-) diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc index 594cba12..d4cf6cee 100644 --- a/controllers/CharactergroupsController.inc +++ b/controllers/CharactergroupsController.inc @@ -57,9 +57,6 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } // Get Character groups-groups $groupsgroups = $this->Charactergroups->getGroupsroupsForSeminary($seminary['id']); @@ -85,9 +82,6 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); @@ -122,9 +116,6 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc index 478d8700..fb9d18c8 100644 --- a/controllers/CharactergroupsquestsController.inc +++ b/controllers/CharactergroupsquestsController.inc @@ -61,9 +61,6 @@ { // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } // Get Character groups-group $groupsgroup = $this->Charactergroups->getGroupsgroupByUrl($seminary['id'], $groupsgroupUrl); diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 8486a2fc..e0a14362 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -66,9 +66,6 @@ { // Get Seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } // Get registered Characters $characters = $this->Characters->getCharactersForSeminary($seminary['id']); @@ -107,9 +104,6 @@ // Get Seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } // Get Character $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); @@ -216,18 +210,11 @@ } } - // Medium - $media = null; - if(!is_null($seminary['media_id'])) { - $media = $this->Media->getMediaById($seminary['media_id']); - } - // Pass data to view $this->set('seminary', $seminary); $this->set('types', $types); $this->set('fields', $fields); - $this->set('media', $media); $this->set('charactername', $charactername); $this->set('validation', $validation); $this->set('fieldsValidation', $fieldsValidation); diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 0afe93fb..35105c4a 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -25,8 +25,10 @@ * @var array */ public $permissions = array( - 'index' => array('admin', 'moderator', 'user', 'guest'), - 'seminary' => array('admin', 'moderator', 'user', 'guest') + 'index' => array('admin', 'moderator', 'user', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'avatar' => array('admin', 'moderator', 'user') ); /** * User seminary permissions @@ -70,44 +72,48 @@ * Display a medium. * * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media */ public function index($mediaUrl, $action=null) { // Get Media $media = $this->Media->getMediaByUrl($mediaUrl); - // Get 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'])) { + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { 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; - default: - throw new ParamsNotValidException($action); - break; - } + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } // Pass data to view @@ -124,6 +130,7 @@ * @throws IdNotFoundException * @param string $seminaryUrl URL-title of the Seminary * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media */ public function seminary($seminaryUrl, $mediaUrl, $action=null) { @@ -133,52 +140,12 @@ // Get Media $media = $this->Media->getSeminaryMediaByUrl($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['seminarymedia'].DS.$media['id']; - if(!file_exists($media['filename'])) { - throw new \nre\exceptions\IdNotFoundException($mediaUrl); - } - - // Cache - if($this->setCacheHeaders($media['filename'])) { + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { 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), self::getImageTypes())) { - $file = file_get_contents($media['filename']); - } - else - { - $file = self::resizeImage( - $media['filename'], - $format, - 480 - ); - } - break; - default: - throw new ParamsNotValidException($action); - break; - } - - // Pass data to view $this->set('media', $media); @@ -225,6 +192,66 @@ } + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + 480 + ); + } + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } /** diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 50d1aac5..771c9787 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -69,11 +69,6 @@ // Created user $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); - // Media - if(!is_null($seminary['media_id'])) { - $seminary['media'] = $this->Media->getMediaById($seminary['media_id']); - } - // Character of currently logged-in user try { $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); @@ -150,17 +145,10 @@ } } - // Medium - $media = null; - if(!is_null($seminary['media_id'])) { - $media = $this->Media->getMediaById($seminary['media_id']); - } - // Pass data to view $this->set('seminary', $seminary); $this->set('questgroupshierarchy', $questgroupshierarchy); - $this->set('media', $media); } diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index 364e82e2..251ade4f 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -49,7 +49,7 @@ { // Get seminaries return $this->db->query( - 'SELECT id, created, created_user_id, title, url, description, media_id '. + 'SELECT id, created, created_user_id, title, url, description, seminarymedia_id '. 'FROM seminaries '. 'ORDER BY created DESC' ); @@ -66,7 +66,7 @@ public function getSeminaryById($seminaryId) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url, description, media_id '. + 'SELECT id, created, created_user_id, title, url, description, seminarymedia_id '. 'FROM seminaries '. 'WHERE id = ?', 'i', @@ -91,7 +91,7 @@ public function getSeminaryByUrl($seminaryUrl) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url, description, media_id '. + 'SELECT id, created, created_user_id, title, url, description, seminarymedia_id '. 'FROM seminaries '. 'WHERE url = ?', 's', diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index 9a66595d..ba53c97b 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl index c604328a..93aeaba7 100644 --- a/views/html/charactergroups/groupsgroup.tpl +++ b/views/html/charactergroups/groupsgroup.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl index 483c0f2e..e0f54b94 100644 --- a/views/html/charactergroups/index.tpl +++ b/views/html/charactergroups/index.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl index 6837cfff..8112a913 100644 --- a/views/html/charactergroupsquests/quest.tpl +++ b/views/html/charactergroupsquests/quest.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index c347a9d0..d597f182 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl index 478c0a65..a1227a5a 100644 --- a/views/html/characters/index.tpl +++ b/views/html/characters/index.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl index f6d2b15e..229dfaaa 100644 --- a/views/html/characters/register.tpl +++ b/views/html/characters/register.tpl @@ -1,6 +1,6 @@ - +
                  - +

                  diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl index 823eec70..30938357 100644 --- a/views/html/seminaries/create.tpl +++ b/views/html/seminaries/create.tpl @@ -1,3 +1,8 @@ + +
                  + +
                  +

                  diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl index d2253f14..8e53fdb5 100644 --- a/views/html/seminaries/delete.tpl +++ b/views/html/seminaries/delete.tpl @@ -1,3 +1,8 @@ + +
                  + +
                  +

                  diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl index 007acf15..66d7efcd 100644 --- a/views/html/seminaries/edit.tpl +++ b/views/html/seminaries/edit.tpl @@ -1,3 +1,8 @@ + +
                  + +
                  +

                  diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index 4ad92986..986991a9 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -7,8 +7,8 @@
                  • - - + +

                    0) : ?> diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 2f7638f7..7697d758 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -1,6 +1,6 @@ - +
                    - +
                    From 2437f331aa052410fb128afbf6084213469059aa Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 13 Apr 2014 15:10:35 +0200 Subject: [PATCH 210/340] implement Achievements --- agents/intermediate/AchievementsAgent.inc | 35 ++ app/controllers/SeminaryRoleController.inc | 134 ++++++ controllers/AchievementsController.inc | 162 +++++++ controllers/CharactersController.inc | 4 + controllers/MediaController.inc | 54 ++- controllers/SeminarybarController.inc | 7 +- models/AchievementsModel.inc | 471 +++++++++++++++++++++ models/MediaModel.inc | 17 +- views/html/achievements/index.tpl | 28 ++ views/html/characters/character.tpl | 30 +- views/html/seminarybar/index.tpl | 11 +- 11 files changed, 914 insertions(+), 39 deletions(-) create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 views/html/achievements/index.tpl diff --git a/agents/intermediate/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index 36a9b439..9c06fdf1 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -73,6 +73,9 @@ // Check permissions $this->checkPermission($request, $response); + + // Check achievements + $this->checkAchievements($request, $response); } @@ -131,6 +134,137 @@ } } + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Get Seminary + $seminary = self::$seminary; + + // Get Character + $character = self::$character; + + // Get unachieved Achievments + $achievements = array_merge( + $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id']), + $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary($seminary['id']) + ); + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], $character['id']); + } + } + } + } ?> diff --git a/controllers/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..b6ef6595 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,162 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Achievements + $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); + foreach($achievements as &$achievement) + { + // Get status for Character + $achieved = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + + // Get Character progress + if(!$achieved && $achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + + // Get media + $achievement['media_index'] = 'unachieved_achievementsmedia_id'; + if($achieved) { + $achievement['media_index'] = 'achieved_achievementsmedia_id'; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('achievements', $achievements); + } + + } + +?> diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index e0a14362..fbeb0ead 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -120,6 +120,9 @@ // Get Character groups $groups = $this->Charactergroups->getGroupsForCharacter($character['id']); + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + // Pass data to view $this->set('seminary', $seminary); @@ -127,6 +130,7 @@ $this->set('characterfields', $characterfields); $this->set('user', $user); $this->set('groups', $groups); + $this->set('achievements', $achievements); } diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 35105c4a..4036954b 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -28,7 +28,7 @@ 'index' => array('admin', 'moderator', 'user', 'guest'), 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), 'seminary' => array('admin', 'moderator', 'user', 'guest'), - 'avatar' => array('admin', 'moderator', 'user') + 'achievement' => array('admin', 'moderator', 'user', 'guest') ); /** * User seminary permissions @@ -36,14 +36,15 @@ * @var array */ public $seminaryPermissions = array( - 'seminary' => array('admin', 'moderator', 'user', 'guest') + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') ); /** * Required models * * @var array */ - public $models = array('seminaries', 'media'); + public $models = array('seminaries', 'achievements', 'media'); @@ -153,6 +154,53 @@ } + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + /** diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc index 04963719..d3253096 100644 --- a/controllers/SeminarybarController.inc +++ b/controllers/SeminarybarController.inc @@ -25,7 +25,7 @@ * * @var array */ - public $models = array('characters', 'quests', 'questgroups'); + public $models = array('characters', 'quests', 'questgroups', 'achievements'); @@ -53,11 +53,16 @@ $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); } + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + // Pass data to view $this->set('seminary', $seminary); $this->set('character', $character); $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); } } diff --git a/models/AchievementsModel.inc b/models/AchievementsModel.inc index 760c9f06..a426efa2 100644 --- a/models/AchievementsModel.inc +++ b/models/AchievementsModel.inc @@ -31,6 +31,477 @@ parent::__construct(); } + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all Achievements of a Seminary. + * + * @param int $seminaryId ID of Seminary to get Achievements of + * @return array Achievements data + */ + public function getAchievementsForSeminary($seminaryId, $includeHidden=false) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND hidden <= ?', + 'ii', + $seminaryId, $includeHidden + ); + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ')', + 'ii', + $seminaryId, + $characterId + ); + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + + + return !empty($data); + } + } ?> diff --git a/models/MediaModel.inc b/models/MediaModel.inc index 34e0c0e8..2c2d5eae 100644 --- a/models/MediaModel.inc +++ b/models/MediaModel.inc @@ -88,21 +88,21 @@ * Get a Seminary 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 + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data */ - public function getSeminaryMediaByUrl($seminaryId, $mediaUrl) + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) { $data = $this->db->query( 'SELECT id, name, url, description, mimetype '. 'FROM seminarymedia '. 'WHERE url = ?', 's', - $mediaUrl + $seminaryMediaUrl ); if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException($mediaUrl); + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); } @@ -114,9 +114,8 @@ * Get a Seminary medium by its ID. * * @throws IdNotFoundException - * @param int $seminaryId ID of the seminary - * @param int $mediaId ID of the Medium - * @return array Medium data + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data */ public function getSeminaryMediaById($mediaId) { diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..62c99c1a --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,28 @@ + +
                    + +
                    + +

                    +

                    + +
                      + +
                    • + + + +

                      +
                      + + +
                      +
                      + +
                      +

                       %

                      +
                      + +
                    • + +
                    diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index d597f182..63054c06 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -67,31 +67,15 @@

                    Neue Achievements

                    diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl index 8b2db16f..0879970c 100644 --- a/views/html/seminarybar/index.tpl +++ b/views/html/seminarybar/index.tpl @@ -16,16 +16,21 @@

                  +

                  • - -

                    Des Königs neue Quests

                    -

                    erreicht am: 14.07.2014

                    + + + +

                    +

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

                  + +

                  Wille und die Majas

                    From fb10e2d01e889a128dd8e7fade497d68f0c788ba Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 13 Apr 2014 15:36:53 +0200 Subject: [PATCH 211/340] correct .hgignore --- .hgignore | 10 +++++----- views/binary/media/achievement.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl diff --git a/.hgignore b/.hgignore index 1ce7ce9e..43dff51a 100644 --- a/.hgignore +++ b/.hgignore @@ -1,5 +1,5 @@ -syntax: glob -media/* -tmp/* -uploads/* -seminarymedia/* +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* diff --git a/views/binary/media/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + From 0bb56c4e33d772d1850524636c700c6ef15d2791 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 14 Apr 2014 15:35:05 +0200 Subject: [PATCH 212/340] check logged Character for NULL --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 ++++++++++++ agents/bottomlevel/MenuAgent.inc | 37 ++ .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarybarAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/AchievementsAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 ++ agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 70 ++ apis/WebApi.inc | 250 ++++++++ app/Controller.inc | 103 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 267 ++++++++ app/QuesttypeController.inc | 349 ++++++++++ app/QuesttypeModel.inc | 154 +++++ app/QuesttypeView.inc | 76 +++ app/ToplevelAgent.inc | 36 ++ app/Utils.inc | 83 +++ app/controllers/IntermediateController.inc | 168 +++++ app/controllers/SeminaryRoleController.inc | 273 ++++++++ app/exceptions/FileUploadException.inc | 75 +++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 +++ .../QuesttypeAgentNotValidException.inc | 77 +++ .../QuesttypeControllerNotFoundException.inc | 77 +++ .../QuesttypeControllerNotValidException.inc | 77 +++ .../QuesttypeModelNotFoundException.inc | 77 +++ .../QuesttypeModelNotValidException.inc | 77 +++ .../SubmissionNotValidException.inc | 77 +++ app/exceptions/WrongFiletypeException.inc | 75 +++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 168 +++++ configs/CoreConfig.inc | 167 +++++ controllers/AchievementsController.inc | 162 +++++ controllers/BinaryController.inc | 37 ++ controllers/CharactergroupsController.inc | 150 +++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 229 +++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 ++ controllers/MediaController.inc | 368 +++++++++++ controllers/MenuController.inc | 51 ++ controllers/QuestgroupsController.inc | 179 ++++++ .../QuestgroupshierarchypathController.inc | 90 +++ controllers/QuestsController.inc | 595 +++++++++++++++++ controllers/SeminariesController.inc | 252 ++++++++ controllers/SeminarybarController.inc | 73 +++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 +++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 328 ++++++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ .../components/ValidationComponent.inc | 115 ++++ core/Agent.inc | 607 ++++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 433 +++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 6347 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 507 +++++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 507 +++++++++++++++ models/AvatarsModel.inc | 62 ++ models/CharactergroupsModel.inc | 172 +++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 396 ++++++++++++ models/CharactertypesModel.inc | 57 ++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 139 ++++ models/QuestgroupsModel.inc | 589 +++++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 ++++ models/QuestsModel.inc | 372 +++++++++++ models/QuesttextsModel.inc | 114 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 193 ++++++ models/SeminarycharacterfieldsModel.inc | 78 +++ models/UploadsModel.inc | 148 +++++ models/UserrolesModel.inc | 77 +++ models/UsersModel.inc | 278 ++++++++ models/UserseminaryrolesModel.inc | 78 +++ .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 + .../ChoiceinputQuesttypeController.inc | 157 +++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 +++++ questtypes/choiceinput/html/quest.tpl | 15 + questtypes/choiceinput/html/submission.tpl | 12 + .../crossword/CrosswordQuesttypeAgent.inc | 24 + .../CrosswordQuesttypeController.inc | 355 ++++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 +++ questtypes/crossword/html/quest.tpl | 26 + questtypes/crossword/html/submission.tpl | 23 + .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 199 ++++++ .../dragndrop/DragndropQuesttypeModel.inc | 146 +++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 261 ++++++++ .../MultiplechoiceQuesttypeModel.inc | 159 +++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 +++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 +++++ .../textinput/TextinputQuesttypeModel.inc | 117 ++++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 ++++++++++++ responses/WebResponse.inc | 250 ++++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/achievement.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/achievements/index.tpl | 28 + views/html/charactergroups/group.tpl | 52 ++ views/html/charactergroups/groupsgroup.tpl | 22 + views/html/charactergroups/index.tpl | 13 + views/html/charactergroupsquests/quest.tpl | 51 ++ views/html/characters/character.tpl | 149 +++++ views/html/characters/index.tpl | 19 + views/html/characters/register.tpl | 72 +++ views/html/error/index.tpl | 2 + views/html/html.tpl | 58 ++ views/html/introduction/index.tpl | 36 ++ views/html/menu/index.tpl | 9 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 96 +++ views/html/quests/submission.tpl | 17 + views/html/quests/submissions.tpl | 41 ++ views/html/seminaries/create.tpl | 15 + views/html/seminaries/delete.tpl | 13 + views/html/seminaries/edit.tpl | 15 + views/html/seminaries/index.tpl | 33 + views/html/seminaries/seminary.tpl | 43 ++ views/html/seminarybar/index.tpl | 64 ++ views/html/seminarymenu/index.tpl | 6 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 86 +++ views/html/users/user.tpl | 33 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 237 +++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 ++++ 229 files changed, 21466 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/AvatarsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/CharactertypesModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/achievements/index.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/characters/register.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarybar/index.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..43dff51a --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* 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..49512791 --- /dev/null +++ b/agents/bottomlevel/MenuAgent.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\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) + { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); + } + + } + +?> 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/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..049ba904 --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,70 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); + } + + + + + 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..5731aeed --- /dev/null +++ b/app/Controller.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; + + + /** + * 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 and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..5b980299 --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,168 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + + // Determine user roles + self::$user['roles'] = array(); + $roles = $this->Userroles->getUserrolesForUserById(self::$user['id']); + foreach($roles as &$role) { + self::$user['roles'][] = $role['name']; + } + + // Character + $controller = $this->agent->controller; + if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) + { + $seminaryUrl = $this->request->getParam(3); + self::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..324e13e2 --- /dev/null +++ b/app/controllers/SeminaryRoleController.inc @@ -0,0 +1,273 @@ + + * @copyright 2014 Heinrich-Heine-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\controllers\IntermediateController + { + /** + * 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); + + // Check achievements + $this->checkAchievements($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(); + } + } + + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Get Seminary + $seminary = self::$seminary; + + // Get Character + $character = self::$character; + if(is_null($character)) { + return; + } + + // Get unachieved Achievments + $achievements = array_merge( + $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id']), + $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary($seminary['id']) + ); + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], $character['id']); + } + } + } + + } + +?> diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..45ccb0fe --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,168 @@ + + * @copyright 2014 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ) + ); + + + /** + * 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|register|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|register))', '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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..b6ef6595 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,162 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Achievements + $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); + foreach($achievements as &$achievement) + { + // Get status for Character + $achieved = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + + // Get Character progress + if(!$achieved && $achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + + // Get media + $achievement['media_index'] = 'unachieved_achievementsmedia_id'; + if($achieved) { + $achievement['media_index'] = 'achieved_achievementsmedia_id'; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('achievements', $achievements); + } + + } + +?> diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc new file mode 100644 index 00000000..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..d4cf6cee --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,150 @@ + + * @copyright 2014 Heinrich-Heine-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', 'avatars', '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: 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); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); + + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + // Get Character groups Quests + $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc new file mode 100644 index 00000000..fb9d18c8 --- /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->getSeminaryMediaById($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..fbeb0ead --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,229 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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']); + + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($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); + $this->set('achievements', $achievements); + } + + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + + } + +?> 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..7bf8859e --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedCharacter', IntermediateController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..4036954b --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,368 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'achievements', '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. + * + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + + + /** + * 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; + } + + + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + 480 + ); + } + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } + + + /** + * Get supported image types. + * + * @return array List of supported image types + */ + private static function getImageTypes() + { + $im = new \Imagick(); + + + return $im->queryFormats(); + } + + + /** + * Resize an image. + * + * @param string $fileName Absolute pathname of image to resize + * @param string $mimeType Mimetype of target image + * @param int $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..2be5ea8c --- /dev/null +++ b/controllers/MenuController.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..d7b8acf1 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,179 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && $currentQuest['solved']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..b630a134 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,595 @@ + + * @copyright 2014 Heinrich-Heine-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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + //$questtexttypesUrls = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) + { + switch($questStatus) + { + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; + } + } + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('queststatustext', $questStatusText); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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 + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..771c9787 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,252 @@ + + * @copyright 2014 Heinrich-Heine-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', 'userseminaryroles', '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) + { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($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 + $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/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..1a36f136 --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,73 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('characters', 'quests', 'questgroups', 'achievements'); + + + + + /** + * Action: index. + */ + public function index() + { + if(is_null(IntermediateController::$seminary)) { + return; + } + + // Get Seminary + $seminary = IntermediateController::$seminary; + + // Get Character + $character = IntermediateController::$character; + if(is_null($character)) { + return; + } + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); + } + + } + +?> diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..d827425d --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..535dcbbe --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,328 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + + // Level + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..dbe5b0b3 --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,115 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $settings) + { + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                    \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..7b3e368689c4e32b4f25e568e170f72901381a29 GIT binary patch literal 6347 zcmai$dyE}b8NiP`R92CKJQNiVLa{B}Ww+K^Ze2^u?v~0HmUdexia7V)v-b`=cjh{e zU3L>6G=2iVgmlYGiPS*zAlqF z^P6+#JLi1o`@ZwdId#EV-&R~6<-?RK&s6Guc+ER`aP4@vQtyS=!3*Fd+yIZjOW@t` zeef9E0*}Ls;R#p&BfOgWoA5%o<(yo87aXE~GqmGD8UJqhet0iD4<2{?0elbjCmf%K z=Td(ja;ukkoDKg5WgV|U8TTwE`2f5Keh>~r`F~@4)e4 zDEs3@hzZpzQ0((Ml=b`%O1z%OW{~}|2}*qo%J_#NBB}+Y|2!1`+z+Myx1g-&$M8e& zr%?2K0e%d=3}yZ|;m6@cbQ1rML0Q*6DE7GvieARmdr;Oh4`uxCK$-UuD0cb@6!|9| zpM#i8z39Hb3@@erD%=9!a@@iqCEgA}(dSMm_J06whL1Tu4@IwkL7DdrD0-ZO($c;G ziajodVy7#hd>?h(0Yz>X6g?;4D4d3Mcnpf2o_72Vly&_TZi0V@qW}3APxQaW@ir*> zAA{oOuR+;2zko8|DJb@R6^h=kL(x-$<{EG=ybRXiFg)V;6)5vR1*PA!a4Y;BlsG+; zpb(x5QB7R}MZN}QA8dEr31wZ^Ly4PPq4aA)u}>F@J}H!S+y^CYzUufm6#3^M+rVC* z;wieyMHt!ZGd!=O$XK`A7Nx%ED0UW`N&FPo0bV{!*+`Lm{0T~N-NefdSHWENy3z3q zaGD}(xrHLO7Js9j{m%e3MiHCH^-0Q^6!D>4vIlnCxBMY{bst51D|XV9JrubB+ML61g>OqNPiTzJgwo!^}f|p4OE>s68GZcw+iIbZta@|R} zjk1d}MUm^O9M(T)@cwzqPS+;BzLLUrDz964yMrR}BA3L4#L(v`68oDeavh@VwQs}= zlsN3a?xG^TKTMHWk$62qvDfZq$2aq)Yr-^9yMrKFFfBdn2S%rhJ(H*#I$rEGQxogG zag_B|H|Se4lhC&k485*dQPs^TOg%qLv>ybf?FG77jJvX@_QQEE@LMZK@DrUzkq)A; ztv7YOdxrJKmK`P8ypCwniQ*JlKU`b37e_r4r~a~4%BrjJP%buJYIM1}qQP6;6q*H{ z7^d^$#X?Nlvc$w{+ysVk%I~i3z9r4r@1=ees)?4LR#LWRWhUY{ij{rpRu=nM!Fnm{ z1(DaP>y3$;IDPbZ*YgAIwOX-BRyUNUofQ9?dz!|y5}o){)DW>^RWIg-l`NvOd7ZD`eKT@r;+uDS2owV8yLPon*}>`(!qg)hAIfPpHmd zq2 zx(#3CL3Sj{;wpDli<3!aJR9vK}OL9lkf%=w0L)3)`93Hnkb!b^GddHr8F^6GQv= z?VqR=9337Rx`F>9zuwvmiMFm&b5DA0FW^VHGyk=jbnMW~WNk+!C)3TExHb_sqZWUG zy57<7)1ljIdrfG_38KezWaKY0I=p>&8(;EjZI73jo|ks&dU*KS{2rPPve*l1lTqAF z>N@P%YBIKUr_Nu;Hii1~F+I9@XEn}2eA+bfLrhFs*SUV>EC-DjH#^hJQyZ!4dEYD~ zeO;@MqjmiyIXKu;#*qBjvJhV-oC{{L2n~UkHTYwSr~FwIg!zwojwINQvtAFe^1tjF zYBtE-nL-5ZCLAY&S;?YBWAo0*9|oyr){`fK)NdPohw+;o##}8;-mL`J`M#9t0QXmC z6F1!E_2uWXc&R#{l=7vf2CXWKl+Bo|GDM$EG?AYg&R1f+{7oA_WILB#`O#+7Z(>*W zN#~7wOqiuhSgIPv$^|E%#48M13iP$`B{qHoD=I78z{3sWiw8^bzP3rz3HU+eAl3NY z+`lVMG5a|-)OOoLj8Wsh(Q>ruZ{%;f%p-`{II-<`f-HRUF>83+jKxTFkIE_LVJi~i z>6C@q7*i8|ID7I$M}R&^dR=5!H5wUH-6i z)eBy%^MU80I5yHPKlhN5f{0^)aVdDepSA;^%vQW$67fN79phdL^A(4mRE?9Nr}HCG zy3znEKMjb+HAL45#KTdwecb!oZk$4Hg@agI|Ca&kt(IIzyZm=H8Iy12z%(!amGZ*Lx5Zc%^_Bo3)-qm1|(9+EsBr_L`kClNFV>^+hST+8N6}4fe)L z(RJLhJDY8L4Yq5hIZC9uC3_=pW22KKQb{?pQNUTLvS1$7k~+ljmC2TC%xR1B2Y;zl z@(rhgGPxB0fx_sek@_3n)=&akL@(qzH^lpo{CBNF)bS87^L!EfwH{#!JsZ{+R;<1f9 literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..696f6da2 --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,507 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-11 16:16+0100\n" +"PO-Revision-Date: 2014-04-11 16:16+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:40 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:42 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/achievements/index.tpl:7 views/html/characters/character.tpl:31 +#: views/html/seminarymenu/index.tpl:4 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/charactergroups/group.tpl:7 +#: views/html/charactergroups/groupsgroup.tpl:7 +#: views/html/charactergroups/index.tpl:7 +#: views/html/characters/character.tpl:55 views/html/seminarymenu/index.tpl:3 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:18 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/charactergroups/group.tpl:20 +msgid "Members" +msgstr "Mitglieder" + +#: views/html/charactergroups/group.tpl:20 +msgid "Member" +msgstr "Mitglied" + +#: views/html/charactergroups/group.tpl:25 +#: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:13 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:40 +#: views/html/questgroups/questgroup.tpl:43 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:17 +#: views/html/charactergroupsquests/quest.tpl:7 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:17 +msgid "Description" +msgstr "Beschreibung" + +#: views/html/charactergroupsquests/quest.tpl:20 +msgid "Rules" +msgstr "Regeln" + +#: views/html/charactergroupsquests/quest.tpl:27 +msgid "Won Quest" +msgstr "Gewonnene Quest" + +#: views/html/charactergroupsquests/quest.tpl:33 +msgid "Lost Quest" +msgstr "Verlorene Quest" + +#: views/html/characters/character.tpl:16 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:20 views/html/users/user.tpl:27 +msgid "Level" +msgstr "Level" + +#: views/html/characters/register.tpl:7 +msgid "Create Character" +msgstr "Charakter erstellen" + +#: views/html/characters/register.tpl:20 +#, php-format +msgid "Character name is too short (min. %d chars)" +msgstr "Der Charaktername ist zu kurz (min. %d Zeichen)" + +#: views/html/characters/register.tpl:22 +#, php-format +msgid "Character name is too long (max. %d chars)" +msgstr "Der Charaktername ist zu lang (max. %d Zeichen)" + +#: views/html/characters/register.tpl:24 +msgid "Character name contains illegal characters" +msgstr "Der Charaktername enthält ungültige Zeichen" + +#: views/html/characters/register.tpl:26 +msgid "Character name invalid" +msgstr "Der Charaktername ist ungültig" + +#: views/html/characters/register.tpl:38 +msgid "Character properties" +msgstr "Charaktereigenschaften" + +#: views/html/characters/register.tpl:39 views/html/characters/register.tpl:40 +msgid "Character name" +msgstr "Charaktername" + +#: views/html/characters/register.tpl:41 +msgid "Character type" +msgstr "Charaktertyp" + +#: views/html/characters/register.tpl:52 +#, php-format +msgid "The Seminary field “%s” is invalid" +msgstr "Das Kursfeld „%s“ ist ungültig" + +#: views/html/characters/register.tpl:57 +msgid "Seminary fields" +msgstr "Kursfelder" + +#: views/html/characters/register.tpl:71 views/html/seminaries/create.tpl:9 +#: views/html/users/create.tpl:17 +msgid "create" +msgstr "erstellen" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 +#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:74 views/html/users/register.tpl:75 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:14 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:14 +msgid "register yourself" +msgstr "registriere dich" + +#: views/html/menu/index.tpl:2 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:3 +msgid "Usergroups" +msgstr "Benutzergruppen" + +#: 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:9 +msgid "Logout" +msgstr "Logout" + +#: views/html/quests/quest.tpl:50 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/quest.tpl:55 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:57 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:71 views/html/quests/quest.tpl:80 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:14 +#, php-format +msgid "Submission of %s" +msgstr "Lösungen von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht am %s um %s Uhr" + +#: 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/delete.tpl:2 views/html/seminaries/seminary.tpl:39 +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:38 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:9 views/html/users/edit.tpl:17 +msgid "save" +msgstr "speichern" + +#: views/html/seminaries/index.tpl:4 +msgid "Create new seminary" +msgstr "Neuen Kurs erstellen" + +#: views/html/seminaries/index.tpl:23 views/html/seminaries/seminary.tpl:42 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminaries/index.tpl:25 +msgid "Create a Character" +msgstr "Erstelle einen Charakter" + +#: views/html/seminaries/index.tpl:27 +#, php-format +msgid "Your Character “%s” has not been activated yet" +msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" + +#: views/html/seminarybar/index.tpl:14 +msgid "Last Quest" +msgstr "Letzter Speicherpunkt" + +#: views/html/seminarymenu/index.tpl:5 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:78 views/html/users/register.tpl:79 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: views/html/users/delete.tpl:2 views/html/users/user.tpl:6 +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:5 +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:10 +#, php-format +msgid "registered on %s" +msgstr "registriert am %s" + +#: views/html/users/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:14 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:16 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:18 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:20 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:25 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:27 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:29 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:31 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:36 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:38 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:40 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:42 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:47 views/html/users/register.tpl:49 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:54 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:56 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:58 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:85 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:32 +msgid "Roles" +msgstr "Rollen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..a426efa2 --- /dev/null +++ b/models/AchievementsModel.inc @@ -0,0 +1,507 @@ + + * @copyright 2014 Heinrich-Heine-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(); + } + + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all Achievements of a Seminary. + * + * @param int $seminaryId ID of Seminary to get Achievements of + * @return array Achievements data + */ + public function getAchievementsForSeminary($seminaryId, $includeHidden=false) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND hidden <= ?', + 'ii', + $seminaryId, $includeHidden + ); + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ')', + 'ii', + $seminaryId, + $characterId + ); + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + + + return !empty($data); + } + + } + +?> diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..115d1fb2 --- /dev/null +++ b/models/AvatarsModel.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 Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..e88b1826 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,172 @@ + + * @copyright 2014 Heinrich-Heine-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, motto '. + '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, motto '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + } + +?> diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc new file mode 100644 index 00000000..4490168d --- /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, xps '. + '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..c659afec --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,396 @@ + + * @copyright 2014 Heinrich-Heine-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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_id, seminaries.url 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, characters.avatar_id, 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.avatar_id, 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + + } + +?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> 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..2c2d5eae --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its URL. + * + * @throws IdNotFoundException + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $seminaryMediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its ID. + * + * @throws IdNotFoundException + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE 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..3e2ced21 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,589 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..87cadc9b --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,372 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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 quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..1fd82319 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..251ade4f --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, seminarymedia_id '. + '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, seminarymedia_id '. + '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, seminarymedia_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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..efad3bde --- /dev/null +++ b/models/SeminarycharacterfieldsModel.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 the Seminarycharacterfields-tables. + * + * @author Oliver Hanraths + */ + class SeminarycharacterfieldsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new SeminarycharacterfieldsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..88a7ed2d --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,278 @@ + + * @copyright 2014 Heinrich-Heine-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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..08029501 --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
                    + &$text) : ?> + 0) : ?> + + + + + +

                    + +
                    diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..200b3c7f --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
                    + &$text) : ?> + 0) : ?> + + + + +
                    diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..e08e1c29 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,355 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null, + 'right' => false + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => array($index), + 'answer' => null, + 'right' => false + ); + if(!is_null($oldValue)) { + $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..3965b69c --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,26 @@ +
                    + + + + + + + + + + +
                    + + + +
                    + +
                      + +
                    • + +
                    + +

                    + +
                    diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..60f239c3 --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,23 @@ +
                    + + + + + + + + + + +
                    + + + +
                    + +
                      + +
                    • + +
                    +
                    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..90843218 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Match drops with user answers + foreach($drops as &$drop) + { + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..1e772edf --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,146 @@ + + * @copyright 2014 Heinrich-Heine-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 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..1af4dd6a --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                    + +
                    + + + +
                    + +
                    + +
                    + + + +
                    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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..456db617 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,261 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) + { + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..c2c32595 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                    +
                    + +

                    +
                      + &$answer) : ?> +
                    1. + /> + +
                    2. + +
                    +
                    + + + + + + + +
                    diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ae14f199 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                      + &$question) : ?> +
                    1. +

                      +
                        + +
                      1. + + × + +
                      2. + +
                      +
                    2. + +
                    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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..0c72c1de --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                    + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                    + + +
                    +
                    + : +
                      + +
                    • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                    • + +
                    + +
                    + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                    + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                    + + + + + + + +
                    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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.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/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..62c99c1a --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,28 @@ + +
                    + +
                    + +

                    +

                    + +
                      + +
                    • + + + +

                      +
                      + + +
                      +
                      + +
                      +

                       %

                      +
                      + +
                    • + +
                    diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..ba53c97b --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,52 @@ + +
                    + +
                    + +

                    +

                    +

                    +
                    +
                    + +
                    +
                    +

                    + +
                    +
                      +
                    • .
                    • +
                    •  XPs
                    • +
                    • 1) ? _('Members') : _('Member')?>
                    • +
                    +
                    + +
                    +

                    +
                      + +
                    • + +

                      + +

                      +

                      XP

                      +
                    • + +
                    +
                    + +
                    +

                    +
                      + +
                    • +

                      + format(new \DateTime($quest['created']))?> + + / XP +

                      +
                    • + +
                    +
                    diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..93aeaba7 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,22 @@ + +
                    + +
                    + +

                    +

                    +

                    + + + + +

                    +
                      + +
                    • + +
                    diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..e0f54b94 --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,13 @@ + +
                    + +
                    + +

                    +

                    + +
                      + +
                    • + +
                    diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..8112a913 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,51 @@ + +
                    + +
                    + +

                    +

                    +

                    +

                    + + + + + +
                    +

                    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..63054c06 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,149 @@ + +
                    + +
                    + +

                    +

                    +

                    + +
                    +
                    +
                    +
                    + +
                    +

                    :  %

                    +
                    +
                    +

                    +

                    +
                    +
                    +

                    +

                    XP

                    +
                    +
                    +

                    .

                    +

                    +
                    + +

                    +
                      +
                    • +

                      Aktive Beteiligung

                      +
                    • +
                    • +

                      + 0,3 Notenbonus

                      +
                    • +
                    • +

                      + 0,7 Notenbonus (noch 300 XP)

                      +
                    • +
                    • +

                      + 1,0 Notenbonus (noch 500 XP)

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

                    +
                      + +
                    • + +

                       XPs

                      +
                    • + +
                    +
                    + +
                    +
                    +

                    Neue Achievements

                    +
                      + +
                    • + + + +

                      +

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

                      +
                    • + +
                    +
                    + +
                    +

                    Ranking

                    +
                      +
                    • + +

                      7. Anduin

                      +

                      Level 27 (1500 XP)

                      +
                    • +
                    • + +

                      8. Jaina

                      +

                      Level 26 (1400 XP)

                      +
                    • +
                    • + +

                      9. Uther

                      +

                      Level 25 (1300 XP)

                      +
                    • +
                    • + +

                      10. Lothar

                      +

                      Level 24 (1200 XP)

                      +
                    • +
                    • + +

                      11. Morris

                      +

                      Level 23 (1100 XP)

                      +
                    • +
                    +
                    +
                    + +
                    +

                    Thematischer Fortschritt

                    + +
                    + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..a1227a5a --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,19 @@ + +
                    + +
                    + +

                    +

                    + +
                      + +
                    • + +

                      + +

                      +

                      XP

                      +
                    • + +
                    diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..229dfaaa --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,72 @@ + +
                    + +
                    + +

                    +

                    + +
                    + +
                      + &$settings) : ?> +
                    • +
                        + $value) : ?> +
                      • + +
                      • + +
                      +
                    • + +
                    + +
                    + + +
                    + + +
                    + + +
                      + &$settings) : ?> +
                    • + +
                    + +
                    + + + + + required="required"/> + + + + +
                    + +
                    diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..0f68bc07 --- /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..9cfcf8f3 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,58 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                    + +
                    +
                    + +
                    + + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..ef919187 --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                    +

                    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..c2662789 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
                  • The Legend of Z
                  • + 0) : ?>
                  • +
                  • + + +
                  • + +
                  • + diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..4d3ce2b9 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
                    + +
                    + +

                    + + + + +

                    :

                    + +

                    + + +

                    + + + + + 0) : ?> +

                    +
                      + +
                    • + +
                      +
                      +

                      Fortschritt:

                      +
                      + +
                      +

                      / XP

                      +
                      +
                    • + +
                    + + + + + +

                    + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + 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..26911951 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,96 @@ + +
                    + +
                    + +

                    + +

                    + + 0) : ?> +
                    +

                    + + + +
                    + +

                    + + +

                    + 0 || !empty($questtext['abort_text'])) : ?> +
                      + +
                    • + + +
                    • + +
                    + + +
                    +
                    + + + +
                    + +

                    + +

                    + +

                    +
                    + + + +
                    +

                    +

                    + + + +

                    :

                    + + +
                    + + + +
                    + 0) : ?> +
                      + + +
                    • + : + + + + + +
                    • + +
                    • + : + + + + + +
                    • + + +
                    + + : + + Spiel vorbei + +
                    + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..a29700db --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,17 @@ + +
                    + +
                    + +

                    + +

                    + + + + + +

                    +
                    + +
                    diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..e2a17579 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,41 @@ + +
                    + +
                    + +

                    + +

                    + + + + + +
                    +

                    +
                      + +
                    • + +
                    • + +
                    + +

                    +
                      + +
                    • + +
                    • + +
                    + +

                    +
                      + +
                    • + +
                    • + +
                    +
                    diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..30938357 --- /dev/null +++ b/views/html/seminaries/create.tpl @@ -0,0 +1,15 @@ + +
                    + +
                    + +

                    +

                    + +
                    +
                    + +
                    +
                    + +
                    diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl new file mode 100644 index 00000000..8e53fdb5 --- /dev/null +++ b/views/html/seminaries/delete.tpl @@ -0,0 +1,13 @@ + +
                    + +
                    + +

                    +

                    + + +
                    + + +
                    diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl new file mode 100644 index 00000000..66d7efcd --- /dev/null +++ b/views/html/seminaries/edit.tpl @@ -0,0 +1,15 @@ + +
                    + +
                    + +

                    +

                    + +
                    +
                    + +
                    +
                    + +
                    diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..986991a9 --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,33 @@ +

                    + 0) : ?> + + +
                      + +
                    • + + + +

                      + 0) : ?> + + + + +

                      +
                      + +
                      + 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..7697d758 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,43 @@ + +
                    + +
                    + + +

                    +

                    + + +

                    + + + + +

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

                    diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..0879970c --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,64 @@ +
                    +

                    + + +
                    + + +
                    +

                    +

                    +
                    + + + +
                    +

                    +
                      +
                    • + + + +

                      +

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

                      +
                    • +
                    +
                    + + +
                    +

                    Wille und die Majas

                    +
                      +
                    • + +

                      Anduin

                      +

                      Level 27 (1500 XP)

                      +
                    • +
                    • + +

                      Jaina

                      +

                      Level 26 (1400 XP)

                      +
                    • +
                    • + +

                      Uther

                      +

                      Level 25 (1300 XP)

                      +
                    • +
                    • + +

                      Lothar

                      +

                      Level 24 (1200 XP)

                      +
                    • +
                    • + +

                      Morris

                      +

                      Level 23 (1100 XP)

                      +
                    • +
                    +

                    Gildenprofil ansehen

                    +
                    diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..041c4f43 --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,6 @@ +
                  • + 0) : ?>
                  • +
                  • +
                  • +
                  • + 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..d4f38bb7 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                    +

                    + +
                    +
                    + +
                    + +
                    + +
                    + +
                    + +
                    +
                    + +
                    diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..56ba5d7b --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                    +

                    + +
                    +
                    + +
                    + +
                    + +
                    + +
                    + +
                    +
                    + +
                    diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..5d2883af --- /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..932b4c20 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                    + +

                    + +

                    .

                    + +
                    +
                    + +
                    + +
                    +
                    + +
                    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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..184e0a0c --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,86 @@ +

                    + +

                    + +
                      + &$settings) : ?> +
                    • +
                        + $value) : ?> +
                      • + getMessage(); + break; + } ?> +
                      • + +
                      +
                    • + +
                    + +
                    +
                    + + />
                    + + />
                    + + />
                    + + />
                    + + />
                    +
                    + +
                    diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..f5615f3e --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,33 @@ +

                    +

                    + 0) : ?> + + +

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

                    + +

                    +
                      + +
                    • + +

                      + +

                      + 0) : ?> + + + + +

                      +

                      +
                    • + +
                    + +

                    + 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..b00b193d --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,237 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} +aside{display:none} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:75%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} +article{background:#f7f5f2;float:left} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:auto} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} +aside .cranks li:nth-child(odd){background:#f7f5f2} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Mon, 14 Apr 2014 15:35:57 +0200 Subject: [PATCH 213/340] sort Achievements by position and hide title and description of secret Achievements --- models/AchievementsModel.inc | 11 ++++++----- views/html/achievements/index.tpl | 9 +++++++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/models/AchievementsModel.inc b/models/AchievementsModel.inc index a426efa2..f25689b3 100644 --- a/models/AchievementsModel.inc +++ b/models/AchievementsModel.inc @@ -66,15 +66,16 @@ * @param int $seminaryId ID of Seminary to get Achievements of * @return array Achievements data */ - public function getAchievementsForSeminary($seminaryId, $includeHidden=false) + public function getAchievementsForSeminary($seminaryId) { return $this->db->query( - 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. 'FROM achievements '. 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. - 'WHERE seminary_id = ? AND hidden <= ?', - 'ii', - $seminaryId, $includeHidden + 'WHERE seminary_id = ? '. + 'ORDER BY achievements.pos ASC', + 'i', + $seminaryId ); } diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 62c99c1a..fdaf2d7c 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -12,8 +12,13 @@ -

                    -
                    +

                    + + + + + +
                    From d73475ce807d5aa8961401844dacca0a840e0db1 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 14 Apr 2014 15:36:32 +0200 Subject: [PATCH 214/340] correcty display Achievements for Characters --- views/html/characters/character.tpl | 118 ++++++++++++---------------- 1 file changed, 50 insertions(+), 68 deletions(-) diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index 63054c06..a19f2355 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -28,20 +28,17 @@

                    -

                    -
                      +

                      +
                        +
                      • -

                        Aktive Beteiligung

                        -
                      • -
                      • -

                        + 0,3 Notenbonus

                        -
                      • -
                      • -

                        + 0,7 Notenbonus (noch 300 XP)

                        -
                      • -
                      • -

                        + 1,0 Notenbonus (noch 500 XP)

                        + + + +

                        +

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

                      • +
                      @@ -51,64 +48,49 @@
                  -
                  -

                  -
                    - -
                  • - -

                     XPs

                    -
                  • - -
                  -
                  -
                  -
                  -

                  Neue Achievements

                  -
                    - -
                  • - - - -

                    -

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

                    -
                  • - -
                  -
                  +
                  +

                  +
                    + +
                  • + +

                     XPs

                    +
                  • + +
                  +
                  -
                  -

                  Ranking

                  -
                    -
                  • - -

                    7. Anduin

                    -

                    Level 27 (1500 XP)

                    -
                  • -
                  • - -

                    8. Jaina

                    -

                    Level 26 (1400 XP)

                    -
                  • -
                  • - -

                    9. Uther

                    -

                    Level 25 (1300 XP)

                    -
                  • -
                  • - -

                    10. Lothar

                    -

                    Level 24 (1200 XP)

                    -
                  • -
                  • - -

                    11. Morris

                    -

                    Level 23 (1100 XP)

                    -
                  • -
                  -
                  +
                  +

                  Ranking

                  +
                    +
                  • + +

                    7. Anduin

                    +

                    Level 27 (1500 XP)

                    +
                  • +
                  • + +

                    8. Jaina

                    +

                    Level 26 (1400 XP)

                    +
                  • +
                  • + +

                    9. Uther

                    +

                    Level 25 (1300 XP)

                    +
                  • +
                  • + +

                    10. Lothar

                    +

                    Level 24 (1200 XP)

                    +
                  • +
                  • + +

                    11. Morris

                    +

                    Level 23 (1100 XP)

                    +
                  • +
                  +
                  From 7f104e5d8f2d550d960e99fd5ac5a04ab76c89d9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 14 Apr 2014 15:36:51 +0200 Subject: [PATCH 215/340] implement lists for Seminary fields --- views/html/characters/register.tpl | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl index 229dfaaa..2916cfe4 100644 --- a/views/html/characters/register.tpl +++ b/views/html/characters/register.tpl @@ -65,7 +65,15 @@ case 'Text': ?> + + +
                  From 7090c0fad88098d033d58edab87b11347105ea0f Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 14 Apr 2014 15:37:03 +0200 Subject: [PATCH 216/340] show more user fields --- views/html/users/user.tpl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl index f5615f3e..745097b0 100644 --- a/views/html/users/user.tpl +++ b/views/html/users/user.tpl @@ -7,7 +7,9 @@

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

                  From fd35574248470cf9443e7403924289522cb01f48 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 14 Apr 2014 15:37:08 +0200 Subject: [PATCH 217/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 6347 -> 6583 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 67 +++++++++++++------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 7b3e368689c4e32b4f25e568e170f72901381a29..72d6af0819a72b71a2210732d170260f1ffba150 100644 GIT binary patch delta 2453 zcmZYATWnNC9LMq5ZtcxpxRhQf;%RA%w8gTewWSscLW@-drCeF z>H}ULBs7a?j4`Pu2BId0O)%EP5Pbof5Fh$tLcj!TVvO-gQ}2F%+tD;S>3=^nyR&Eh zGxOhcCh+mb#EoT{uNm49v5M&M8}kYdWbs0KKF64Rd;yo?DO`e6Sc(_05U*k#euX8N znY%EqzpV@L1-i{5{HY{Mg1DR^} z;%zv9+Rzcya}!vGr?DK*r$0{^^93F4@N4AH{J@JIFpt^zryXaps^;aR4yPP7-?8I* z)chvQ#CFvDPUKL`F6(|&LW5Y%`evL?4W2eup=cpZjYsWuhHRF{$RS9j@X#NRn0(HVCQKw?wpreJ}#YRlw2E2u=X^J^tW$d6544@L+g!8pti={*`GNBYm+FwNSSm_n}Va8PpfwLEZ5=RLxvK zB`}9NfuF3uqiV;`hkCyhmC$O;z%{6ot1aUEwP2f_(1TiNH}VX#5A}c$YT+kP#S=p% zehM%7jCmdPoPM6V0>h|r0`)67jcneW!}WL(@5FC3p^;7|7b2TcKc6Aw^v%<#D|#7Q zF^SsQ462%MqJCEMc3i-RTBsIzv}r~?ryI%9^df&I#EUkTI8LX6&U2`Y-^40>AC<^8 zT#Iw4S}0&O?XVnmQjMqtTkLo%YG>W31%^=zM^GpBENcFX$dx3_qd2>16FY6-Z#+7>`(vn|)qAJ~ zucD6pOVk}+#|HckS7QkUa5uJChfoVAQ9FJMoA3iWzKJU0AFaP&f=wGg=cg~0qkbk; z*0rdeHK2-VGwK0*P$zT%mDpj_l|)dr6t_;I=6{I%Nd9fR>FMMi)cyarX@?u_n*&xA zl-f3;k5Dpuh`q$(Mva>Ni{5CZ{}7>T)5bVB(?WC+JBWU*e;*y)znV@%?GT~N>xoK& zt4kkg2Oc4M2sK^X7J?0?i&v*mLmVd5bnj}qR-I%>1DXogA&M98Kgb)p2DJwWe$wfR z*0`4FR)_XDv5ROX))R}{Jq%P6|Fna2RBZZBsG_YVHWO-tM8co$E>tS@V0IFn!~sG@ zr~-P7c$iSp>E4HkeZ(fBmrxBnLTn}OCl)sqa0XFDJW6aQ?jY_ZXnE;>G;Q=MQh}Ux zzQDK_3wv?bIXV_R5%xx$*tiq-#>PWWI+vo5-^ZExCw<8?1y_oK3*Q-Xg0Xg|W+FLIcq_Zv3AD5~ZAf+(RoZ?ar&Z}Z;w>Hq)$ delta 2209 zcmYk-ZA{fw9Ki8&1w@c13DK+!1r6{aua+T3pydN)hLsu>nM-uDC&rSk7wk@$t#Y~; ztClOP7uKsYQjh1G=9w^(18;Po{5PEUpWG`ov>rjE!I3JtQ z0d$~yqYE9tK6Ie3q0hfnNdE2c^Z3PCG&O&qFPO#Cu5AS_#aeXcZP6X*UU@pUQ|N$R z!b!Lv&DcS-pZDVLN745mDrL#9882Tj=pG+%XIt}ZC~Qz zY4{r%dnlpTMK~L8#tmr7pNYPWo|54d6|xvEpeee7&gdU>*A{amJT}#6y8-QB3vx@? zfj-}bE>SP~d_Ov&5Ag>47#-*toQosq08(RAZlN-rr`!V#=**hY6m3HX)*jm(=#0A1 z4&FdJeh1CeN9g^>qNk9gh2i-72+pE?5o?$} zqa9yG2a?CA{k{avNI9CRO7!<+v<|(m5%WF&Yp5joq6O=*2Tj$9=-24Xe!^<}6`jE} z^2ZFq!su3X0zK$jzlxro&(ID>(F|Wi2Yv+$=s&Y_7D6FAXg1d4Vr-4Rj1FJ`eQ*$$ z;y37K&0=F{Au_fw1HFF{dK#8TSE3VZLif%lO!?qbR7}ycXzE@-XR;UFJg-ELp!c6b z4nwBg!PUW9*h-ml7uOYp?|LNjO$bj$&9qr_4^cAn4-ZnimzYQRw|Nv`e9q(q@NLbp4 ztwbZSfw0V1!++Sc@oQ)&|Fb9p-x>87dc66r$)J&BxMozL&-=r1oi+y7_LFS*Hz zrSK>lz*DY-PO6Lqoe(~^vI0tME~WPU;PXB CZ@>@$ diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 696f6da2..cc8234f3 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-11 16:16+0100\n" -"PO-Revision-Date: 2014-04-11 16:16+0100\n" +"POT-Creation-Date: 2014-04-14 15:20+0100\n" +"PO-Revision-Date: 2014-04-14 15:21+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -16,6 +16,8 @@ msgstr "" "X-Poedit-SearchPath-0: views\n" "X-Poedit-SearchPath-1: questtypes\n" +#: questtypes/choiceinput/html/quest.tpl:14 +#: questtypes/crossword/html/quest.tpl:25 #: questtypes/dragndrop/html/quest.tpl:16 #: questtypes/multiplechoice/html/quest.tpl:19 #: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 @@ -78,10 +80,18 @@ msgstr "Fehler" msgid "Achievements" msgstr "Errungenschaften" +#: views/html/achievements/index.tpl:15 +msgid "Secret Achievement" +msgstr "Geheime Errungenschaft" + +#: views/html/achievements/index.tpl:19 +msgid "Continue playing to unlock this secret Achievement" +msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" + #: views/html/charactergroups/group.tpl:7 #: views/html/charactergroups/groupsgroup.tpl:7 #: views/html/charactergroups/index.tpl:7 -#: views/html/characters/character.tpl:55 views/html/seminarymenu/index.tpl:3 +#: views/html/characters/character.tpl:53 views/html/seminarymenu/index.tpl:3 msgid "Character Groups" msgstr "Charaktergruppen" @@ -100,7 +110,7 @@ msgstr "Mitglied" #: views/html/charactergroups/group.tpl:25 #: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 -#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:13 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:15 msgid "Characters" msgstr "Charaktere" @@ -134,10 +144,15 @@ msgstr "Verlorene Quest" msgid "Total progress" msgstr "Fortschritt" -#: views/html/characters/character.tpl:20 views/html/users/user.tpl:27 +#: views/html/characters/character.tpl:20 views/html/users/user.tpl:29 msgid "Level" msgstr "Level" +#: views/html/characters/character.tpl:39 views/html/seminarybar/index.tpl:28 +#, php-format +msgid "achieved at: %s" +msgstr "erhalten am: %s" + #: views/html/characters/register.tpl:7 msgid "Create Character" msgstr "Charakter erstellen" @@ -181,7 +196,7 @@ msgstr "Das Kursfeld „%s“ ist ungültig" msgid "Seminary fields" msgstr "Kursfelder" -#: views/html/characters/register.tpl:71 views/html/seminaries/create.tpl:9 +#: views/html/characters/register.tpl:79 views/html/seminaries/create.tpl:14 #: views/html/users/create.tpl:17 msgid "create" msgstr "erstellen" @@ -191,7 +206,7 @@ msgid "Introduction" msgstr "Einführung" #: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 -#: views/html/menu/index.tpl:7 views/html/users/login.tpl:3 +#: views/html/menu/index.tpl:6 views/html/users/login.tpl:3 #: views/html/users/login.tpl:14 msgid "Login" msgstr "Login" @@ -227,17 +242,13 @@ msgstr "registriere dich" msgid "Users" msgstr "Benutzer" -#: views/html/menu/index.tpl:3 -msgid "Usergroups" -msgstr "Benutzergruppen" - -#: 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/menu/index.tpl:3 views/html/seminaries/create.tpl:6 +#: views/html/seminaries/delete.tpl:6 views/html/seminaries/edit.tpl:6 #: views/html/seminaries/index.tpl:1 msgid "Seminaries" msgstr "Kurse" -#: views/html/menu/index.tpl:9 +#: views/html/menu/index.tpl:8 msgid "Logout" msgstr "Logout" @@ -266,37 +277,37 @@ msgstr "Lösungen von %s" msgid "submitted" msgstr "eingereicht am %s um %s Uhr" -#: views/html/seminaries/create.tpl:2 +#: views/html/seminaries/create.tpl:7 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 +#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 +#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 msgid "Title" msgstr "Titel" -#: views/html/seminaries/delete.tpl:2 views/html/seminaries/seminary.tpl:39 +#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:39 msgid "Delete seminary" msgstr "Kurs löschen" -#: views/html/seminaries/delete.tpl:4 +#: views/html/seminaries/delete.tpl:9 #, 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 +#: views/html/seminaries/delete.tpl:11 views/html/users/delete.tpl:6 msgid "delete" msgstr "löschen" -#: views/html/seminaries/delete.tpl:7 views/html/users/delete.tpl:7 +#: views/html/seminaries/delete.tpl:12 views/html/users/delete.tpl:7 msgid "cancel" msgstr "abbrechen" -#: views/html/seminaries/edit.tpl:2 views/html/seminaries/seminary.tpl:38 +#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:38 msgid "Edit seminary" msgstr "Kurs bearbeiten" -#: views/html/seminaries/edit.tpl:9 views/html/users/edit.tpl:17 +#: views/html/seminaries/edit.tpl:14 views/html/users/edit.tpl:17 msgid "save" msgstr "speichern" @@ -345,6 +356,7 @@ msgstr "Nachname" #: views/html/users/create.tpl:12 views/html/users/create.tpl:13 #: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 #: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +#: views/html/users/user.tpl:12 msgid "E‑mail address" msgstr "E‑Mail-Adresse" @@ -456,10 +468,17 @@ msgstr "Das Passwort ist ungültig" msgid "Register" msgstr "Registrieren" -#: views/html/users/user.tpl:32 +#: views/html/users/user.tpl:11 +msgid "Name" +msgstr "Name" + +#: views/html/users/user.tpl:34 msgid "Roles" msgstr "Rollen" +#~ msgid "Usergroups" +#~ msgstr "Benutzergruppen" + #~ msgid "E‑Mail" #~ msgstr "E‑Mail" From b1ffcc0c5580e76cd5091435d053c3bf0bd006c6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 14 Apr 2014 16:44:42 +0200 Subject: [PATCH 218/340] mood pic width fix for resolutions 1366px+ --- www/css/desktop.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/css/desktop.css b/www/css/desktop.css index b00b193d..e469b212 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -224,7 +224,7 @@ body{background:#eae8e4;height:100%} .wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} article{background:#f7f5f2;float:left} .moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} -.moodpic img{width:auto} +.moodpic img{width:100%} } @media only screen and (min-width:1600px){ From 0d449bca925aa564001bd549fbbf0741627c11e3 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 14 Apr 2014 19:20:47 +0200 Subject: [PATCH 219/340] crossword design update & javascript input changes --- questtypes/crossword/html/quest.tpl | 30 ++++++++++++++++++------ questtypes/crossword/html/submission.tpl | 28 ++++++++++++++++++---- www/css/desktop.css | 10 ++++++++ 3 files changed, 56 insertions(+), 12 deletions(-) diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl index 3965b69c..38c09cc9 100644 --- a/questtypes/crossword/html/quest.tpl +++ b/questtypes/crossword/html/quest.tpl @@ -1,4 +1,4 @@ -
                  + @@ -6,7 +6,8 @@ @@ -14,13 +15,28 @@
                  - + +
                  - -
                    +
                    1. -
                  - -

                  +
                  + diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl index 60f239c3..00b8a4dc 100644 --- a/questtypes/crossword/html/submission.tpl +++ b/questtypes/crossword/html/submission.tpl @@ -1,4 +1,4 @@ -
                  + @@ -6,7 +6,8 @@ @@ -14,10 +15,27 @@
                  - + +
                  - -
                    +
                    1. -
                  +
                  + diff --git a/www/css/desktop.css b/www/css/desktop.css index e469b212..de6671e7 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -172,6 +172,16 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gquests .date{color:#aca8a1;display:block} .gquests .xp{display:block} + +/** Quest Types **/ + +.crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} +.crossword td{background:#d7d4cf;padding:1px} +.crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} +.crossword ol{list-style-type:decimal;list-style-position:inside} +.crossword li{margin-top:20px} +.crossword .index{position:absolute;font-size:0.625em;color:#c9c7c4;margin:-3px 0 0 2px} + /** Media Queries **/ @media only screen and (min-width:480px){ From 3ea6c17dceca6c1a234fc7b365c36a3475592832 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 14 Apr 2014 20:48:17 +0200 Subject: [PATCH 220/340] correct display of numbers for Questtype ?crossword? --- .../crossword/CrosswordQuesttypeController.inc | 15 +++++++-------- questtypes/crossword/html/quest.tpl | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc index e08e1c29..04e7efc1 100644 --- a/questtypes/crossword/CrosswordQuesttypeController.inc +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -184,14 +184,13 @@ foreach(range($startY, $endY) as $y) { - $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; $matrix[$x][$y] = array( 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), - 'indices' => array($index), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), 'answer' => null ); - if(!is_null($oldValue)) { - $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; } if(array_key_exists('answer', $word)) { @@ -216,14 +215,14 @@ { $matrix[$x] = array_pad($matrix[$x], $y+1, null); - $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; $matrix[$x][$y] = array( 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), - 'indices' => array($index), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), 'answer' => null ); - if(!is_null($oldValue)) { - $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + var_dump($matrix[$x][$y]['indices']); } if(array_key_exists('answer', $word)) { diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl index 38c09cc9..59068547 100644 --- a/questtypes/crossword/html/quest.tpl +++ b/questtypes/crossword/html/quest.tpl @@ -6,7 +6,7 @@ - + 0) : ?> From b15bb406f77852092248ed7d3274dcb063591b1c Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 00:06:43 +0200 Subject: [PATCH 221/340] correct display of numbers for Questtype ?crossword? (submission) and mark orientation --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 6583 -> 6655 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 16 +++++++++++++--- .../CrosswordQuesttypeController.inc | 14 ++++++-------- questtypes/crossword/html/quest.tpl | 9 ++++++++- questtypes/crossword/html/submission.tpl | 11 +++++++++-- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 72d6af0819a72b71a2210732d170260f1ffba150..fa698bc48281f88a940eac09a8d284b7352f5554 100644 GIT binary patch delta 1864 zcmYk*S!_&E9LMo9)y`s@E|!j#s%eWFl#)^+=FL)q2cec4)kzx-Ql-(9n-CERmAEM- zq}oIXVyoEVL5tLq2o)ae!IR)gBoa~IUmc0b%>A4*_nh5VY&V!kV)2x-K*QptKD&_)eC%3Q&yHGprL7nb2I(p3_sD2a` zs1yldDpBjyBRLvp4-LNNFcpek05O0dfI zYf&4kN3DMp6}T0toMSH0(1e?)XLHZ?AEHYC997~^I1ImHKKh9&3sHx1H7c<<>d`df zSUhd*MkV+Ql|TZ!@e<#p($I|%sx(=sl0|w4@VZ%}$k)u_qVZ*@#4B(TZo*=0LsjmL z^&4sfJvava8~|-(66O=%l&FDqsGYQ-&cGSeYxWWq@HeXDi9A>(nv4+~jw5g$PQWT_ zGb(TwYR6A-GQPL{G|J@YG>2)(@kn({5o+QB)DD-T9!<;|N9}Mu>QFVH<~O3AaWg8x z7Sx8$q0ZD5>l4)YFCm62&<}UHwWqH=PCw=2QE8M5dE_1*7MyIUg-Xeagk5a2>&-MTG_O0AlQ&qol zLv5_u9UiRrZ>^}QttzK;C>Zrk9a&INTrkCb8_fRKZucf<2L7Lv9Xb@~owPnyZTUZZ3R|rWuog$1n@eU?%qBEPQ}daR5v34bH?=??gWrL-cDf8~32b zpTu-ylI8-97&m%R6Az;TjpB41LqBFPTIS&tEVV90AN`fcP_rHWcxp{kp66DRf3f`4XaTJZAJxdKm}|;zGfE}jqkQ5 zQ58Ops*1TpLxHYi8TMfWKOiwpHuY7;K~#cKRD#P;&(+(02P(lHR3iNt#HUzi<2j`*!)!6=KRAmmK9=wLy<6EdR^8l5=D5?TKtiMoaCxt(Ce--kMp1^^>T# z3T)i(!GTXYJSpo3c3AnG)~L%mjGw(sLl1u89#!=K+@DWWb44^7ihDxy9_N!1U zt3d_WfePG#s@P%F_!G#MBu$UKaRF8GUev(*I2#{h2@YHTq7GpRStzj>YD;QyA$D1> zq7rJWdkj$<2(HYwCk4+c=L$vkT@YGtLU!?YYV!CF*>wxSYiL~Th2>MZTE z_Mpb!bGqG2CfCt2&O`TJSG}{)lj~}BsyzjPM&0G33hAQ)C}xXu%#-VFqNU7?bHg*o zwcUB?3At*W?>yb$_|l4}ZR4+X{8SEQ)Y?~_Htu#-`MaG#e^psL7%fl4BB4k$5=mdT pHoU%VfBW9{-JRi;OTDbYfhzFOiw&b(#X_{{f0Gsty1E diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index cc8234f3..ddc5748c 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-14 15:20+0100\n" -"PO-Revision-Date: 2014-04-14 15:21+0100\n" +"POT-Creation-Date: 2014-04-15 00:04+0100\n" +"PO-Revision-Date: 2014-04-15 00:04+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -17,13 +17,23 @@ msgstr "" "X-Poedit-SearchPath-1: questtypes\n" #: questtypes/choiceinput/html/quest.tpl:14 -#: questtypes/crossword/html/quest.tpl:25 +#: questtypes/crossword/html/quest.tpl:30 #: questtypes/dragndrop/html/quest.tpl:16 #: questtypes/multiplechoice/html/quest.tpl:19 #: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 msgid "solve" msgstr "lösen" +#: questtypes/crossword/html/quest.tpl:22 +#: questtypes/crossword/html/submission.tpl:22 +msgid "vertical" +msgstr "vertikal" + +#: questtypes/crossword/html/quest.tpl:24 +#: questtypes/crossword/html/submission.tpl:24 +msgid "horizontal" +msgstr "horizontal" + #: questtypes/multiplechoice/html/quest.tpl:3 #, php-format msgid "Question %d of %d" diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc index 04e7efc1..65833c87 100644 --- a/questtypes/crossword/CrosswordQuesttypeController.inc +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -282,15 +282,14 @@ foreach(range($startY, $endY) as $y) { - $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; $matrix[$x][$y] = array( 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), - 'indices' => array($index), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), 'answer' => null, 'right' => false ); - if(!is_null($oldValue)) { - $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; } if(!is_null($word['answer'])) @@ -318,15 +317,14 @@ { $matrix[$x] = array_pad($matrix[$x], $y+1, null); - $oldValue = (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y])) ? $matrix[$x][$y] : null; $matrix[$x][$y] = array( 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), - 'indices' => array($index), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), 'answer' => null, 'right' => false ); - if(!is_null($oldValue)) { - $matrix[$x][$y]['indices'] = array_merge($matrix[$x][$y]['indices'], $oldValue['indices']); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; } if(!is_null($word['answer'])) { diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl index 59068547..d202eb8f 100644 --- a/questtypes/crossword/html/quest.tpl +++ b/questtypes/crossword/html/quest.tpl @@ -17,7 +17,14 @@
                    -
                  1. +
                  2. + + : + + : + + +
                  diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl index 00b8a4dc..c47e414d 100644 --- a/questtypes/crossword/html/submission.tpl +++ b/questtypes/crossword/html/submission.tpl @@ -6,7 +6,7 @@ - + 0) : ?> @@ -17,7 +17,14 @@
                    -
                  1. +
                  2. + + : + + : + + +
                  From 7ab5fd60d5bc76d7ab9a5de60bcb4d2c1ff91dee Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 15 Apr 2014 00:20:10 +0200 Subject: [PATCH 222/340] css list type margin fix --- www/css/desktop.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/css/desktop.css b/www/css/desktop.css index de6671e7..aecc38a8 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -178,7 +178,7 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} .crossword td{background:#d7d4cf;padding:1px} .crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} -.crossword ol{list-style-type:decimal;list-style-position:inside} +.crossword ol{list-style-type:decimal;margin-left:25px} .crossword li{margin-top:20px} .crossword .index{position:absolute;font-size:0.625em;color:#c9c7c4;margin:-3px 0 0 2px} From b9bca6e2c5d7affd60c3cb59399eb582f722a124 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 15 Apr 2014 00:23:51 +0200 Subject: [PATCH 223/340] improved readability for crossword input field numbers --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 ++++++++++++ agents/bottomlevel/MenuAgent.inc | 37 ++ .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarybarAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/AchievementsAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 ++ agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 70 ++ apis/WebApi.inc | 250 ++++++++ app/Controller.inc | 103 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 267 ++++++++ app/QuesttypeController.inc | 349 ++++++++++ app/QuesttypeModel.inc | 154 +++++ app/QuesttypeView.inc | 76 +++ app/ToplevelAgent.inc | 36 ++ app/Utils.inc | 83 +++ app/controllers/IntermediateController.inc | 168 +++++ app/controllers/SeminaryRoleController.inc | 273 ++++++++ app/exceptions/FileUploadException.inc | 75 +++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 +++ .../QuesttypeAgentNotValidException.inc | 77 +++ .../QuesttypeControllerNotFoundException.inc | 77 +++ .../QuesttypeControllerNotValidException.inc | 77 +++ .../QuesttypeModelNotFoundException.inc | 77 +++ .../QuesttypeModelNotValidException.inc | 77 +++ .../SubmissionNotValidException.inc | 77 +++ app/exceptions/WrongFiletypeException.inc | 75 +++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 168 +++++ configs/CoreConfig.inc | 167 +++++ controllers/AchievementsController.inc | 162 +++++ controllers/BinaryController.inc | 37 ++ controllers/CharactergroupsController.inc | 150 +++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 229 +++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 ++ controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 ++ controllers/MediaController.inc | 368 +++++++++++ controllers/MenuController.inc | 51 ++ controllers/QuestgroupsController.inc | 179 ++++++ .../QuestgroupshierarchypathController.inc | 90 +++ controllers/QuestsController.inc | 595 +++++++++++++++++ controllers/SeminariesController.inc | 252 ++++++++ controllers/SeminarybarController.inc | 73 +++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 +++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 328 ++++++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ .../components/ValidationComponent.inc | 115 ++++ core/Agent.inc | 607 ++++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 433 +++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 +++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 +++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 +++ exceptions/ClassNotValidException.inc | 77 +++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 +++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 +++ exceptions/ServiceUnavailableException.inc | 77 +++ exceptions/ViewNotFoundException.inc | 77 +++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 6655 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 536 ++++++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 508 +++++++++++++++ models/AvatarsModel.inc | 62 ++ models/CharactergroupsModel.inc | 172 +++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 396 ++++++++++++ models/CharactertypesModel.inc | 57 ++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 139 ++++ models/QuestgroupsModel.inc | 589 +++++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 ++++ models/QuestsModel.inc | 372 +++++++++++ models/QuesttextsModel.inc | 114 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 193 ++++++ models/SeminarycharacterfieldsModel.inc | 78 +++ models/UploadsModel.inc | 148 +++++ models/UserrolesModel.inc | 77 +++ models/UsersModel.inc | 278 ++++++++ models/UserseminaryrolesModel.inc | 78 +++ .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 + .../ChoiceinputQuesttypeController.inc | 157 +++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 +++++ questtypes/choiceinput/html/quest.tpl | 15 + questtypes/choiceinput/html/submission.tpl | 12 + .../crossword/CrosswordQuesttypeAgent.inc | 24 + .../CrosswordQuesttypeController.inc | 352 ++++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 +++ questtypes/crossword/html/quest.tpl | 49 ++ questtypes/crossword/html/submission.tpl | 48 ++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 199 ++++++ .../dragndrop/DragndropQuesttypeModel.inc | 146 +++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 261 ++++++++ .../MultiplechoiceQuesttypeModel.inc | 159 +++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 +++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 +++++ .../textinput/TextinputQuesttypeModel.inc | 117 ++++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 ++++++++++++ responses/WebResponse.inc | 250 ++++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/achievement.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/achievements/index.tpl | 33 + views/html/charactergroups/group.tpl | 52 ++ views/html/charactergroups/groupsgroup.tpl | 22 + views/html/charactergroups/index.tpl | 13 + views/html/charactergroupsquests/quest.tpl | 51 ++ views/html/characters/character.tpl | 131 ++++ views/html/characters/index.tpl | 19 + views/html/characters/register.tpl | 80 +++ views/html/error/index.tpl | 2 + views/html/html.tpl | 58 ++ views/html/introduction/index.tpl | 36 ++ views/html/menu/index.tpl | 9 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/index.tpl | 0 views/html/quests/quest.tpl | 96 +++ views/html/quests/submission.tpl | 17 + views/html/quests/submissions.tpl | 41 ++ views/html/seminaries/create.tpl | 15 + views/html/seminaries/delete.tpl | 13 + views/html/seminaries/edit.tpl | 15 + views/html/seminaries/index.tpl | 33 + views/html/seminaries/seminary.tpl | 43 ++ views/html/seminarybar/index.tpl | 64 ++ views/html/seminarymenu/index.tpl | 6 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 86 +++ views/html/users/user.tpl | 35 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 247 +++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 ++++ 229 files changed, 21548 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/AvatarsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/CharactertypesModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/achievements/index.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/characters/register.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarybar/index.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..43dff51a --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* 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..49512791 --- /dev/null +++ b/agents/bottomlevel/MenuAgent.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\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) + { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); + } + + } + +?> 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/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..049ba904 --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,70 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); + } + + + + + 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..5731aeed --- /dev/null +++ b/app/Controller.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; + + + /** + * 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 and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..5b980299 --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,168 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + + // Determine user roles + self::$user['roles'] = array(); + $roles = $this->Userroles->getUserrolesForUserById(self::$user['id']); + foreach($roles as &$role) { + self::$user['roles'][] = $role['name']; + } + + // Character + $controller = $this->agent->controller; + if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) + { + $seminaryUrl = $this->request->getParam(3); + self::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..324e13e2 --- /dev/null +++ b/app/controllers/SeminaryRoleController.inc @@ -0,0 +1,273 @@ + + * @copyright 2014 Heinrich-Heine-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\controllers\IntermediateController + { + /** + * 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); + + // Check achievements + $this->checkAchievements($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(); + } + } + + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Get Seminary + $seminary = self::$seminary; + + // Get Character + $character = self::$character; + if(is_null($character)) { + return; + } + + // Get unachieved Achievments + $achievements = array_merge( + $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id']), + $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary($seminary['id']) + ); + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], $character['id']); + } + } + } + + } + +?> diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..45ccb0fe --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,168 @@ + + * @copyright 2014 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ) + ); + + + /** + * 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|register|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|register))', '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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..b6ef6595 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,162 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Achievements + $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); + foreach($achievements as &$achievement) + { + // Get status for Character + $achieved = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + + // Get Character progress + if(!$achieved && $achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + + // Get media + $achievement['media_index'] = 'unachieved_achievementsmedia_id'; + if($achieved) { + $achievement['media_index'] = 'achieved_achievementsmedia_id'; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('achievements', $achievements); + } + + } + +?> diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc new file mode 100644 index 00000000..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..d4cf6cee --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,150 @@ + + * @copyright 2014 Heinrich-Heine-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', 'avatars', '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: 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); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); + + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + // Get Character groups Quests + $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc new file mode 100644 index 00000000..fb9d18c8 --- /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->getSeminaryMediaById($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..fbeb0ead --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,229 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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']); + + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($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); + $this->set('achievements', $achievements); + } + + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + + } + +?> 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..7bf8859e --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedCharacter', IntermediateController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..4036954b --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,368 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'achievements', '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. + * + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + + + /** + * 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; + } + + + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + 480 + ); + } + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } + + + /** + * Get supported image types. + * + * @return array List of supported image types + */ + private static function getImageTypes() + { + $im = new \Imagick(); + + + return $im->queryFormats(); + } + + + /** + * Resize an image. + * + * @param string $fileName Absolute pathname of image to resize + * @param string $mimeType Mimetype of target image + * @param int $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..2be5ea8c --- /dev/null +++ b/controllers/MenuController.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..d7b8acf1 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,179 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && $currentQuest['solved']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..b630a134 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,595 @@ + + * @copyright 2014 Heinrich-Heine-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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + //$questtexttypesUrls = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) + { + switch($questStatus) + { + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; + } + } + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('queststatustext', $questStatusText); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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 + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..771c9787 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,252 @@ + + * @copyright 2014 Heinrich-Heine-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', 'userseminaryroles', '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) + { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($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 + $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/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..1a36f136 --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,73 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('characters', 'quests', 'questgroups', 'achievements'); + + + + + /** + * Action: index. + */ + public function index() + { + if(is_null(IntermediateController::$seminary)) { + return; + } + + // Get Seminary + $seminary = IntermediateController::$seminary; + + // Get Character + $character = IntermediateController::$character; + if(is_null($character)) { + return; + } + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); + } + + } + +?> diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..d827425d --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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', IntermediateController::$user); + $this->set('loggedSeminary', IntermediateController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..535dcbbe --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,328 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + + // Level + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..dbe5b0b3 --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,115 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $settings) + { + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                  \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..fa698bc48281f88a940eac09a8d284b7352f5554 GIT binary patch literal 6655 zcma);ZHygN8OKN7#8nhQuqZDF%3=$>?QSWw+)`l6ZcAaeY`fb6RjG6DJ@?*mXJ@YS zvX|W;Hu}MsAVw1+8n8k%8Z{zHh#Ec!)!<8F2u45PYpS1!`pKA>5b^h)nLGD(ce^;b z&;8ArdFGtwJkN8UbN+O}S&uqgP2^H!@=VA141Dk${?--YMH7vOv0 zEAXB0HFz!j54;#&{kFWl6JAHV3Eu<10Hyy8I0T=ATj2{({r(GToU?g(AzTOF1veGE z8NQwNWWjskd9)jlPn`&!4IhM>&nKb!Jr38w@4-vpFN*hng_@r_<<>b5>ic!@t?=@q zeGQb}CMeh31f_pl(Y^~x{~$aI9)Z$73UP_!7fhk-c?ez&KL>~5vryl^3fIGvQ1d>A zPSm~_O1}za-%8BI0Dba zshHqwgR=KFDEkgT*>fM%cONg>pM|pT87Mn`2Mzo^yav7wWygAgulR4|Pj>Er`fdzr zoMzGPLiy!!sPA5cito>%^5qq%@z3T@^S`LzrBHdZzG!cUn&&N0cI|^_!2MAEI{-Cq zy?CEMjkgFH!ubSL|Bpb8|0q=c9EY<1M+IMmKY5Ge{0geShw1zx_ykn@2T<$frw~^- ze}EPE8oUCoBPd(oj)HBdb@Uh{B+k=N^LQR^hc7|R^9`uHzL192*Cwd;7}PjNAVWB9 zsD73pCOV&n{G2cHr}=#wz8`)c%HChYtKgrZ>^OtYKL9U*%8N0m`AtFjs}5zKSF{tT zc`ZTp|7ECgpM>(;Gf?_JhKkG2iuNy|{Qg@gy}!Zt!GFMO;kgVdUJsRz_e0s$go?`} za3lO?!B?Q{dmU>0GnkC*xd7_TiucumW00S72Y=E(3T3y2qp$<3@F^(2 z{juObq3nMHZh+?!e9dDN%KpiMU8s3H1r`5qL9MS>p~m|!l)uj+sIv2XxCvecFNb%+ z&Co0O7}U5gLCyDNxE21TXkUnvl*j7}Rv@l%wnKe41vS6JP;qe!HlgM>2bE6`LiO_q zls_MXvhO(5e7*yfPtO*78A|`}P~-d?vRuk*KX>xuUFDPhjploO(flxMAj&OWW5_*- zY`Pmchzwq&uXAYN!FKMa5Y18Z!@bT8$OJNu+>7i+lnc7#8(sG!vi~~dN`$ax{=FI2 zk$s3R#qbt{>1BB=UkoEIqDwiTOEH!|kEp?=95x7PUS2isnL(&c9-WYDdX%#Zbj}xl#j}#4_C(|*)>*gdv?Lj+aQUZ zG2ainwqcr{Z%wk;v9WV|+l}0MVk5IR3e(QY2j)O(W4`Sx2JXCF)-?4nNL(+7jpzHe z<@%=H>v#E=#tRl)-)k)I!HZ22hQ<$rmf0}xKCsygH?m&hji*tK+hLTT>jkUpcA~Ij zqr^L9mYQ|HKa9h0FG#KF`0k>|*hy&8zz^$lCTU|+Z0nIt%vj%z&h3$P6KhI`^d7h? z9|g8c4_N3%i#;_xOJf^3-HL_{B(&~9M9oa0=3$B^r zJ)K@^m+iHz$on@%-mh*%w)ULtpG?q-2%G%pPh#hw8_YQeZOe;Uz{O4EX0bRJ`sDq= zRR1$8b9g3CO0A9Jc9G<AAgZui^G(I;*>V){W;J{TbJ1#*IZ2 zr}aAPrnV&3cJ%o!Ky~K8uF{H~dk8?DZ1;s})GuWH8L0vUVM6F_VAwId1&Uz9EZW3z^WxYrZjx1< zx*OD4{S;1A3N%b@k@y;AaqQ&D<50l8CG8x(ql)iP&Y~il%_4Th)^Cb7iU*1Gcp?J6to==j9Y)V`^SzJa5gM~1Y8vAtJjsmxWA z*awm=Y-V1tGv7XKvg^?7p308Co`!4Ms4@}M!v@<%)$FKw$C-vfGVq$4%N*vw8ED{5Lf1r;+Pd_Jq-VTs1)_YsR~_?qo~Vn_U|MbLB2G zx^ZW}pBaK%x7tBtOU~r>m7~m9H>$U%8K*K*H4C2Y#si=BUyfGI$8iCzg@Gr$!I~Q0 zcQWM~nNiaW2Gj?h?Xhj29VZ86%$v{iV%c$Nit|ZjEITb(XWX+!XQ;u4?AWJ75|u2! zvgbsw6@@2`X93F|<7tZMS9Dx@JW~nCkc5e5x#>)JLG#4(ZDlTpreq2Ko0(3QMNuaW z<~Sl{N2%Rj&G$k!<+dM@vsvJz*UEnqr7dnL=gu>92;sl1Bn-0E3;IP9;z;{Hun zT~BXWl$hJ#n>EZ^sd*af^8Ip8M6uS%Y(cwIc{H1*o-g%|WS%7g0}Tb61DaL~r#I^d z&fa{TvCIZ*SeDl|`)?9xotOBEjUW$HKd-u8H0P67tk-;jB&_AAVFQk5y)Sz4?0=OV z(AM~LlGUf=a)YI;j5x&uR&h329n$OVvO4w}ZyKUfab=87d9`+smaCp7N@HoW<4Fi&=^206a(vQsVQXvaB+oi9->;>c0fsnRJuum4{= z#wp6D4y6rc#p|^lw3_;9oYYe#54&MN+3uB#af*1qg!lc>P023KgE%ZNJMJ7OLh?Lu zY?N;fM#q=o*l}yM$Yw>nU)+nkq{t&wrCHYY73V^wett5Hw3Y~By*=; tvAXibseWnXYXbv0@+j~s!`Z;v?}lS@s2#2Oj-`;TuEfdCjB}hB{|CR6*6aWP literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..ddc5748c --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,536 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-15 00:04+0100\n" +"PO-Revision-Date: 2014-04-15 00:04+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/choiceinput/html/quest.tpl:14 +#: questtypes/crossword/html/quest.tpl:30 +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/crossword/html/quest.tpl:22 +#: questtypes/crossword/html/submission.tpl:22 +msgid "vertical" +msgstr "vertikal" + +#: questtypes/crossword/html/quest.tpl:24 +#: questtypes/crossword/html/submission.tpl:24 +msgid "horizontal" +msgstr "horizontal" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:40 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:42 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/achievements/index.tpl:7 views/html/characters/character.tpl:31 +#: views/html/seminarymenu/index.tpl:4 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/achievements/index.tpl:15 +msgid "Secret Achievement" +msgstr "Geheime Errungenschaft" + +#: views/html/achievements/index.tpl:19 +msgid "Continue playing to unlock this secret Achievement" +msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" + +#: views/html/charactergroups/group.tpl:7 +#: views/html/charactergroups/groupsgroup.tpl:7 +#: views/html/charactergroups/index.tpl:7 +#: views/html/characters/character.tpl:53 views/html/seminarymenu/index.tpl:3 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:18 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/charactergroups/group.tpl:20 +msgid "Members" +msgstr "Mitglieder" + +#: views/html/charactergroups/group.tpl:20 +msgid "Member" +msgstr "Mitglied" + +#: views/html/charactergroups/group.tpl:25 +#: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:15 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:40 +#: views/html/questgroups/questgroup.tpl:43 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:17 +#: views/html/charactergroupsquests/quest.tpl:7 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:17 +msgid "Description" +msgstr "Beschreibung" + +#: views/html/charactergroupsquests/quest.tpl:20 +msgid "Rules" +msgstr "Regeln" + +#: views/html/charactergroupsquests/quest.tpl:27 +msgid "Won Quest" +msgstr "Gewonnene Quest" + +#: views/html/charactergroupsquests/quest.tpl:33 +msgid "Lost Quest" +msgstr "Verlorene Quest" + +#: views/html/characters/character.tpl:16 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:20 views/html/users/user.tpl:29 +msgid "Level" +msgstr "Level" + +#: views/html/characters/character.tpl:39 views/html/seminarybar/index.tpl:28 +#, php-format +msgid "achieved at: %s" +msgstr "erhalten am: %s" + +#: views/html/characters/register.tpl:7 +msgid "Create Character" +msgstr "Charakter erstellen" + +#: views/html/characters/register.tpl:20 +#, php-format +msgid "Character name is too short (min. %d chars)" +msgstr "Der Charaktername ist zu kurz (min. %d Zeichen)" + +#: views/html/characters/register.tpl:22 +#, php-format +msgid "Character name is too long (max. %d chars)" +msgstr "Der Charaktername ist zu lang (max. %d Zeichen)" + +#: views/html/characters/register.tpl:24 +msgid "Character name contains illegal characters" +msgstr "Der Charaktername enthält ungültige Zeichen" + +#: views/html/characters/register.tpl:26 +msgid "Character name invalid" +msgstr "Der Charaktername ist ungültig" + +#: views/html/characters/register.tpl:38 +msgid "Character properties" +msgstr "Charaktereigenschaften" + +#: views/html/characters/register.tpl:39 views/html/characters/register.tpl:40 +msgid "Character name" +msgstr "Charaktername" + +#: views/html/characters/register.tpl:41 +msgid "Character type" +msgstr "Charaktertyp" + +#: views/html/characters/register.tpl:52 +#, php-format +msgid "The Seminary field “%s” is invalid" +msgstr "Das Kursfeld „%s“ ist ungültig" + +#: views/html/characters/register.tpl:57 +msgid "Seminary fields" +msgstr "Kursfelder" + +#: views/html/characters/register.tpl:79 views/html/seminaries/create.tpl:14 +#: views/html/users/create.tpl:17 +msgid "create" +msgstr "erstellen" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 +#: views/html/menu/index.tpl:6 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:74 views/html/users/register.tpl:75 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:14 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:14 +msgid "register yourself" +msgstr "registriere dich" + +#: views/html/menu/index.tpl:2 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:3 views/html/seminaries/create.tpl:6 +#: views/html/seminaries/delete.tpl:6 views/html/seminaries/edit.tpl:6 +#: views/html/seminaries/index.tpl:1 +msgid "Seminaries" +msgstr "Kurse" + +#: views/html/menu/index.tpl:8 +msgid "Logout" +msgstr "Logout" + +#: views/html/quests/quest.tpl:50 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/quest.tpl:55 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:57 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:71 views/html/quests/quest.tpl:80 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:14 +#, php-format +msgid "Submission of %s" +msgstr "Lösungen von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht am %s um %s Uhr" + +#: views/html/seminaries/create.tpl:7 +msgid "New seminary" +msgstr "Neuer Kurs" + +#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 +#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 +msgid "Title" +msgstr "Titel" + +#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:39 +msgid "Delete seminary" +msgstr "Kurs löschen" + +#: views/html/seminaries/delete.tpl:9 +#, php-format +msgid "Should the seminary “%s” really be deleted?" +msgstr "Soll der Kurs „%s“ wirklich gelöscht werden?" + +#: views/html/seminaries/delete.tpl:11 views/html/users/delete.tpl:6 +msgid "delete" +msgstr "löschen" + +#: views/html/seminaries/delete.tpl:12 views/html/users/delete.tpl:7 +msgid "cancel" +msgstr "abbrechen" + +#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:38 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:14 views/html/users/edit.tpl:17 +msgid "save" +msgstr "speichern" + +#: views/html/seminaries/index.tpl:4 +msgid "Create new seminary" +msgstr "Neuen Kurs erstellen" + +#: views/html/seminaries/index.tpl:23 views/html/seminaries/seminary.tpl:42 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminaries/index.tpl:25 +msgid "Create a Character" +msgstr "Erstelle einen Charakter" + +#: views/html/seminaries/index.tpl:27 +#, php-format +msgid "Your Character “%s” has not been activated yet" +msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" + +#: views/html/seminarybar/index.tpl:14 +msgid "Last Quest" +msgstr "Letzter Speicherpunkt" + +#: views/html/seminarymenu/index.tpl:5 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:78 views/html/users/register.tpl:79 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +#: views/html/users/user.tpl:12 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: views/html/users/delete.tpl:2 views/html/users/user.tpl:6 +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:5 +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:10 +#, php-format +msgid "registered on %s" +msgstr "registriert am %s" + +#: views/html/users/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:14 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:16 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:18 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:20 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:25 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:27 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:29 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:31 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:36 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:38 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:40 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:42 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:47 views/html/users/register.tpl:49 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:54 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:56 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:58 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:85 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:11 +msgid "Name" +msgstr "Name" + +#: views/html/users/user.tpl:34 +msgid "Roles" +msgstr "Rollen" + +#~ msgid "Usergroups" +#~ msgstr "Benutzergruppen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..f25689b3 --- /dev/null +++ b/models/AchievementsModel.inc @@ -0,0 +1,508 @@ + + * @copyright 2014 Heinrich-Heine-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(); + } + + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all Achievements of a Seminary. + * + * @param int $seminaryId ID of Seminary to get Achievements of + * @return array Achievements data + */ + public function getAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? '. + 'ORDER BY achievements.pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ')', + 'ii', + $seminaryId, + $characterId + ); + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + + + return !empty($data); + } + + } + +?> diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..115d1fb2 --- /dev/null +++ b/models/AvatarsModel.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 Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..e88b1826 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,172 @@ + + * @copyright 2014 Heinrich-Heine-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, motto '. + '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, motto '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + } + +?> diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc new file mode 100644 index 00000000..4490168d --- /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, xps '. + '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..c659afec --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,396 @@ + + * @copyright 2014 Heinrich-Heine-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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_id, seminaries.url 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, characters.avatar_id, 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.avatar_id, 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + + } + +?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> 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..2c2d5eae --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its URL. + * + * @throws IdNotFoundException + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $seminaryMediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its ID. + * + * @throws IdNotFoundException + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE 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..3e2ced21 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,589 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..87cadc9b --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,372 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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 quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..1fd82319 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..251ade4f --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, seminarymedia_id '. + '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, seminarymedia_id '. + '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, seminarymedia_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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..efad3bde --- /dev/null +++ b/models/SeminarycharacterfieldsModel.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 the Seminarycharacterfields-tables. + * + * @author Oliver Hanraths + */ + class SeminarycharacterfieldsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new SeminarycharacterfieldsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..88a7ed2d --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,278 @@ + + * @copyright 2014 Heinrich-Heine-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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..08029501 --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
                  + &$text) : ?> + 0) : ?> + + + + + +

                  + +
                  diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..200b3c7f --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
                  + &$text) : ?> + 0) : ?> + + + + +
                  diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..65833c87 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,352 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + var_dump($matrix[$x][$y]['indices']); + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..d202eb8f --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,49 @@ +
                  + + + + + + + + + + +
                  + + 0) : ?> + + +
                  +
                    + +
                  1. + + : + + : + + +
                  2. + +
                  + +
                  + diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..c47e414d --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,48 @@ +
                  + + + + + + + + + + +
                  + + 0) : ?> + + +
                  +
                    + +
                  1. + + : + + : + + +
                  2. + +
                  +
                  + 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..90843218 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Match drops with user answers + foreach($drops as &$drop) + { + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..1e772edf --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,146 @@ + + * @copyright 2014 Heinrich-Heine-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 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..1af4dd6a --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                  + +
                  + + + +
                  + +
                  + +
                  + + + +
                  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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..456db617 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,261 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) + { + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..c2c32595 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                  +
                  + +

                  +
                    + &$answer) : ?> +
                  1. + /> + +
                  2. + +
                  +
                  + + + + + + + +
                  diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ae14f199 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                    + &$question) : ?> +
                  1. +

                    +
                      + +
                    1. + + × + +
                    2. + +
                    +
                  2. + +
                  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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..0c72c1de --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                  + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                  + + +
                  +
                  + : +
                    + +
                  • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                  • + +
                  + +
                  + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                  + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                  + + + + + + + +
                  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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.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/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..fdaf2d7c --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,33 @@ + +
                  + +
                  + +

                  +

                  + +
                    + +
                  • + + + +

                    + + + + + +
                    + + +
                    +
                    + +
                    +

                     %

                    +
                    + +
                  • + +
                  diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..ba53c97b --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,52 @@ + +
                  + +
                  + +

                  +

                  +

                  +
                  +
                  + +
                  +
                  +

                  + +
                  +
                    +
                  • .
                  • +
                  •  XPs
                  • +
                  • 1) ? _('Members') : _('Member')?>
                  • +
                  +
                  + +
                  +

                  +
                    + +
                  • + +

                    + +

                    +

                    XP

                    +
                  • + +
                  +
                  + +
                  +

                  +
                    + +
                  • +

                    + format(new \DateTime($quest['created']))?> + + / XP +

                    +
                  • + +
                  +
                  diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..93aeaba7 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,22 @@ + +
                  + +
                  + +

                  +

                  +

                  + + + + +

                  +
                    + +
                  • + +
                  diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..e0f54b94 --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,13 @@ + +
                  + +
                  + +

                  +

                  + +
                    + +
                  • + +
                  diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..8112a913 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,51 @@ + +
                  + +
                  + +

                  +

                  +

                  +

                  + + + + + +
                  +

                  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..a19f2355 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,131 @@ + +
                  + +
                  + +

                  +

                  +

                  + +
                  +
                  +
                  +
                  + +
                  +

                  :  %

                  +
                  +
                  +

                  +

                  +
                  +
                  +

                  +

                  XP

                  +
                  +
                  +

                  .

                  +

                  +
                  + +

                  +
                    + +
                  • + + + +

                    +

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

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

                  +
                    + +
                  • + +

                     XPs

                    +
                  • + +
                  +
                  + +
                  +

                  Ranking

                  +
                    +
                  • + +

                    7. Anduin

                    +

                    Level 27 (1500 XP)

                    +
                  • +
                  • + +

                    8. Jaina

                    +

                    Level 26 (1400 XP)

                    +
                  • +
                  • + +

                    9. Uther

                    +

                    Level 25 (1300 XP)

                    +
                  • +
                  • + +

                    10. Lothar

                    +

                    Level 24 (1200 XP)

                    +
                  • +
                  • + +

                    11. Morris

                    +

                    Level 23 (1100 XP)

                    +
                  • +
                  +
                  +
                  + +
                  +

                  Thematischer Fortschritt

                  + +
                  + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..a1227a5a --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,19 @@ + +
                  + +
                  + +

                  +

                  + +
                    + +
                  • + +

                    + +

                    +

                    XP

                    +
                  • + +
                  diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..2916cfe4 --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,80 @@ + +
                  + +
                  + +

                  +

                  + +
                  + +
                    + &$settings) : ?> +
                  • +
                      + $value) : ?> +
                    • + +
                    • + +
                    +
                  • + +
                  + +
                  + + +
                  + + +
                  + + +
                    + &$settings) : ?> +
                  • + +
                  + +
                  + + + + + required="required"/> + + + + + +
                  + +
                  + +
                  diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..0f68bc07 --- /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..9cfcf8f3 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,58 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                  + +
                  +
                  + +
                  + + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..ef919187 --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                  +

                  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..c2662789 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
                • The Legend of Z
                • + 0) : ?>
                • +
                • + + +
                • + +
                • + diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..4d3ce2b9 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
                  + +
                  + +

                  + + + + +

                  :

                  + +

                  + + +

                  + + + + + 0) : ?> +

                  +
                    + +
                  • + +
                    +
                    +

                    Fortschritt:

                    +
                    + +
                    +

                    / XP

                    +
                    +
                  • + +
                  + + + + + +

                  + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + 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..26911951 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,96 @@ + +
                  + +
                  + +

                  + +

                  + + 0) : ?> +
                  +

                  + + + +
                  + +

                  + + +

                  + 0 || !empty($questtext['abort_text'])) : ?> +
                    + +
                  • + + +
                  • + +
                  + + +
                  +
                  + + + +
                  + +

                  + +

                  + +

                  +
                  + + + +
                  +

                  +

                  + + + +

                  :

                  + + +
                  + + + +
                  + 0) : ?> +
                    + + +
                  • + : + + + + + +
                  • + +
                  • + : + + + + + +
                  • + + +
                  + + : + + Spiel vorbei + +
                  + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..a29700db --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,17 @@ + +
                  + +
                  + +

                  + +

                  + + + + + +

                  +
                  + +
                  diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..e2a17579 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,41 @@ + +
                  + +
                  + +

                  + +

                  + + + + + +
                  +

                  +
                    + +
                  • + +
                  • + +
                  + +

                  +
                    + +
                  • + +
                  • + +
                  + +

                  +
                    + +
                  • + +
                  • + +
                  +
                  diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..30938357 --- /dev/null +++ b/views/html/seminaries/create.tpl @@ -0,0 +1,15 @@ + +
                  + +
                  + +

                  +

                  + +
                  +
                  + +
                  +
                  + +
                  diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl new file mode 100644 index 00000000..8e53fdb5 --- /dev/null +++ b/views/html/seminaries/delete.tpl @@ -0,0 +1,13 @@ + +
                  + +
                  + +

                  +

                  + + +
                  + + +
                  diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl new file mode 100644 index 00000000..66d7efcd --- /dev/null +++ b/views/html/seminaries/edit.tpl @@ -0,0 +1,15 @@ + +
                  + +
                  + +

                  +

                  + +
                  +
                  + +
                  +
                  + +
                  diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..986991a9 --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,33 @@ +

                  + 0) : ?> + + +
                    + +
                  • + + + +

                    + 0) : ?> + + + + +

                    +
                    + +
                    + 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..7697d758 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,43 @@ + +
                  + +
                  + + +

                  +

                  + + +

                  + + + + +

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

                  diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..0879970c --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,64 @@ +
                  +

                  + + +
                  + + +
                  +

                  +

                  +
                  + + + +
                  +

                  +
                    +
                  • + + + +

                    +

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

                    +
                  • +
                  +
                  + + +
                  +

                  Wille und die Majas

                  +
                    +
                  • + +

                    Anduin

                    +

                    Level 27 (1500 XP)

                    +
                  • +
                  • + +

                    Jaina

                    +

                    Level 26 (1400 XP)

                    +
                  • +
                  • + +

                    Uther

                    +

                    Level 25 (1300 XP)

                    +
                  • +
                  • + +

                    Lothar

                    +

                    Level 24 (1200 XP)

                    +
                  • +
                  • + +

                    Morris

                    +

                    Level 23 (1100 XP)

                    +
                  • +
                  +

                  Gildenprofil ansehen

                  +
                  diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..041c4f43 --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,6 @@ +
                • + 0) : ?>
                • +
                • +
                • +
                • + 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..d4f38bb7 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                  +

                  + +
                  +
                  + +
                  + +
                  + +
                  + +
                  + +
                  +
                  + +
                  diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..56ba5d7b --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                  +

                  + +
                  +
                  + +
                  + +
                  + +
                  + +
                  + +
                  +
                  + +
                  diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..5d2883af --- /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..932b4c20 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                  + +

                  + +

                  .

                  + +
                  +
                  + +
                  + +
                  +
                  + +
                  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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..184e0a0c --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,86 @@ +

                  + +

                  + +
                    + &$settings) : ?> +
                  • +
                      + $value) : ?> +
                    • + getMessage(); + break; + } ?> +
                    • + +
                    +
                  • + +
                  + +
                  +
                  + + />
                  + + />
                  + + />
                  + + />
                  + + />
                  +
                  + +
                  diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..745097b0 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,35 @@ +

                  +

                  + 0) : ?> + + +

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

                  + +

                  +
                    + +
                  • + +

                    + +

                    + 0) : ?> + + + + +

                    +

                    +
                  • + +
                  + +

                  + 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..b9dabe4a --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,247 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} +aside{display:none} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + + +/** Quest Types **/ + +.crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} +.crossword td{background:#d7d4cf;padding:1px} +.crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} +.crossword ol{list-style-type:decimal;margin-left:25px} +.crossword li{margin-top:20px} +.crossword .index{position:absolute;font-size:0.625em;margin:-2px 0 0 2px} + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:75%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} +article{background:#f7f5f2;float:left} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:100%} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} +aside .cranks li:nth-child(odd){background:#f7f5f2} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Tue, 15 Apr 2014 00:26:02 +0200 Subject: [PATCH 224/340] remove forgotten var_dump() --- questtypes/crossword/CrosswordQuesttypeController.inc | 1 - 1 file changed, 1 deletion(-) diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc index 65833c87..ba3e5a0c 100644 --- a/questtypes/crossword/CrosswordQuesttypeController.inc +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -222,7 +222,6 @@ ); if($x == $startX) { $matrix[$x][$y]['indices'][] = $index; - var_dump($matrix[$x][$y]['indices']); } if(array_key_exists('answer', $word)) { From 399d0bf0140b66a4513c7ed42ca342491c218e7f Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 13:13:13 +0200 Subject: [PATCH 225/340] implement Questtype ?Boss-Fight? --- .../bossfight/BossfightQuesttypeAgent.inc | 24 ++ .../BossfightQuesttypeController.inc | 244 ++++++++++++++++++ .../bossfight/BossfightQuesttypeModel.inc | 183 +++++++++++++ questtypes/bossfight/html/quest.tpl | 58 +++++ questtypes/bossfight/html/submission.tpl | 43 +++ 5 files changed, 552 insertions(+) create mode 100644 questtypes/bossfight/BossfightQuesttypeAgent.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeController.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeModel.inc create mode 100644 questtypes/bossfight/html/quest.tpl create mode 100644 questtypes/bossfight/html/submission.tpl diff --git a/questtypes/bossfight/BossfightQuesttypeAgent.inc b/questtypes/bossfight/BossfightQuesttypeAgent.inc new file mode 100644 index 00000000..6d99ec44 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeAgent.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 a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeController.inc b/questtypes/bossfight/BossfightQuesttypeController.inc new file mode 100644 index 00000000..366aabc8 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeController.inc @@ -0,0 +1,244 @@ + + * @copyright 2014 Heinrich-Heine-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 BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeController 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) + { + // Prepare session + $this->prepareSession($quest['id']); + + // Remove previous answers + $this->Bossfight->clearCharacterSubmissions($quest['id'], $character['id']); + + // Save answers + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) { + $this->Bossfight->setCharacterSubmission($stage['id'], $character['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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + + // Prepare session + $this->prepareSession($quest['id']); + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + + return ($lives['boss'] == 0 && $lives['character'] > 0); + } + + + /** + * Action: quest. + * + * Display a stage with a text and the answers for the following + * stages. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Prepare session + $this->prepareSession($quest['id']); + + // Get Stage + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit_stages'))) + { + $stages = $this->request->getPostParam('submit_stages'); + $stageId = array_keys($stages)[0]; + $stage = $this->Bossfight->getStageById($stageId); + } + else + { + $_SESSION['quests'][$quest['id']]['stages'] = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + } + + // Store Stage in session + if(count($_SESSION['quests'][$quest['id']]['stages']) == 0 || $_SESSION['quests'][$quest['id']]['stages'][count($_SESSION['quests'][$quest['id']]['stages'])-1]['id'] != $stage['id']) { + $_SESSION['quests'][$quest['id']]['stages'][] = $stage; + } + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + // Get Child-Stages + $childStages = $this->Bossfight->getChildStages($stage['id']); + + // Get answer of Character + if($this->request->getGetParam('show-answer') == 'true') { + foreach($childStages as &$childStage) { + $childStage['answer'] = $this->Bossfight->getCharacterSubmission($childStage['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stage', $stage); + $this->set('lives', $lives); + $this->set('childStages', $childStages); + } + + + /** + * Action: submission. + * + * Display all stages with the answers the character has + * choosen. + * + * @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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Get stages + $stages = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + while(!is_null($stage)) + { + $stages[] = $stage; + + $childStages = $this->Bossfight->getChildStages($stage['id']); + $stage = null; + foreach($childStages as &$childStage) + { + if($this->Bossfight->getCharacterSubmission($childStage['id'], $character['id'])) + { + $stage = $childStage; + break; + } + } + } + + // Calculate lives + $stages[0]['lives'] = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + for($i=1; $i $stages[$i-1]['lives']['character'] + $stages[$i]['livedrain_character'], + 'boss' => $stages[$i-1]['lives']['boss'] + $stages[$i]['livedrain_boss'], + ); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stages', $stages); + } + + + + + /** + * Prepare the session to store stage information in + * + * @param int $questId ID of Quest + */ + private function prepareSession($questId) + { + if(!array_key_exists('quests', $_SESSION)) { + $_SESSION['quests'] = array(); + } + if(!array_key_exists($questId, $_SESSION['quests'])) { + $_SESSION['quests'][$questId] = array(); + } + if(!array_key_exists('stages', $_SESSION['quests'][$questId])) { + $_SESSION['quests'][$questId]['stages'] = array(); + } + } + + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeModel.inc b/questtypes/bossfight/BossfightQuesttypeModel.inc new file mode 100644 index 00000000..a05927f0 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeModel.inc @@ -0,0 +1,183 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\questtypes; + + + /** + * Model of the BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get a Boss-Fight. + * + * @throws IdNotFoundException + * @param int $questId ID of Quest + * @return array Boss-Fight data + */ + public function getBossFight($questId) + { + $data = $this->db->query( + 'SELECT bossname, boss_seminarymedia_id, lives_character, lives_boss '. + 'FROM questtypes_bossfight '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Stage to begin the Boss-Fight with. + * + * @param int $questId ID of Quest + * @return array Data of first Stage + */ + public function getFirstStage($questId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ? AND parent_stage_id IS NULL', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get a Stage by its ID. + * + * @param int $stageId ID of Stage + * @return array Stage data or null + */ + public function getStageById($stageId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE id = ?', + 'i', + $stageId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get the follow-up Stages for a Stage. + * + * @param int $parentStageId ID of Stage to get follow-up Stages for + * @return array List of follow-up Stages + */ + public function getChildStages($parentStageId) + { + return $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE parent_stage_id = ?', + 'i', + $parentStageId + ); + } + + + /** + * Reset all Character submissions of a Boss-Fight. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + */ + public function clearCharacterSubmissions($questId, $characterId) + { + $this->db->query( + 'DELETE FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id IN ('. + 'SELECT id '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ?'. + ') AND character_id = ?', + 'ii', + $questId, + $characterId + ); + } + + + /** + * Save Character’s submitted answer for one Boss-Fight-Stage. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + */ + public function setCharacterSubmission($stageId, $characterId) + { + $this->db->query( + 'INSERT INTO questtypes_bossfight_stages_characters '. + '(questtypes_bossfight_stage_id, character_id) '. + 'VALUES '. + '(?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_bossfight_stage_id = ?', + 'iii', + $stageId, $characterId, $stageId + ); + } + + + /** + * Get answer of one Boss-Fight-Stage submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return boolean Stage taken + */ + public function getCharacterSubmission($stageId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_bossfight_stage_id '. + 'FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id = ? AND character_id = ? ', + 'ii', + $stageId, $characterId + ); + + + return (!empty($data)); + } + + } + +?> diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl new file mode 100644 index 00000000..bcf2bcd0 --- /dev/null +++ b/questtypes/bossfight/html/quest.tpl @@ -0,0 +1,58 @@ + + + + + + + + + + + +
                  + + + + + +
                  + : + 0) : ?> + + ♥ + + + + + + : + 0) : ?> + + ♥ + + + + +
                  + +

                  + +
                  + +
                    + +
                  • + + → + + +
                  • + + +
                  • + + +
                  • + +
                  +
                  diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl new file mode 100644 index 00000000..a8b7eefb --- /dev/null +++ b/questtypes/bossfight/html/submission.tpl @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + +
                  + + + + + +
                  + +
                  + : + 0) : ?> + + ♥ + + + + + + : + 0) : ?> + + ♥ + + + + +
                  From bf16cb9c9da27b044d3d134a831dc872d0cdf3ef Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 13:57:13 +0200 Subject: [PATCH 226/340] show Character groups members in Seminarybar --- controllers/SeminarybarController.inc | 25 ++++++++++++++++++- models/CharactergroupsModel.inc | 29 +++++++++++++++++++-- models/CharactersModel.inc | 2 +- views/html/seminarybar/index.tpl | 36 ++++++++------------------- 4 files changed, 63 insertions(+), 29 deletions(-) diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc index 1a36f136..53039526 100644 --- a/controllers/SeminarybarController.inc +++ b/controllers/SeminarybarController.inc @@ -25,7 +25,7 @@ * * @var array */ - public $models = array('characters', 'quests', 'questgroups', 'achievements'); + public $models = array('characters', 'quests', 'questgroups', 'achievements', 'charactergroups', 'avatars', 'media'); @@ -60,12 +60,35 @@ $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); $lastAchievement = array_shift($achievements); + // Get Character group members + $characterGroups = array(); + foreach($this->Charactergroups->getGroupsForCharacter($character['id']) as $group) + { + $groupsgroup = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']); + if($groupsgroup['preferred']) + { + $group['members'] = $this->Characters->getCharactersForGroup($group['id']); + foreach($group['members'] as &$member) + { + if(!is_null($member['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($member['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $member['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + $characterGroups[] = $group; + } + } + // Pass data to view $this->set('seminary', $seminary); $this->set('character', $character); $this->set('lastQuest', $lastQuest); $this->set('lastAchievement', $lastAchievement); + $this->set('characterGroups', $characterGroups); } } diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc index e88b1826..e042fd10 100644 --- a/models/CharactergroupsModel.inc +++ b/models/CharactergroupsModel.inc @@ -44,7 +44,7 @@ public function getGroupsroupsForSeminary($seminaryId) { return $this->db->query( - 'SELECT id, name, url '. + 'SELECT id, name, url, preferred '. 'FROM charactergroupsgroups '. 'WHERE seminary_id = ?', 'i', @@ -64,7 +64,7 @@ public function getGroupsgroupByUrl($seminaryId, $groupsgroupUrl) { $data = $this->db->query( - 'SELECT id, name, url '. + 'SELECT id, name, url, preferred '. 'FROM charactergroupsgroups '. 'WHERE seminary_id = ? AND url = ?', 'is', @@ -79,6 +79,31 @@ } + /** + * Get a Character groups-group by its ID. + * + * @throws IdNotFoundException + * @param string $groupsgroupId ID of the Character groups-group + * @return array Character groups-group data + */ + public function getGroupsgroupById($groupsgroupId) + { + $data = $this->db->query( + 'SELECT id, name, url, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE id = ?', + 'i', + $groupsgroupId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupId); + } + + + return $data[0]; + } + + /** * Get Character groups for a Character groups-group. * diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index c659afec..81b24018 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -83,7 +83,7 @@ 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.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, 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 '. diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl index 0879970c..60752cc7 100644 --- a/views/html/seminarybar/index.tpl +++ b/views/html/seminarybar/index.tpl @@ -32,33 +32,19 @@
                  -

                  Wille und die Majas

                  + +

                    +
                  • - -

                    Anduin

                    -

                    Level 27 (1500 XP)

                    -
                  • -
                  • - -

                    Jaina

                    -

                    Level 26 (1400 XP)

                    -
                  • -
                  • - -

                    Uther

                    -

                    Level 25 (1300 XP)

                    -
                  • -
                  • - -

                    Lothar

                    -

                    Level 24 (1200 XP)

                    -
                  • -
                  • - -

                    Morris

                    -

                    Level 23 (1100 XP)

                    + + + +

                    +

                    ( XPs)

                  • +
                  -

                  Gildenprofil ansehen

                  +

                  +
                  From 64805a27dba5052f8224f8b8cc7ed1f7bbc07eec Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 16:23:40 +0200 Subject: [PATCH 227/340] implement LibraryAgent --- agents/intermediate/LibraryAgent.inc | 35 ++++++ controllers/CharactersController.inc | 11 +- controllers/LibraryController.inc | 129 ++++++++++++++++++++++ models/QuestsModel.inc | 19 ++++ models/QuesttopicsModel.inc | 154 +++++++++++++++++++++++++++ views/html/characters/character.tpl | 32 +----- views/html/library/index.tpl | 21 ++++ views/html/library/topic.tpl | 28 +++++ 8 files changed, 401 insertions(+), 28 deletions(-) create mode 100644 agents/intermediate/LibraryAgent.inc create mode 100644 controllers/LibraryController.inc create mode 100644 models/QuesttopicsModel.inc create mode 100644 views/html/library/index.tpl create mode 100644 views/html/library/topic.tpl diff --git a/agents/intermediate/LibraryAgent.inc b/agents/intermediate/LibraryAgent.inc new file mode 100644 index 00000000..aedc890a --- /dev/null +++ b/agents/intermediate/LibraryAgent.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 Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index fbeb0ead..70a651dd 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media'); + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media', 'questtopics'); /** * Required components * @@ -123,6 +123,14 @@ // Get Achievements $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + // Pass data to view $this->set('seminary', $seminary); @@ -131,6 +139,7 @@ $this->set('user', $user); $this->set('groups', $groups); $this->set('achievements', $achievements); + $this->set('questtopics', $questtopics); } diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc new file mode 100644 index 00000000..1aa78b80 --- /dev/null +++ b/controllers/LibraryController.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\controllers; + + + /** + * Controller of the LibraryAgent to list Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('questtopics', 'seminaries', 'quests', 'questgroups'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Questtopics of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + // Get Quest count + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + + // Get Character progress + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopics', $questtopics); + } + + + /** + * Action: topic. + * + * Show a Questtopic and its Quests with Questsubtopics. + * + * @throws IdNotFoundException + * @param string $eminaryUrl URL-Title of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + */ + public function topic($seminaryUrl, $questtopicUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = IntermediateController::$character; + + // Get Questtopic + $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl); + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForQuesttopic($questtopic['id']) as $quest) + { + if($this->Quests->hasCharacterEnteredQuest($quest['id'], $character['id'])) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + + // Get Subtopics + $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']); + + $quests[] = $quest; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopic', $questtopic); + $this->set('quests', $quests); + } + + } + +?> diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 87cadc9b..815e8294 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -325,6 +325,25 @@ } + /** + * Get all Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return array Quests for this Questtopic + */ + public function getQuestsForQuesttopic($questtopicId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_questsubtopics '. + 'INNER JOIN quests ON quests.id = quests_questsubtopics.quest_id '. + 'WHERE quests_questsubtopics.questsubtopic_id = ?', + 'i', + $questtopicId + ); + } + + /** diff --git a/models/QuesttopicsModel.inc b/models/QuesttopicsModel.inc new file mode 100644 index 00000000..abd246f7 --- /dev/null +++ b/models/QuesttopicsModel.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\models; + + + /** + * Model to interact with Questtopics-table. + * + * @author Oliver Hanraths + */ + class QuesttopicsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttopicsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get a Questtopic by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + * @return array Questtopic data + */ + public function getQuesttopicByUrl($seminaryId, $questtopicUrl) + { + $data = $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $questtopicUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questtopicUrl); + } + + + return $data[0]; + } + + + /** + * Get all Questtopics for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questtopics + */ + public function getQuesttopicsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get count of Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return int Count of Quests + */ + public function getQuestCountForQuesttopic($questtopicId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_questsubtopics.quest_id) AS c ' . + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'WHERE questsubtopics.questtopic_id = ?', + 'i', + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get count of Quests that are linked to a Questtopic and are + * unlocked by a Character. + * + * @param int $questtopicId ID of Questtopic + * @param int $characterId ID of Character + * @return int Count of Quests + */ + public function getCharacterQuestCountForQuesttopic($questtopicId, $characterId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_characters.quest_id) AS c '. + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'INNER JOIN quests_characters ON quests_characters.quest_id = quests_questsubtopics.quest_id AND quests_characters.character_id = ? AND quests_characters.status = 3 '. + 'WHERE questsubtopics.questtopic_id = ?', + 'ii', + $characterId, + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get all Questsubtopics for a Quest. + * + * @param int $questId ID of Quest + * @return array List of Questsubtopics + */ + public function getQuestsubtopicsForQuest($questId) + { + return $this->db->query( + 'SELECT DISTINCT id, questtopic_id, title, url '. + 'FROM quests_questsubtopics '. + 'INNER JOIN questsubtopics ON questsubtopics.id = quests_questsubtopics.questsubtopic_id '. + 'WHERE quests_questsubtopics.quest_id = ?', + 'i', + $questId + ); + } + + } + +?> diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index a19f2355..dedab4c8 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -94,38 +94,16 @@
                  -

                  Thematischer Fortschritt

                  +

                  diff --git a/views/html/library/index.tpl b/views/html/library/index.tpl new file mode 100644 index 00000000..be5b441d --- /dev/null +++ b/views/html/library/index.tpl @@ -0,0 +1,21 @@ + +
                  + +
                  + +

                  +

                  + +
                    + +
                  • +

                    +
                    +
                    + +
                    +

                    /

                    +
                    +
                  • + +
                  diff --git a/views/html/library/topic.tpl b/views/html/library/topic.tpl new file mode 100644 index 00000000..be396d4f --- /dev/null +++ b/views/html/library/topic.tpl @@ -0,0 +1,28 @@ + +
                  + +
                  + +

                  +

                  +

                  + +
                  +
                  + +
                  +

                  /

                  +
                  + +
                    + +
                  • + +
                      + +
                    • + +
                    +
                  • + +
                  From f64e19e2736dbb43cb7924c7910f36b3a298f67d Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 16:24:27 +0200 Subject: [PATCH 228/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 6655 -> 6875 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 28 +++++++++++++++++--- questtypes/bossfight/html/quest.tpl | 4 +-- questtypes/bossfight/html/submission.tpl | 4 +-- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index fa698bc48281f88a940eac09a8d284b7352f5554..88fa10b4ce8aa7bbf76a8f41c214548c4279ff36 100644 GIT binary patch delta 2457 zcmX}tdrXye9LMn=95@|_n@8m4?T~=r1w8_yB#~5xS|MPigeI2#QZq9CO)Rb90lfi)vJMSw)vWTJw+S{W<*bx99u3e!u5D&-45K zE)UFAeN_|v#+&krp$rhqh(VVzx^T(OiIS6MOa?AOFRs8G^kY6YVivaJ8Vq0_p0w?^ za4qf6Fc*{4=kL!)j;JZ)yon$Ds2BI3IvT;nIEvXgX1#!S)4pWAhMBZKLHaP?VJiNH z8qnXU_h$1@0s62Ix8gGTH*Hij!eQiI^Bn30qj(p-Xxp!%?i)i*&Ks!v$8Gz4)csd5 z1wTgJe*--@ZM}s`XckN8-^@`d#S-q;3wGfuY(E>;Ms+-g9LB>m>A5A=r5M#z7gNzls!<&_;0A2QjTp7p$59b^*z*{x@_AwvzdR5{7WtJA8{*Ib47Y!wthP0z5mx@MKgzBIU)$w-J)U}`FTYGr~yA*WO@^uZXXx)!U5EjM^KqQiyGl5 zYSX=By@0y^8mhya$X8_=h=tN@D;hvw>}B`j=zzV#&581uRohqdq0&ZdCp4UGgh~~m zY&6G@6PjD?SbY}Sfm(|Ip|YFMnmNQCVlT0lV5&7R#;jCImHd;$y+jMKh0s4M4YH2V zKFQIYv9s=!XfZ97t@9+-qSn^$!$)m>CpHl^#8U+OfJVZQA=#NAhafr~`Y$hrQl^uk3n!Yo>_e43!xs~W4N)lI$c>?zn8;M5< zeJ(D7{bc%yPD1P2NMssT@_s6sziMKkB-=_a^4Bo&XL32}6KE$MB)W-uqLf%D>G9V* zGp_WO5homp1cw9Nv11t@q?d-*?+FEa1A|ViD)ZkAF8hW0a4#&&0ijz_XgW*W5 zDf?=we{-F`D(1~uHD8O@<$Rm!-rMgSafV_Ac~`x6-sI~Xdd3O#IYaU3yz-<1-O?Ee jguDBlkZ(sY6rmOhL?Url!NDZYQ71GQ4DpIXg=hW;D*ov{ delta 2276 zcmYk-ZA{fw9Ki9zqge6~B!VfLpe911U?4sOJ^BC^R;HAZA$PiZQO(A32^t*z#w@Aqv1^H__2V>#ALofxmg z#f%epIX;VC|0-S*LNdHdXBl4{M<4trI#C|KmSF`JV_kF=Udp&5x&;dv??7%14`Ut< zpbL5&eeN(;;5%4}pJjeehVT;|SLjpehC=iK6*vW};&=giT^+hPE70rP;&=mk{U*%C zZuI&dWK+Te(MQmP_F@h5hiB-_$D`;2#;_WHL|2~AQyiCL4Yr~Q_TXjMhbGjIPP`YL z@CoEE?Bm1hpNkHoJNybJUBP>Foakd*f?r_+=CH5i5Sq}8+t38N(1f<4_dOQJPooJQ zMico4=i(V$fEUn2swn_#U{Mk0Z>BAbys;gfC=ti~=+3-=-gp8%<4@6=8ATJwHYelwb9C#!Mz{E9oQc0- zEfz4VHljs&51Lp4J(@vWgs(+M(F8A`31qXHeC7|Obo`+b-I_V*mepl0;B||(BY$Bn zA70;sCf$VvT952bXhv`BL|3>OJ(}&& z1iHfe(V`ka?;k|ZcnD2!7+uhtXiXiBo<^_#wUWG@=r6wDt-07X(sL^}q#l}5mUNAc z@waWbgRrD*?ZlmgY1~3=A|^MgHrz4ki)Q+rgv)YCY-w0d+(_I&Y$2{AEC3tZ;So_@ zggGxJt|U0j%%-lwUSb_#^WfGHtSD2yu75tUov?ZGHV@W4>~=u2bmtOO_r-P}y#nV? z&{sb_Oi|4jQZb+Yh}^e07CS*c%&NAemPS{s^DM@p(Edg-x}b9t#_GZtp0 OzpdDom0CUXrT+lo@VagQ diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index ddc5748c..8f2b8ed2 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-15 00:04+0100\n" -"PO-Revision-Date: 2014-04-15 00:04+0100\n" +"POT-Creation-Date: 2014-04-15 16:13+0100\n" +"PO-Revision-Date: 2014-04-15 16:13+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -16,6 +16,14 @@ msgstr "" "X-Poedit-SearchPath-0: views\n" "X-Poedit-SearchPath-1: questtypes\n" +#: questtypes/bossfight/html/quest.tpl:21 +#: questtypes/bossfight/html/quest.tpl:31 +#: questtypes/bossfight/html/submission.tpl:27 +#: questtypes/bossfight/html/submission.tpl:37 +msgid "lost" +msgstr "verloren" + +#: questtypes/bossfight/html/quest.tpl:54 #: questtypes/choiceinput/html/quest.tpl:14 #: questtypes/crossword/html/quest.tpl:30 #: questtypes/dragndrop/html/quest.tpl:16 @@ -154,7 +162,8 @@ msgstr "Verlorene Quest" msgid "Total progress" msgstr "Fortschritt" -#: views/html/characters/character.tpl:20 views/html/users/user.tpl:29 +#: views/html/characters/character.tpl:20 views/html/seminarybar/index.tpl:44 +#: views/html/users/user.tpl:29 msgid "Level" msgstr "Level" @@ -163,6 +172,10 @@ msgstr "Level" msgid "achieved at: %s" msgstr "erhalten am: %s" +#: views/html/characters/character.tpl:97 +msgid "Topic progress" +msgstr "Thematischer Fortschritt" + #: views/html/characters/register.tpl:7 msgid "Create Character" msgstr "Charakter erstellen" @@ -245,6 +258,10 @@ msgstr "oder" msgid "register yourself" msgstr "registriere dich" +#: views/html/library/index.tpl:7 views/html/library/topic.tpl:7 +msgid "Questtopics" +msgstr "Themen" + #: views/html/menu/index.tpl:2 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 @@ -343,6 +360,11 @@ msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" msgid "Last Quest" msgstr "Letzter Speicherpunkt" +#: views/html/seminarybar/index.tpl:48 +#, php-format +msgid "Show %s-Profile" +msgstr "%s-Profil anzeigen" + #: views/html/seminarymenu/index.tpl:5 msgid "Library" msgstr "Bibliothek" diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl index bcf2bcd0..ba427577 100644 --- a/questtypes/bossfight/html/quest.tpl +++ b/questtypes/bossfight/html/quest.tpl @@ -18,7 +18,7 @@ ♥ - + @@ -28,7 +28,7 @@ ♥ - + diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl index a8b7eefb..33cf172f 100644 --- a/questtypes/bossfight/html/submission.tpl +++ b/questtypes/bossfight/html/submission.tpl @@ -24,7 +24,7 @@ ♥ - + @@ -34,7 +34,7 @@ ♥ - + From 39643f4e36237864a8452ba01f34e4da035341b8 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 20:04:46 +0200 Subject: [PATCH 229/340] move Seminary and Character data determination to SeminaryRoleController --- app/controllers/IntermediateController.inc | 31 +-------------- app/controllers/SeminaryRoleController.inc | 44 +++++++++++++--------- controllers/AchievementsController.inc | 2 +- controllers/HtmlController.inc | 4 +- controllers/LibraryController.inc | 4 +- controllers/MediaController.inc | 2 +- controllers/MenuController.inc | 2 +- controllers/SeminarybarController.inc | 6 +-- controllers/SeminarymenuController.inc | 2 +- 9 files changed, 38 insertions(+), 59 deletions(-) diff --git a/app/controllers/IntermediateController.inc b/app/controllers/IntermediateController.inc index 5b980299..19647d7e 100644 --- a/app/controllers/IntermediateController.inc +++ b/app/controllers/IntermediateController.inc @@ -31,18 +31,6 @@ * @var array */ public static $user = null; - /** - * Current Seminary - * - * var array - */ - public static $seminary = null; - /** - * Character of current user and Seminary - * - * @var array - */ - public static $character = null; @@ -79,22 +67,7 @@ // Get userdata try { self::$user = $this->Users->getUserById($this->Auth->getUserId()); - - // Determine user roles - self::$user['roles'] = array(); - $roles = $this->Userroles->getUserrolesForUserById(self::$user['id']); - foreach($roles as &$role) { - self::$user['roles'][] = $role['name']; - } - - // Character - $controller = $this->agent->controller; - if(is_subclass_of($controller, '\hhu\z\controllers\SeminaryRoleController')) - { - $seminaryUrl = $this->request->getParam(3); - self::$seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); - self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); - } + self::$user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById(self::$user['id'])); } catch(\nre\exceptions\IdNotFoundException $e) { } @@ -104,8 +77,6 @@ // Set userdata $this->set('loggedUser', self::$user); - $this->set('loggedSeminary', self::$seminary); - $this->set('loggedCharacter', self::$character); } diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index 324e13e2..77a95a2b 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -33,11 +33,17 @@ */ public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements'); /** - * Data of currently logged in user if any + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary * * @var array */ - public static $user = null; + public static $character = null; @@ -71,11 +77,24 @@ { parent::preFilter($request, $response); + // Get Seminary and Character data + try { + self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + // Check permissions $this->checkPermission($request, $response); // Check achievements $this->checkAchievements($request, $response); + + // Set Seminary and Character data + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$character); } @@ -105,17 +124,6 @@ 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'); @@ -129,7 +137,7 @@ // Check permissions - if(count(array_intersect($userSeminaryRoles, $permissions)) == 0) { + if(count(array_intersect(self::$user['seminaryroles'], $permissions)) == 0) { throw new \nre\exceptions\AccessDeniedException(); } } @@ -186,7 +194,7 @@ 'params' => array( $condition['field'], $condition['value'], - $character['id'] + self::$character['id'] ) ); } @@ -205,7 +213,7 @@ $condition['status'], $condition['groupby'], $condition['quest_id'], - $character['id'] + self::$character['id'] ) ); } @@ -223,7 +231,7 @@ $condition['value'], $condition['groupby'], $condition['meta_achievement_id'], - $character['id'] + self::$character['id'] ) ); } @@ -263,7 +271,7 @@ // Set status if($achieved) { - $this->Achievements->setAchievementAchieved($achievement['id'], $character['id']); + $this->Achievements->setAchievementAchieved($achievement['id'], self::$character['id']); } } } diff --git a/controllers/AchievementsController.inc b/controllers/AchievementsController.inc index b6ef6595..6f363012 100644 --- a/controllers/AchievementsController.inc +++ b/controllers/AchievementsController.inc @@ -59,7 +59,7 @@ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); // Get Character - $character = IntermediateController::$character; + $character = SeminaryRoleController::$character; // Get Achievements $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); diff --git a/controllers/HtmlController.inc b/controllers/HtmlController.inc index 7bf8859e..dd4bc86c 100644 --- a/controllers/HtmlController.inc +++ b/controllers/HtmlController.inc @@ -50,8 +50,8 @@ // Set userdata $this->set('loggedUser', IntermediateController::$user); - $this->set('loggedSeminary', IntermediateController::$seminary); - $this->set('loggedCharacter', IntermediateController::$character); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + $this->set('loggedCharacter', SeminaryRoleController::$character); } } diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc index 1aa78b80..a37618ae 100644 --- a/controllers/LibraryController.inc +++ b/controllers/LibraryController.inc @@ -59,7 +59,7 @@ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); // Get Character - $character = IntermediateController::$character; + $character = SeminaryRoleController::$character; // Get Quest topics $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); @@ -94,7 +94,7 @@ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); // Get Character - $character = IntermediateController::$character; + $character = SeminaryRoleController::$character; // Get Questtopic $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl); diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 4036954b..16e01cbb 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -170,7 +170,7 @@ $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); // Get Character - $character = IntermediateController::$character; + $character = SeminaryRoleController::$character; // Get Achievement $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc index 2be5ea8c..c254a617 100644 --- a/controllers/MenuController.inc +++ b/controllers/MenuController.inc @@ -35,7 +35,7 @@ // Set userdata $this->set('loggedUser', IntermediateController::$user); - $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); } diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc index 53039526..5ab1567b 100644 --- a/controllers/SeminarybarController.inc +++ b/controllers/SeminarybarController.inc @@ -35,15 +35,15 @@ */ public function index() { - if(is_null(IntermediateController::$seminary)) { + if(is_null(SeminaryRoleController::$seminary)) { return; } // Get Seminary - $seminary = IntermediateController::$seminary; + $seminary = SeminaryRoleController::$seminary; // Get Character - $character = IntermediateController::$character; + $character = SeminaryRoleController::$character; if(is_null($character)) { return; } diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc index d827425d..5b5d780f 100644 --- a/controllers/SeminarymenuController.inc +++ b/controllers/SeminarymenuController.inc @@ -36,7 +36,7 @@ // Set userdata $this->set('loggedUser', IntermediateController::$user); - $this->set('loggedSeminary', IntermediateController::$seminary); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); } From 03830a8127900184a59fd110100406fec0877aff Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 20:05:26 +0200 Subject: [PATCH 230/340] give ?only-once? Achievements only to users with Seminary role ?users? --- app/controllers/SeminaryRoleController.inc | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index 77a95a2b..cb7f6b86 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -148,20 +148,16 @@ */ private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) { - // Get Seminary - $seminary = self::$seminary; - - // Get Character - $character = self::$character; - if(is_null($character)) { + // Check if Character is present + if(is_null(self::$character)) { return; } // Get unachieved Achievments - $achievements = array_merge( - $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id']), - $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary($seminary['id']) - ); + $achievements = $this->Achievements->getUnachhievedAchievementsForCharacter(self::$seminary['id'], self::$character['id']); + if(in_array('user', self::$user['seminaryroles'])) { + $achievements = array_merge($achievements, $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary(self::$seminary['id'])); + } // Check conditions foreach($achievements as &$achievement) From fc6dcfcf6d93fd2ba7d17a5ab076a9ff981cec16 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 20:05:52 +0200 Subject: [PATCH 231/340] fix regexs for Seminary fields lists --- views/html/characters/register.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl index 2916cfe4..15317277 100644 --- a/views/html/characters/register.tpl +++ b/views/html/characters/register.tpl @@ -68,7 +68,7 @@ case 'List': ?> Date: Tue, 15 Apr 2014 20:17:11 +0200 Subject: [PATCH 232/340] implement simple overview of Quests for a Seminary --- controllers/QuestsController.inc | 70 ++++++++++++++++++++++++++++++-- models/QuestsModel.inc | 19 +++++++++ views/html/quests/index.tpl | 49 ++++++++++++++++++++++ 3 files changed, 135 insertions(+), 3 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index b630a134..e8ed7fca 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -31,9 +31,10 @@ * @var array */ public $permissions = array( - 'quest' => array('admin', 'moderator', 'user'), - 'submissions' => array('admin', 'moderator'), - 'submission' => array('admin', 'moderator') + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator', 'user'), + 'submission' => array('admin', 'moderator', 'user') ); /** * User seminary permissions @@ -41,6 +42,7 @@ * @var array */ public $seminaryPermissions = array( + 'index' => array('admin', 'moderator', 'user'), 'quest' => array('admin', 'moderator', 'user'), 'submissions' => array('admin', 'moderator'), 'submission' => array('admin', 'moderator') @@ -49,6 +51,68 @@ + /** + * Action: index. + * + * List all Quests for a Seminary. + * + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Prepare filters + $filters = array( + 'questgroups' => array(), + 'questtypes' => array() + ); + + // Get selected filters + $selectedFilters = array( + 'questgroup' => "0", + 'questtype' => "" + ); + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('filters'))) { + $selectedFilters = $this->request->getPostParam('filters'); + } + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForSeminary($seminary['id']) as $quest) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + if($selectedFilters['questgroup'] != "0" && $selectedFilters['questgroup'] != $quest['questgroup']['id']) { + continue; + } + + // Get Questtype + $quest['questtype'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + if($selectedFilters['questtype'] != "" && $selectedFilters['questtype'] != $quest['questtype']['classname']) { + continue; + } + + // Add filter values + $filters['questgroups'][$quest['questgroup']['id']] = $quest['questgroup']; + $filters['questtypes'][$quest['questtype']['classname']] = $quest['questtype']; + + // Add open submissions count + $quest['opensubmissionscount'] = count($this->Characters->getCharactersSubmittedQuest($quest['id'])); + + $quests[] = $quest; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('quests', $quests); + $this->set('filters', $filters); + $this->set('selectedFilters', $selectedFilters); + } + + /** * Action: quest. * diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 815e8294..5b7eda91 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -325,6 +325,25 @@ } + /** + * Get all Quests for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array Quests for this Seminary + */ + public function getQuestsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM questgroups '. + 'INNER JOIN quests ON quests.questgroup_id = questgroups.id '. + 'WHERE questgroups.seminary_id = ?', + 'i', + $seminaryId + ); + } + + /** * Get all Quests that are linked to a Questtopic. * diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl index e69de29b..80a3e4fe 100644 --- a/views/html/quests/index.tpl +++ b/views/html/quests/index.tpl @@ -0,0 +1,49 @@ + +
                  + +
                  + +

                  +

                  + +
                  + + + + + + + + + + + + + + + + + + + + + +
                  + + + + XPs
                  + + + +
                  From 196599911411f83143f5cf574b27a64a3d0324e9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 20:18:57 +0200 Subject: [PATCH 233/340] update submenu for Seminaries --- views/html/seminaries/seminary.tpl | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/views/html/seminaries/seminary.tpl b/views/html/seminaries/seminary.tpl index 7697d758..28488018 100644 --- a/views/html/seminaries/seminary.tpl +++ b/views/html/seminaries/seminary.tpl @@ -5,6 +5,13 @@

                  + 0) : ?> + +

                  @@ -33,11 +40,3 @@
                - - -

                - format(new \DateTime($seminary['created'])))?> -

                From 5ac99345ca0282983cabdfa63288f5e1a8a664b5 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 15 Apr 2014 20:20:23 +0200 Subject: [PATCH 234/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 6875 -> 7126 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 36 ++++++++++++++++---- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 88fa10b4ce8aa7bbf76a8f41c214548c4279ff36..1b1ca27ad7273e1c519ddb5637e377ca714944d2 100644 GIT binary patch delta 2667 zcmYk-drXye9LMpWhwI^@2*lM3DwrU35EW3v0!d4YP)tKfAb3D-=NxV|*zB~h+ETMT z(fGsVx@>mY8b)Q74w_qQtJc(2n>AL8{3ACTTeIs{@6S2Ev-R8aeO|xkInVR_zP}rM zxBi>*z)WiNn4$QIMa0P{V{~It94Ct3ZA=pOVJbe3$@l`M;3>?+37n6gVG(|d8R$v~ zUuWZLu4^zI{mAnH^Ar^h^CG8GJcau3WmJPdVH)1V1(=W+HU~LOk+lq!ST)kNslyoD zhw4}}>br+93!lSm+Bc`|1D~LJI)k)juA@G16EztB*z0H>(sK!%Vlfl-{1SUzhsE!?-&-m+w343E2)w5YthHie$!}+)pJ*dR?Adi?vYd5MRBdGUB zQ4PLfug{@Ib_Vr+G(T#D5;Xo=bm`pSH?s{jWP7a#P>av8*Tbk0cmkE+5$p5l;(81< z1>?wJ-r}U^K1MY>jY|AeR6Ab>s3?=~F&nR;dZMpLH%`xE_>rH?73SdpVT$T}C@sDuk}x%Ph<6=l+Zx1t}lc*c>#yv|8O_Yo?=X?y)S zs%Mu_4a}h${sT2~@qARzFGNkjB740IH3h{Oul>J?N)9(_un3#1&!SfA`>4b&pr+_& zEX04Ug^arrtVbo@fJ($cP3;gW!GQH?RD#DahW5=%R5WxaQA7GBGHT{5>cuatSCBT% zRaAp>s2=`~YtY3!ZNzoRvM@eWB2S`*{Dk#2R08i|U=@`MR5SwDP$MvpZpvcR2<$}< zHlgju+#aWN~o`V`uA1cvDbGL8SsaucDlj?l!H5c>(Gm`7+4 zXgF3AK0;+DQAl(Xdx(96#+sqlS~BMT&^dg%$JRAg4-xoEl*-8)U&zzQbf!2CH(M)V7I*DpxB@ro!!DM%SQkCD|Go07n z*)!<$PkP*MM{Vxt_YL{Y&LL-Du+!(wD`?B}wQ~;N@wW6j;o3l$L7%^~HBuk;JHbDb z7AH9a&Y=C~KrlV|Ol;_D1Cvw9H(W8LB^%dl2)>)LB3zrinle0Z!PZa$c`e?@9B-TB zo!pgL7o8Y-tD}F&&u3rB$jJSFz>% delta 2472 zcmZwIe@xVM9LMnw91=$Ya$+Yaa>{|ChMt0_B#~tPtcb%(2rJ;O^FR*A9d51KcWth* zwp#17w#H_&f3)1BZj~(Om}|{7Ha97&Wo=ZJt)d@)^oM__&)4yxwbp0f_xJJneDC}H zemHl)zJ{n$6+kO5$h?uo%UJl1uUTb8PbRO z4(H&ns0sardT$XAEkqxd;wD^7|E7(KW_S>}*F1-M!7$#2FWdHOsQX4x$vKI-f7G@= zLfwBJbMRBt{TDG0uUN051~iR+`ZqVJRH2`{^@3eki6PX?583t$=%@WQYJeB97(Yi1 z=qKb=W*Rks>!^-zB8SN%O?qyDbrCvB^-?ODNgb-gCR~FpxE39IeH7KfIBEtHr~!Y2 z8t_%r^KLeT+NG!gH=+jAjXsQGH4Yb&e~sug7dXuO){jvmpG3Xj3aY~_-mbO>m9Ze| z`F*Ih??LUQe&jH3a#ChStskQH)H&O}R7CzY^RKv|5&mHP8MA3mqf-1QY5*=yx~~M) zaVau3Q-SKJ7B!H1)Fx{}O{Cr0jpJFy^dW~?t^6;b(ulgyhWf0+NH)#0cqhJy6?g{M z;Fs1srlZeoBWjIXP!sCJAV!hH9OI>&0#7^<+6}6fSOq+YEOhwpWP8u$7hhkoa3aW_yRTX?{PK$fzA>t<>aFlH(UEq19$_K ziMMbarfvI2)Gq(qs?m{MlaG4NhnjJXZP%feuF=|zn(+2A@~=&|mkWAfFDm6p)JUH} z&2SjC>0Ys(LfwA>)!`-Nt1?Z*Y-zC-O&~gZ@n804>g1niGvz_6cC*r>(nf3{G`r1& z%6dYBQ_3GEl?X8?5#k|Y53!0M^O>?;>;K=PnLSF}MYIwd3H>Ki z1{w%$BBk&D%2IoAlU2(WwDr630bAdRI|!$q(_;jC>!0GIrhTC`yO+@3Q0XMPGqsu1 zE?eJc-D|xMpCGif)x?v8mT?17L#S*c*pyD@o8)gpraaC$M1%={=87?oU^}svxS!Cc z Date: Wed, 16 Apr 2014 11:36:58 +0200 Subject: [PATCH 235/340] add ranking to Character page --- configs/AppConfig.inc | 11 ++++ controllers/CharactersController.inc | 27 ++++++++++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 7126 -> 7166 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 13 +++-- models/CharactersModel.inc | 54 +++++++++++++++++++- views/html/characters/character.tpl | 40 +++++++-------- 6 files changed, 120 insertions(+), 25 deletions(-) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 45ccb0fe..adca33d5 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -68,6 +68,17 @@ ); + /** + * Miscellaneous settings + * + * @static + * @var array + */ + public static $misc = array( + 'ranking_range' => 2 + ); + + /** * Validation settings for user input * diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 70a651dd..6299dfe7 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -123,6 +123,32 @@ // Get Achievements $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + // Get ranking + $ranking = array( + 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']), + 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']) + ); + foreach($ranking['superior'] as &$rankCharacter) + { + if(!is_null($rankCharacter['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + foreach($ranking['inferior'] as &$rankCharacter) + { + if(!is_null($rankCharacter['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + // Get Quest topics $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); foreach($questtopics as &$questtopic) @@ -139,6 +165,7 @@ $this->set('user', $user); $this->set('groups', $groups); $this->set('achievements', $achievements); + $this->set('ranking', $ranking); $this->set('questtopics', $questtopics); } diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 1b1ca27ad7273e1c519ddb5637e377ca714944d2..ea6966685377ae2d1fb8addd1978bc04909e406d 100644 GIT binary patch delta 2492 zcmZA3Z%mbC7{~D|I3C3GDE>+LV;ql&oFjNRVxV9unx#c*DVjnlrlOKG%bCvRv)Y)N z>(9oBRV&+cZXvBUX0TSPHCL`)L@#2Q7WATCWBYC4R{kLVdQSJ@fdUGA(t(4SECZ!jErr& za1=g=n%F_q?{?rcJcGH6Z(n$ai>R6YgN$jJH1q=z)M8Bbb^&r(j9VC!sQ#7Su0{1< zi6LBzTvpF*G;VWSa027oekw)ShO@8>^#lDl6MsU@{Fb-JGEMa_Mhq^?s4oUrUvsmk!^2sUg}E5!ZOtU=#_`L$ND}E{f3Xi!Fho{LI1lwa zf%+V4kX^BLScXklh_7Q3KXQXXD*8^x^HFN=i%~PHz$#pfbg=`d&*l~6vQysfLk;u; zlA8^pe&-r0v0>Ev5wg`pW0;R|B;mkTP>Jzi11gb2I2+$U9iBeqvh&=ubU&jK9P;)R z)Xc7-2FT$@H1IUk3YMVyr%+q4$lJB3^JjHb)Ug5ca62Y2aJx{a^+!}e_j(;ZP1F)WIk%+*TV+vVq->?KjL pT3dFvHuszu_f2M2Wi*wluBdF^lrv+b-t$sUL3pIoJ5y^y{{l#q%ToXV delta 2462 zcmXxleN0t#9LMpmm&?PwCW%-)icnyJ)CE*7U{oSyshLGWC<#5Fg&Ehi+H}*~Bx_5{ z(rEnQay@LOn~io$vvOYNbZym|x@vRnZY}Z;-E3^lTD9u^f$#n8e9!B5&OPV+zP~5< zH~K>~o|+LHHcE^rCH7~S=|*WbCrT`2b{%fU8Mqy%;hQ)e4`2a~VlIA#HTWamh=K6r zbrH_vx*ca?40%3oJE?HkTbvs30P4f%Q4Rir`FIVl$MDoii;=@>+(uMlZAja;3NvvH zs$+eq?{33Fd>xBu-wyf%r%*jjA#K?e)CaDi2IC*U4)Tzm3vIi9XOr#t%ph_Zo(?ui~7K4crzwZJsoya5B<8DNCWGm|ZAyk7O z`t@ZkMueYE^;6+q|yWBT0!1XX{3PzB_ z4s+6TU!WRJq7pxgYUlen6=m`h7U3mSPxKWT!sHZ&ANk2HFb_B27%JhvQ1`=Rscl$- z%!bX!TX8YY#zBnW9`___d&YmGqPfpvQ>thA7{xN=C+kFQmjUFkA-^6$HS{s6$0t$W zIfqItg?j%gs;2?6F2N8g;VQgI`@fNjGU>)!Fos$@BgkPNaMI9yj!H1;*I%Q0b`I6R zII7`4Q6rbbNA-LjY6?pIx&k!?wV0#*zlcgPH`=iVd)-%2tMyYGH%u@?4K$e9Kq7vDG8uGpF`=|sy!T4M%C#YxyE}=$X3f+{os1fMG2==1(?H*JD z3FH+!j_Ub&R7X-+i&wA=3;FnM7Tw%tadx1-*N+;}K~$nIlq{X>;Se`CY?pfw^}=aXgWn>%(AtS~ zS?v`yv}s>`C_f(aJ3Ks59(FZm+KejQ#65&2auK1jfY8L(5o-yhSWajWXgKB(gM`Y1 zL^ZLI=p@z<8f%7HYsu{XiSy*?UaxDc9wqJ~?j&v}W)b?ocn6`e*Els2>C)vDHt9rZ zb?0M?*Oy?2*B?;(OYW(Vm6^wb04AAw32w7SWKkLDz7}@u5*`TKcQu< zy||vxyfhP9sVXbff6eM#LaXH|;z{BWVt}Y3RCd%~(ca_$9CDBJLBc3MOh&e>MOikdb->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_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.seminarymedia_id = avatars.avatarpicture_id '. 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. + 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_id '. 'WHERE charactertypes.seminary_id = ? AND characters.url = ?', 'is', $seminaryId, $characterUrl @@ -164,12 +166,14 @@ 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_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.seminarymedia_id = avatars.avatarpicture_id '. 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. + 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_id '. 'WHERE characters.id = ?', 'i', $characterId @@ -258,6 +262,52 @@ } + /** + * Get the superior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of superior Characters + */ + public function getSuperiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps > ? '. + 'ORDER BY characters.xps ASC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * Get the inferior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of inferior Characters + */ + public function getInferiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps < ? '. + 'ORDER BY characters.xps DESC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + /** * Get Characters that solved a Quest. * diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index dedab4c8..74ba7cae 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -62,33 +62,33 @@
                -

                Ranking

                +

                  + &$rankCharacter) : ?>
                • - -

                  7. Anduin

                  -

                  Level 27 (1500 XP)

                  + + + +

                  .

                  +

                  ( XPs)

                • +
                • - -

                  8. Jaina

                  -

                  Level 26 (1400 XP)

                  + + + +

                  .

                  +

                  ( XPs)

                • + &$rankCharacter) : ?>
                • - -

                  9. Uther

                  -

                  Level 25 (1300 XP)

                  -
                • -
                • - -

                  10. Lothar

                  -

                  Level 24 (1200 XP)

                  -
                • -
                • - -

                  11. Morris

                  -

                  Level 23 (1100 XP)

                  + + + +

                  .

                  +

                  ( XPs)

                • +
                From 914f0963a2912e2f95bfb473b65df29a0e52173c Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 16 Apr 2014 11:54:20 +0200 Subject: [PATCH 236/340] handle empty Quests --- controllers/QuestsController.inc | 8 +++++++- views/html/quests/quest.tpl | 10 ++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index e8ed7fca..ee8a3c67 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -252,7 +252,13 @@ $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); // Render task - $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); + if(!is_null($questtype['classname'])) { + $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); + } + else { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } } // Has Character solved quest? diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 26911951..6d7e8c06 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -45,19 +45,21 @@
              - +
              +

              + - +

              :

              + -
              From f3d15cd7f8cbaca75ae4adebb13ce7a0134698a5 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 16 Apr 2014 11:54:42 +0200 Subject: [PATCH 237/340] fix media link in Questtype ?Drag&Drop? --- questtypes/dragndrop/html/quest.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl index 1af4dd6a..5a5d3717 100644 --- a/questtypes/dragndrop/html/quest.tpl +++ b/questtypes/dragndrop/html/quest.tpl @@ -1,7 +1,7 @@
              -
              +
              From 62499176c9500306f2cb47cb7326803a96c6d1f9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 16 Apr 2014 13:42:19 +0200 Subject: [PATCH 238/340] check if unique values already exists for user and Character registration and send notification mail to moderators --- app/Utils.inc | 29 ++++++ configs/AppConfig.inc | 3 +- controllers/CharactersController.inc | 31 ++++++ controllers/UsersController.inc | 37 ++++++- .../components/ValidationComponent.inc | 25 +++++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 7166 -> 7430 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 76 +++++++++------ models/CharactersModel.inc | 25 +++++ models/UsersModel.inc | 92 ++++++++++++++++++ views/html/characters/register.tpl | 2 + views/html/users/register.tpl | 4 + 11 files changed, 290 insertions(+), 34 deletions(-) diff --git a/app/Utils.inc b/app/Utils.inc index d248dee1..a445dcf5 100644 --- a/app/Utils.inc +++ b/app/Utils.inc @@ -78,6 +78,35 @@ return substr($string, 0, $pos); } + + /** + * Send an e‑mail. + * + * @param string $from Sender of mail + * @param mixed $to One (string) or many (array) receivers + * @param string $subject Subject of mail + * @param string $message Message of mail + * @param boolean $html Whether mail should be formatted as HTML or not + * @return Whether mail has been send or not + */ + public static function sendMail($from, $to, $subject, $message, $html=false) + { + // Set receivers + $to = is_array($to) ? implode(',', $to) : $to; + + // Set header + $headers = array(); + $headers[] = 'Content-type: text/'.($html ? 'html' : 'plain').'; charset=UTF-8'; + if(!is_null($from)) { + $headers[] = "From: $from"; + } + $header = implode("\r\n", $headers)."\r\n"; + + + // Send mail + return mail($to, $subject, $message, $header); + } + } ?> diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index adca33d5..8128f4d2 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -32,7 +32,8 @@ public static $app = array( 'name' => 'The Legend of Z', 'namespace' => 'hhu\\z\\', - 'timeZone' => 'Europe/Berlin' + 'timeZone' => 'Europe/Berlin', + 'mailsender' => 'noreply@zyren.inf-d.de' ); diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 6299dfe7..8f7e02cf 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -199,6 +199,9 @@ // Validate Character properties $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); $charactername = $this->request->getPostParam('charactername'); + if($this->Characters->characterNameExists($charactername)) { + $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true); + } // Validate type $typeIndex = null; @@ -245,6 +248,9 @@ } } + // Send mail + $this->sendRegistrationMail($charactername); + // Redirect $this->redirect($this->linker->link(array('seminaries'))); } @@ -260,6 +266,31 @@ $this->set('fieldsValidation', $fieldsValidation); } + + + + /** + * Send mail for new Character registration. + * + * @param string $charactername Name of newly registered Character + */ + private function sendRegistrationMail($charactername) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new Character registration: %s', $charactername); + $message = sprintf('User “%s” <%s> has registered a new Character “%s” for the Seminary “%s”', self::$user['username'], self::$user['email'], $charactername, self::$seminary['title']); + $moderators = $this->Users->getUsersWithSeminaryRole(self::$seminary['id'], 'moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + } ?> diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 535dcbbe..90e50912 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -164,9 +164,16 @@ // Get params and validate them $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); $username = $this->request->getPostParam('username'); + if($this->Users->usernameExists($username)) { + $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true); + } $prename = $this->request->getPostParam('prename'); $surname = $this->request->getPostParam('surname'); $email = $this->request->getPostParam('email'); + if($this->Users->emailExists($email)) { + $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true); + } + // Register if($validation === true) @@ -178,7 +185,10 @@ $email, $this->request->getPostParam('password') ); - + + // Send mail + $this->sendRegistrationMail($username, $email); + // Login $this->Auth->setUserId($userId); $user = $this->Users->getUserById($userId); @@ -323,6 +333,31 @@ } + + + /** + * Send mail for new user registration. + * + * @param string $username Name of newly registered user + * @param string $email E‑mail address of newly registered user + */ + private function sendRegistrationMail($username, $email) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new user registration: %s', $username); + $message = sprintf('User “%s” <%s> has registered themself to %s', $username, $email, \nre\configs\AppConfig::$app['name']); + $moderators = $this->Users->getUsersWithRole('moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + } ?> diff --git a/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc index dbe5b0b3..950d45ed 100644 --- a/controllers/components/ValidationComponent.inc +++ b/controllers/components/ValidationComponent.inc @@ -110,6 +110,31 @@ return $validation; } + + /** + * Add a custom determined validation result to a validation + * store. + * + * @param mixed $validation Validation store to add result to + * @param string $param Name of parameter of the custom validation result + * @param string $setting Name of setting of the custom validation result + * @param mixed $result Validation result + * @return mixed The altered validation store + */ + public function addValidationResult($validation, $param, $setting, $result) + { + if(!is_array($validation)) { + $validation = array(); + } + if(!array_key_exists($param, $validation)) { + $validation[$param] = array(); + } + $validation[$param][$setting] = $result; + + + return $validation; + } + } ?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index ea6966685377ae2d1fb8addd1978bc04909e406d..3525bcf513b478dda590a7bd9d9c7ee48b96a9c2 100644 GIT binary patch delta 2679 zcmZYAeN5F=9LMqRg=<2{bC?Jw&jEr0W(br?ML|BKDP%<;#6>WyycbtE*JWv~hd)ej zRx_Q$xu$>guw8SlToKEp{$Ni@LQRi_~f`dqI zHi9$oN7MqxP|y7b=QF>}VtI48P=e`Lhq|#9`PdG=l-Mh%2fcwR&VDyOjC|}UUpL@K zsQXX4@oCikBk0Am$j8p}6~p{?K^+`NW%f7bVI0fI$1>CdnlKkzP!)K=jrXGNKY>c{ zJSu@-Fc~kQDlvgdFotrocuT~f{vm^oGRsFzRE$cf0?EZ{kl$K8YJw(bGiqzvQ4?-M zCDxCN@O3Q1L3e!|HL-`AWPBp^*NW4*5SbXY!V2VLO>W$YTH&jxL_feRJdOqUJu0z( zkvS}ihstEsLb6bYvru|F^cgz+zYD>yd z_btb{*nq{@;oOZn#GjxN8$xZ_A6SH5$|Dz|65N1s%x^7pv{#!^d%Yc%;V$QEsEPNY ze)tY*q5)JXkD*F@0#*7iQNKUu{25uJ{f_$m1ZtsPvI~~eNu*PawW#-b2P&c6s4Y0; z{1BDM7g&hjpek_%Rfz-!vI>=82kI>KpbpysR04xY4))bt>aV@~i3?iEMbuvZg(aB5 zLAV9$aUpJUzKKfY6e`gX)N4BC#>xEsRwc8Y3s7fc3F>z%P!(98N&R)9l?&SQF6S21 z%KfNQyA$=meW=nNLM8kzYNcUhZu`{v1M2=OsOMip-cq}ZSV7!F+(DFS|2Gp;Z5^Fn zBATcnx`-{rZNvs*s%@pCim2(Z>AzgH+lhAKAq{B$Nh1H{x~@G*r;vDtNY^UWl>PmL z4vJb!s5~|`xSHNZqK60&YL60+5tW3BuXke^p_B6n(L@vxZG>u7PpIjL))A`LO5$!p zO?6sDG!i;JYEMiOjBJYf+C7~N4o2iH2D_KgCbSY&L^h$#iXzq!1%ysd1EHo9qW07z zk(ZNq(|p8zL_X0;Xu~!Uk-wPT&=DD!J%EderIFe!r}s3W6P`!tL_bcb)e^acPWx1Q zkj{EH$U+_bQa2VQ#9HD`4QLNfV&%y3u_c85Q0m}DsODSmdvoZ;*uI3e9>1@xqc6+% zTz8;15WXDywI_bsweXR+7;nn7O95*P_(Ja|3$g7{>8;+M&`?hHe5~uyrV;Xo8qPNGvsyOrQ;sU<@&+G1L$U6|~g^KF9|`qPc*Lr{E_&bcUohTv>rJ8kO#Y3DZd(+J_aUbU3XE++a#w_f?ad-v;IEZ1qj$^SP z<6b+8Q)oA19_~f%_iR5E4(sML4SP^8{s-0J9n8hRD6=sbbWTGKo9kSHN^A|%w{67{ z_y%fVJ5lfL!0~t(^XcDCxf}XXBfW+6X{j{yf&gkV@?5(JIV{L24I`-gYh1e?b^ikN z<1*y1MouGfgR>R0=-=L=Qi|<33A<1)=);M45jFC=uARv+b$=Bq!A4X9O_+@>s2SLa zO7MMTOt#Nm??5GX06ledh>9}nM*6U0$YDQlQU`s`3#cWzjOy?@DzV!b!3-9(3S+42 zt*DN7IrpHJAdbm?1I)ih*v$n!(1&UdqDJWFqB0F(2~Nc_T#8C;2Xd>uYk!WKfv->r9&sMU6xzp7 zOVNuQ_A@8lcL~+;AS&^z9u;*ogi7Qt=3^R9Ya}Jk5Pt6?N#qv$mvwj;{bZ$t3sBd? zsL!zuSruE3mAD>@@e_>Tw@z<>ioVk^e3V-IGStYbu@+|`x7ZHUXY&Da*g@CsMRoKW zlA8^n-g5($*f8q(0NHAwK`g`&lCWp@+7$-S4Ob2VMIb zYGgN19c1$obvzz5gXO6Eqo^gA;o9}6{bvnSbYl}1;3f>C=j=l5*7K;u{y;6!2%_faA9uPN>1f-?OQHNqpvVF~A1)C1R0 z9S$L1W3nuCiuuG6LbLD!@nCs|hBoRpf^pg7#B{B{N-Z&ic!H=So+9oSb-bE*)Lqzu z)kG<=o_LbbAhZMvh~>lvqK06LBugvjnXcY|8;N?ttKu|=V5zKxxL?*$nM-JzUnOP{ z<-`=?!J_5T%xPM+7qnru_A80aL<+H(P+`*SIYOIJ`-`AD2^_tR!aVobx4W^8WTE>c*b+Cn^j3eg6SN Cvd@qJ diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index d4860fa3..86648bc7 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-16 11:36+0100\n" -"PO-Revision-Date: 2014-04-16 11:36+0100\n" +"POT-Creation-Date: 2014-04-16 13:19+0100\n" +"PO-Revision-Date: 2014-04-16 13:19+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -79,12 +79,12 @@ msgid "submitted at %s on %s h" msgstr "eingereicht am %s um %s Uhr" #: questtypes/submit/html/submission.tpl:6 -#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:40 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:46 #: views/html/quests/submissions.tpl:33 msgid "solved" msgstr "gelöst" -#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:42 +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:48 #: views/html/quests/submissions.tpl:24 msgid "unsolved" msgstr "ungelöst" @@ -202,31 +202,35 @@ msgid "Character name contains illegal characters" msgstr "Der Charaktername enthält ungültige Zeichen" #: views/html/characters/register.tpl:26 +msgid "Character name already exists" +msgstr "Der Charaktername existiert bereits" + +#: views/html/characters/register.tpl:28 msgid "Character name invalid" msgstr "Der Charaktername ist ungültig" -#: views/html/characters/register.tpl:38 +#: views/html/characters/register.tpl:40 msgid "Character properties" msgstr "Charaktereigenschaften" -#: views/html/characters/register.tpl:39 views/html/characters/register.tpl:40 +#: views/html/characters/register.tpl:41 views/html/characters/register.tpl:42 msgid "Character name" msgstr "Charaktername" -#: views/html/characters/register.tpl:41 +#: views/html/characters/register.tpl:43 msgid "Character type" msgstr "Charaktertyp" -#: views/html/characters/register.tpl:52 +#: views/html/characters/register.tpl:54 #, php-format msgid "The Seminary field “%s” is invalid" msgstr "Das Kursfeld „%s“ ist ungültig" -#: views/html/characters/register.tpl:57 +#: views/html/characters/register.tpl:59 msgid "Seminary fields" msgstr "Kursfelder" -#: views/html/characters/register.tpl:79 views/html/seminaries/create.tpl:14 +#: views/html/characters/register.tpl:81 views/html/seminaries/create.tpl:14 #: views/html/users/create.tpl:17 msgid "create" msgstr "erstellen" @@ -245,7 +249,7 @@ msgstr "Login" #: 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:9 views/html/users/login.tpl:10 -#: views/html/users/register.tpl:74 views/html/users/register.tpl:75 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 msgid "Username" msgstr "Benutzername" @@ -253,7 +257,7 @@ msgstr "Benutzername" #: views/html/users/create.tpl:14 views/html/users/create.tpl:15 #: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 #: views/html/users/login.tpl:11 views/html/users/login.tpl:12 -#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 +#: views/html/users/register.tpl:86 views/html/users/register.tpl:87 msgid "Password" msgstr "Passwort" @@ -306,19 +310,19 @@ msgstr "Filter anwenden" msgid "Reset filters" msgstr "Filter zurücksetzen" -#: views/html/quests/quest.tpl:50 +#: views/html/quests/quest.tpl:57 msgid "Task" msgstr "Aufgabe" -#: views/html/quests/quest.tpl:55 +#: views/html/quests/quest.tpl:63 msgid "Task already successfully solved" msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" -#: views/html/quests/quest.tpl:57 +#: views/html/quests/quest.tpl:66 msgid "Show answer" msgstr "Lösung anzeigen" -#: views/html/quests/quest.tpl:71 views/html/quests/quest.tpl:80 +#: views/html/quests/quest.tpl:79 views/html/quests/quest.tpl:88 msgid "Quest" msgstr "Quest" @@ -406,19 +410,19 @@ msgstr "Neuer Benutzer" #: views/html/users/create.tpl:8 views/html/users/create.tpl:9 #: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 -#: views/html/users/register.tpl:76 views/html/users/register.tpl:77 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 msgid "Prename" msgstr "Vorname" #: 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/register.tpl:78 views/html/users/register.tpl:79 +#: views/html/users/register.tpl:82 views/html/users/register.tpl:83 msgid "Surname" msgstr "Nachname" #: views/html/users/create.tpl:12 views/html/users/create.tpl:13 #: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 -#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 #: views/html/users/user.tpl:12 msgid "E‑mail address" msgstr "E‑Mail-Adresse" @@ -468,66 +472,74 @@ msgid "Username contains illegal characters" msgstr "Der Benutzername enthält ungültige Zeichen" #: views/html/users/register.tpl:20 +msgid "Username already exists" +msgstr "Der Benutzername existiert bereits" + +#: views/html/users/register.tpl:22 msgid "Username invalid" msgstr "Der Benutzername ist ungültig" -#: views/html/users/register.tpl:25 +#: views/html/users/register.tpl:27 #, php-format msgid "Prename is too short (min. %d chars)" msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:27 +#: views/html/users/register.tpl:29 #, php-format msgid "Prename is too long (max. %d chars)" msgstr "Der Vorname ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:29 +#: views/html/users/register.tpl:31 #, php-format msgid "Prename contains illegal characters" msgstr "Der Vorname enthält ungültige Zeichen" -#: views/html/users/register.tpl:31 +#: views/html/users/register.tpl:33 msgid "Prename invalid" msgstr "Der Vorname ist ungültig" -#: views/html/users/register.tpl:36 +#: views/html/users/register.tpl:38 #, php-format msgid "Surname is too short (min. %d chars)" msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:38 +#: views/html/users/register.tpl:40 #, php-format msgid "Surname is too long (max. %d chars)" msgstr "Der Nachname ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:40 +#: views/html/users/register.tpl:42 #, php-format msgid "Surname contains illegal characters" msgstr "Der Nachname enthält ungültige Zeichen" -#: views/html/users/register.tpl:42 +#: views/html/users/register.tpl:44 msgid "Surname invalid" msgstr "Der Nachname ist ungültig" -#: views/html/users/register.tpl:47 views/html/users/register.tpl:49 +#: views/html/users/register.tpl:49 views/html/users/register.tpl:53 msgid "E‑mail address invalid" msgstr "Die E‑Mail-Adresse ist ungültig" -#: views/html/users/register.tpl:54 +#: views/html/users/register.tpl:51 +msgid "E‑mail address already exists" +msgstr "E‑Mail-Adresse existiert bereits" + +#: views/html/users/register.tpl:58 #, php-format msgid "Password is too short (min. %d chars)" msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" -#: views/html/users/register.tpl:56 +#: views/html/users/register.tpl:60 #, php-format msgid "Password is too long (max. %d chars)" msgstr "Das Passwort ist zu lang (max. %d Zeichen)" -#: views/html/users/register.tpl:58 +#: views/html/users/register.tpl:62 msgid "Password invalid" msgstr "Das Passwort ist ungültig" -#: views/html/users/register.tpl:85 +#: views/html/users/register.tpl:89 msgid "Register" msgstr "Registrieren" diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index a409ef0d..7b27da8d 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -390,6 +390,31 @@ } + /** + * Check if a Character name already exists. + * + * @param string $name Character name to check + * @return boolean Whether Character name exists or not + */ + public function characterNameExists($name) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM characters '. + 'WHERE name = ? OR url = ?', + 'ss', + $name, + \nre\core\Linker::createLinkParam($name) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + /** * Create a new Character. * diff --git a/models/UsersModel.inc b/models/UsersModel.inc index 88a7ed2d..c773a0b5 100644 --- a/models/UsersModel.inc +++ b/models/UsersModel.inc @@ -49,6 +49,49 @@ } + /** + * Get users with the given user role. + * + * @param string $userrole User role + * @return array List of users + */ + public function getUsersWithRole($userrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + 'FROM users '. + 'LEFT JOIN users_userroles ON users_userroles.user_id = users.id '. + 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '. + 'WHERE userroles.name = ? '. + 'ORDER BY username ASC', + 's', + $userrole + ); + } + + + /** + * Get users with the given user Seminary role. + * + * @param int $seminaryId ID of Seminary + * @param string $userseminaryrole User Seminary role + * @return array List of users + */ + public function getUsersWithSeminaryRole($seminaryId, $userseminaryrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + '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_userseminaryroles.seminary_id = ? AND userseminaryroles.name = ? '. + 'ORDER BY username ASC', + 'is', + $seminaryId, $userseminaryrole + ); + } + + /** * Get a user and its data by its ID. * @@ -124,6 +167,55 @@ } + /** + * Check if an username already exists. + * + * @param string $username Username to check + * @return boolean Whether username exists or not + */ + public function usernameExists($username) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE username = ? OR url = ?', + 'ss', + $username, + \nre\core\Linker::createLinkParam($username) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Check if an e‑mail address already exists. + * + * @param string $email E‑mail address to check + * @return boolean Whether e‑mail address exists or not + */ + public function emailExists($email) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE email = ?', + 's', + $email + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + /** * Create a new user. * diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl index 15317277..d62c0d35 100644 --- a/views/html/characters/register.tpl +++ b/views/html/characters/register.tpl @@ -23,6 +23,8 @@ break; case 'regex': echo _('Character name contains illegal characters'); break; + case 'exist': echo _('Character name already exists'); + break; default: echo _('Character name invalid'); } break; diff --git a/views/html/users/register.tpl b/views/html/users/register.tpl index 184e0a0c..20cbfc63 100644 --- a/views/html/users/register.tpl +++ b/views/html/users/register.tpl @@ -17,6 +17,8 @@ break; case 'regex': echo _('Username contains illegal characters'); break; + case 'exist': echo _('Username already exists'); + break; default: echo _('Username invalid'); } break; @@ -46,6 +48,8 @@ switch($setting) { case 'regex': echo _('E‑mail address invalid'); break; + case 'exist': echo _('E‑mail address already exists'); + break; default: echo _('E‑mail address invalid'); } break; From 1d7cfc6ceb63fbe8cc39f7e8002f7368863bca06 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 16 Apr 2014 15:28:09 +0200 Subject: [PATCH 239/340] boss fight design --- questtypes/bossfight/html/quest.tpl | 64 +++++++++---------- questtypes/bossfight/html/submission.tpl | 79 +++++++++++------------- www/css/desktop.css | 10 +++ 3 files changed, 75 insertions(+), 78 deletions(-) diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl index ba427577..d28a153e 100644 --- a/questtypes/bossfight/html/quest.tpl +++ b/questtypes/bossfight/html/quest.tpl @@ -1,39 +1,31 @@ - - - - - - - - - - - -
              - - - - - -
              - : - 0) : ?> - - ♥ - - - - - - : - 0) : ?> - - ♥ - - - - -
              +
              +
              +

              +

              +

              + 0) : ?> + + + + + + +

              +
              +
              +

              +

              +

              + 0) : ?> + + + + + + +

              +
              +

              diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl index 33cf172f..6861e0ea 100644 --- a/questtypes/bossfight/html/submission.tpl +++ b/questtypes/bossfight/html/submission.tpl @@ -1,43 +1,38 @@ - - - - - - - - - - - - - - +
              +
              +

              +
              +
              +

              +
              +
              + + +

              +
              +
              +

              +

              + 0) : ?> + + -

              -
              - - - - - -
              - -
              - : - 0) : ?> - - ♥ - - - - - - : - 0) : ?> - - ♥ - - - - -
              + + + +

              +
            +
            +

            +

            + 0) : ?> + + + + + + +

            +
            + + diff --git a/www/css/desktop.css b/www/css/desktop.css index b9dabe4a..ed0f25b3 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -182,6 +182,14 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .crossword li{margin-top:20px} .crossword .index{position:absolute;font-size:0.625em;margin:-2px 0 0 2px} +.opponent{width:50%;float:left} +.opponent .portrait{height:250px;overflow:hidden;margin-bottom:15px} +.opponent .hero{background:#fff;max-width:130px} +.opponent .boss{max-width:100%} +.opponent p{text-align:center} +.opponent .fa{font-size:1.25em;color:#c7135b;padding-right:0} + + /** Media Queries **/ @media only screen and (min-width:480px){ @@ -198,6 +206,8 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gdata ul{clear:both} .gchars li{width:32%;margin-right:5px} .gchars li:nth-child(even){float:left} + +.opponent .hero{max-width:200px} } @media only screen and (min-width:768px){ From aef2c451939465b242a0dc6d5eb53ced10483b80 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 16 Apr 2014 15:42:21 +0200 Subject: [PATCH 240/340] show Quest media for first Qusettext without own media --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 +++++++++++ agents/bottomlevel/MenuAgent.inc | 37 + .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarybarAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/AchievementsAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/LibraryAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 + agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 70 ++ apis/WebApi.inc | 250 +++++++ app/Controller.inc | 103 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 267 +++++++ app/QuesttypeController.inc | 349 +++++++++ app/QuesttypeModel.inc | 154 ++++ app/QuesttypeView.inc | 76 ++ app/ToplevelAgent.inc | 36 + app/Utils.inc | 112 +++ app/controllers/IntermediateController.inc | 139 ++++ app/controllers/SeminaryRoleController.inc | 277 ++++++++ app/exceptions/FileUploadException.inc | 75 ++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 ++ .../QuesttypeAgentNotValidException.inc | 77 ++ .../QuesttypeControllerNotFoundException.inc | 77 ++ .../QuesttypeControllerNotValidException.inc | 77 ++ .../QuesttypeModelNotFoundException.inc | 77 ++ .../QuesttypeModelNotValidException.inc | 77 ++ .../SubmissionNotValidException.inc | 77 ++ app/exceptions/WrongFiletypeException.inc | 75 ++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 180 +++++ configs/CoreConfig.inc | 167 +++++ controllers/AchievementsController.inc | 162 +++++ controllers/BinaryController.inc | 37 + controllers/CharactergroupsController.inc | 150 ++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 296 ++++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 + controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 + controllers/LibraryController.inc | 129 ++++ controllers/MediaController.inc | 368 ++++++++++ controllers/MenuController.inc | 51 ++ controllers/QuestgroupsController.inc | 179 +++++ .../QuestgroupshierarchypathController.inc | 90 +++ controllers/QuestsController.inc | 665 ++++++++++++++++++ controllers/SeminariesController.inc | 252 +++++++ controllers/SeminarybarController.inc | 96 +++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 +++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 363 ++++++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ .../components/ValidationComponent.inc | 140 ++++ core/Agent.inc | 607 ++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 433 ++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 ++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 ++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 ++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 ++ exceptions/ClassNotValidException.inc | 77 ++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 ++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 ++ exceptions/ServiceUnavailableException.inc | 77 ++ exceptions/ViewNotFoundException.inc | 77 ++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 7430 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 601 ++++++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 508 +++++++++++++ models/AvatarsModel.inc | 62 ++ models/CharactergroupsModel.inc | 197 ++++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 471 +++++++++++++ models/CharactertypesModel.inc | 57 ++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 139 ++++ models/QuestgroupsModel.inc | 589 ++++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 ++++ models/QuestsModel.inc | 410 +++++++++++ models/QuesttextsModel.inc | 114 +++ models/QuesttopicsModel.inc | 154 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 193 +++++ models/SeminarycharacterfieldsModel.inc | 78 ++ models/UploadsModel.inc | 148 ++++ models/UserrolesModel.inc | 77 ++ models/UsersModel.inc | 370 ++++++++++ models/UserseminaryrolesModel.inc | 78 ++ .../bossfight/BossfightQuesttypeAgent.inc | 24 + .../BossfightQuesttypeController.inc | 244 +++++++ .../bossfight/BossfightQuesttypeModel.inc | 183 +++++ questtypes/bossfight/html/quest.tpl | 58 ++ questtypes/bossfight/html/submission.tpl | 43 ++ .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 + .../ChoiceinputQuesttypeController.inc | 157 +++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 ++++ questtypes/choiceinput/html/quest.tpl | 15 + questtypes/choiceinput/html/submission.tpl | 12 + .../crossword/CrosswordQuesttypeAgent.inc | 24 + .../CrosswordQuesttypeController.inc | 351 +++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 +++ questtypes/crossword/html/quest.tpl | 49 ++ questtypes/crossword/html/submission.tpl | 48 ++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 199 ++++++ .../dragndrop/DragndropQuesttypeModel.inc | 146 ++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 261 +++++++ .../MultiplechoiceQuesttypeModel.inc | 159 +++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 +++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 +++++ .../textinput/TextinputQuesttypeModel.inc | 117 +++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 +++++++++++ responses/WebResponse.inc | 250 +++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/achievement.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/achievements/index.tpl | 33 + views/html/charactergroups/group.tpl | 52 ++ views/html/charactergroups/groupsgroup.tpl | 22 + views/html/charactergroups/index.tpl | 13 + views/html/charactergroupsquests/quest.tpl | 51 ++ views/html/characters/character.tpl | 109 +++ views/html/characters/index.tpl | 19 + views/html/characters/register.tpl | 82 +++ views/html/error/index.tpl | 2 + views/html/html.tpl | 58 ++ views/html/introduction/index.tpl | 36 + views/html/library/index.tpl | 21 + views/html/library/topic.tpl | 28 + views/html/menu/index.tpl | 9 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/index.tpl | 49 ++ views/html/quests/quest.tpl | 104 +++ views/html/quests/submission.tpl | 17 + views/html/quests/submissions.tpl | 41 ++ views/html/seminaries/create.tpl | 15 + views/html/seminaries/delete.tpl | 13 + views/html/seminaries/edit.tpl | 15 + views/html/seminaries/index.tpl | 33 + views/html/seminaries/seminary.tpl | 42 ++ views/html/seminarybar/index.tpl | 50 ++ views/html/seminarymenu/index.tpl | 6 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 90 +++ views/html/users/user.tpl | 35 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 247 +++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 +++ 239 files changed, 23023 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/LibraryAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/LibraryController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/AvatarsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/CharactertypesModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttopicsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeAgent.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeController.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeModel.inc create mode 100644 questtypes/bossfight/html/quest.tpl create mode 100644 questtypes/bossfight/html/submission.tpl create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/achievements/index.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/characters/register.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/library/index.tpl create mode 100644 views/html/library/topic.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarybar/index.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..43dff51a --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* 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..49512791 --- /dev/null +++ b/agents/bottomlevel/MenuAgent.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\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) + { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); + } + + } + +?> 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/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\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/LibraryAgent.inc b/agents/intermediate/LibraryAgent.inc new file mode 100644 index 00000000..aedc890a --- /dev/null +++ b/agents/intermediate/LibraryAgent.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 Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryAgent 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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..049ba904 --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,70 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); + } + + + + + 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..5731aeed --- /dev/null +++ b/app/Controller.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; + + + /** + * 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 and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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..a445dcf5 --- /dev/null +++ b/app/Utils.inc @@ -0,0 +1,112 @@ + + * @copyright 2014 Heinrich-Heine-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); + } + + + /** + * Send an e‑mail. + * + * @param string $from Sender of mail + * @param mixed $to One (string) or many (array) receivers + * @param string $subject Subject of mail + * @param string $message Message of mail + * @param boolean $html Whether mail should be formatted as HTML or not + * @return Whether mail has been send or not + */ + public static function sendMail($from, $to, $subject, $message, $html=false) + { + // Set receivers + $to = is_array($to) ? implode(',', $to) : $to; + + // Set header + $headers = array(); + $headers[] = 'Content-type: text/'.($html ? 'html' : 'plain').'; charset=UTF-8'; + if(!is_null($from)) { + $headers[] = "From: $from"; + } + $header = implode("\r\n", $headers)."\r\n"; + + + // Send mail + return mail($to, $subject, $message, $header); + } + + } + +?> diff --git a/app/controllers/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..19647d7e --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + self::$user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById(self::$user['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + } + + + /** + * 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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..cb7f6b86 --- /dev/null +++ b/app/controllers/SeminaryRoleController.inc @@ -0,0 +1,277 @@ + + * @copyright 2014 Heinrich-Heine-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\controllers\IntermediateController + { + /** + * Required components + * + * @var array + */ + public $components = array('achievement', 'auth'); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements'); + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = 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); + + // Get Seminary and Character data + try { + self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Check achievements + $this->checkAchievements($request, $response); + + // Set Seminary and Character data + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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) + { + // Do not check index page + if(is_null($request->getParam(3))) { + return; + } + + + // 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(self::$user['seminaryroles'], $permissions)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Check if Character is present + if(is_null(self::$character)) { + return; + } + + // Get unachieved Achievments + $achievements = $this->Achievements->getUnachhievedAchievementsForCharacter(self::$seminary['id'], self::$character['id']); + if(in_array('user', self::$user['seminaryroles'])) { + $achievements = array_merge($achievements, $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary(self::$seminary['id'])); + } + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + self::$character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + self::$character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + self::$character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], self::$character['id']); + } + } + } + + } + +?> diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..8128f4d2 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,180 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace 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', + 'mailsender' => 'noreply@zyren.inf-d.de' + ); + + + /** + * 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Miscellaneous settings + * + * @static + * @var array + */ + public static $misc = array( + 'ranking_range' => 2 + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ) + ); + + + /** + * 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|register|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|register))', '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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..6f363012 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,162 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Achievements + $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); + foreach($achievements as &$achievement) + { + // Get status for Character + $achieved = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + + // Get Character progress + if(!$achieved && $achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + + // Get media + $achievement['media_index'] = 'unachieved_achievementsmedia_id'; + if($achieved) { + $achievement['media_index'] = 'achieved_achievementsmedia_id'; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('achievements', $achievements); + } + + } + +?> diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc new file mode 100644 index 00000000..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..d4cf6cee --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,150 @@ + + * @copyright 2014 Heinrich-Heine-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', 'avatars', '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: 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); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); + + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + // Get Character groups Quests + $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc new file mode 100644 index 00000000..fb9d18c8 --- /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->getSeminaryMediaById($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..8f7e02cf --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,296 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media', 'questtopics'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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']); + + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get ranking + $ranking = array( + 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']), + 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']) + ); + foreach($ranking['superior'] as &$rankCharacter) + { + if(!is_null($rankCharacter['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + foreach($ranking['inferior'] as &$rankCharacter) + { + if(!is_null($rankCharacter['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('characterfields', $characterfields); + $this->set('user', $user); + $this->set('groups', $groups); + $this->set('achievements', $achievements); + $this->set('ranking', $ranking); + $this->set('questtopics', $questtopics); + } + + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + if($this->Characters->characterNameExists($charactername)) { + $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true); + } + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Send mail + $this->sendRegistrationMail($charactername); + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + + + + + /** + * Send mail for new Character registration. + * + * @param string $charactername Name of newly registered Character + */ + private function sendRegistrationMail($charactername) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new Character registration: %s', $charactername); + $message = sprintf('User “%s” <%s> has registered a new Character “%s” for the Seminary “%s”', self::$user['username'], self::$user['email'], $charactername, self::$seminary['title']); + $moderators = $this->Users->getUsersWithSeminaryRole(self::$seminary['id'], 'moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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..dd4bc86c --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + $this->set('loggedCharacter', SeminaryRoleController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc new file mode 100644 index 00000000..a37618ae --- /dev/null +++ b/controllers/LibraryController.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\controllers; + + + /** + * Controller of the LibraryAgent to list Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('questtopics', 'seminaries', 'quests', 'questgroups'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Questtopics of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + // Get Quest count + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + + // Get Character progress + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopics', $questtopics); + } + + + /** + * Action: topic. + * + * Show a Questtopic and its Quests with Questsubtopics. + * + * @throws IdNotFoundException + * @param string $eminaryUrl URL-Title of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + */ + public function topic($seminaryUrl, $questtopicUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Questtopic + $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl); + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForQuesttopic($questtopic['id']) as $quest) + { + if($this->Quests->hasCharacterEnteredQuest($quest['id'], $character['id'])) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + + // Get Subtopics + $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']); + + $quests[] = $quest; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopic', $questtopic); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..16e01cbb --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,368 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'achievements', '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. + * + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + + + /** + * 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; + } + + + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + 480 + ); + } + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } + + + /** + * Get supported image types. + * + * @return array List of supported image types + */ + private static function getImageTypes() + { + $im = new \Imagick(); + + + return $im->queryFormats(); + } + + + /** + * Resize an image. + * + * @param string $fileName Absolute pathname of image to resize + * @param string $mimeType Mimetype of target image + * @param int $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..c254a617 --- /dev/null +++ b/controllers/MenuController.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..d7b8acf1 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,179 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && $currentQuest['solved']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..ee8a3c67 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,665 @@ + + * @copyright 2014 Heinrich-Heine-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( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator', 'user'), + 'submission' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator') + ); + + + + + /** + * Action: index. + * + * List all Quests for a Seminary. + * + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Prepare filters + $filters = array( + 'questgroups' => array(), + 'questtypes' => array() + ); + + // Get selected filters + $selectedFilters = array( + 'questgroup' => "0", + 'questtype' => "" + ); + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('filters'))) { + $selectedFilters = $this->request->getPostParam('filters'); + } + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForSeminary($seminary['id']) as $quest) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + if($selectedFilters['questgroup'] != "0" && $selectedFilters['questgroup'] != $quest['questgroup']['id']) { + continue; + } + + // Get Questtype + $quest['questtype'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + if($selectedFilters['questtype'] != "" && $selectedFilters['questtype'] != $quest['questtype']['classname']) { + continue; + } + + // Add filter values + $filters['questgroups'][$quest['questgroup']['id']] = $quest['questgroup']; + $filters['questtypes'][$quest['questtype']['classname']] = $quest['questtype']; + + // Add open submissions count + $quest['opensubmissionscount'] = count($this->Characters->getCharactersSubmittedQuest($quest['id'])); + + $quests[] = $quest; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('quests', $quests); + $this->set('filters', $filters); + $this->set('selectedFilters', $selectedFilters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + //$questtexttypesUrls = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) + { + switch($questStatus) + { + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; + } + } + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Task + $task = null; + if($questtexttypeUrl == 'Prolog') + { + // Questtype + $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + + // Render task + if(!is_null($questtype['classname'])) { + $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); + } + else { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + } + + // Has Character solved quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Next Quest/Questgroup + $nextQuests = null; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('queststatustext', $questStatusText); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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 + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..771c9787 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,252 @@ + + * @copyright 2014 Heinrich-Heine-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', 'userseminaryroles', '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) + { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($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 + $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/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..5ab1567b --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,96 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('characters', 'quests', 'questgroups', 'achievements', 'charactergroups', 'avatars', 'media'); + + + + + /** + * Action: index. + */ + public function index() + { + if(is_null(SeminaryRoleController::$seminary)) { + return; + } + + // Get Seminary + $seminary = SeminaryRoleController::$seminary; + + // Get Character + $character = SeminaryRoleController::$character; + if(is_null($character)) { + return; + } + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + + // Get Character group members + $characterGroups = array(); + foreach($this->Charactergroups->getGroupsForCharacter($character['id']) as $group) + { + $groupsgroup = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']); + if($groupsgroup['preferred']) + { + $group['members'] = $this->Characters->getCharactersForGroup($group['id']); + foreach($group['members'] as &$member) + { + if(!is_null($member['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($member['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $member['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + $characterGroups[] = $group; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); + $this->set('characterGroups', $characterGroups); + } + + } + +?> diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..5b5d780f --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..90e50912 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,363 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + + // Level + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + if($this->Users->usernameExists($username)) { + $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true); + } + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + if($this->Users->emailExists($email)) { + $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true); + } + + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Send mail + $this->sendRegistrationMail($username, $email); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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); + } + + + + + /** + * Send mail for new user registration. + * + * @param string $username Name of newly registered user + * @param string $email E‑mail address of newly registered user + */ + private function sendRegistrationMail($username, $email) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new user registration: %s', $username); + $message = sprintf('User “%s” <%s> has registered themself to %s', $username, $email, \nre\configs\AppConfig::$app['name']); + $moderators = $this->Users->getUsersWithRole('moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..950d45ed --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,140 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $settings) + { + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Add a custom determined validation result to a validation + * store. + * + * @param mixed $validation Validation store to add result to + * @param string $param Name of parameter of the custom validation result + * @param string $setting Name of setting of the custom validation result + * @param mixed $result Validation result + * @return mixed The altered validation store + */ + public function addValidationResult($validation, $param, $setting, $result) + { + if(!is_array($validation)) { + $validation = array(); + } + if(!array_key_exists($param, $validation)) { + $validation[$param] = array(); + } + $validation[$param][$setting] = $result; + + + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
            \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..3525bcf513b478dda590a7bd9d9c7ee48b96a9c2 GIT binary patch literal 7430 zcmai%ZHygN8OIM$1j{=Lf`Fb{7F+0TcT1tLwZOt|OJ!S@c3WP=*4cZ{-aG8<%w=A7 z*$pv`^1+u62#6Xi5(qKy2@{YQZM0Gm;s*jIqJ*SsVPL+^6}|?~hDYFO@Ev$2`~ZFe zp1ItZ)8Q(35!?tbg}dRoaDUnUF1(EP3-IIcA5h<&aEdWJnzP}0co9_pBT(ad@MAEA zXTYzO{2t`bJXP{JDEkgUOf#>*6X74A=63|D-~Yl-!&8~ud2kIp9}Yq3?SuT8d%4N3 zuS50oZ7A0~QnsIf{F$e?eFXj(O8;QlehEtd6}Sw(4*4^0ayuEmRq`DuyZ!?&hNm!z z9@q=@{T{dy?uDBFSIYK-Q2Nh9+4m;Y_AmJg>-FB#F_HnIVCTIipP~@dn1$|u7R?1Tge^pc-p%le`XIix-<zk@+ElaIPw#!zXvep9QXv3y+46^|1#A4-+)@j zccA?I0j$84B%$))7Pt=HUGj0LynPKS4&H*A-#_6-sKMyMTm_Xs!;qhhE8DYB<2?j1 z(L4^-&$CeW9fJD)RjB#?1zrH(hO+x42LB8^ujD2ud-ihsEX<(d=uyZg=4mKD{sPL* z7t8jmQ1g2oYMg&TjsG5$KTpFs(mxk!Jyw+MOQ6=H4@&Pk_(`}8UJ7d^?}v)tpF-L7 z0@Ql_4X%dEaE`bZ%D&s6?A{Ay$6ZkCJPT#teI>sMHSU8@-+doyoX4U3_%xJ%pNI1M zub{qvqvW3;Q#5~t`u=^Wc`n1aK6nP~hXYV~d@qze_e1&rv64T6vg4O<75oj9AKr!X z!|61{tD)?x!F4czin9Yy;~#>UV15lX|35;_<87#Q{x@6$FCYj$1#g6FVZG#cpzL@N z%Fb7y^7Ba9K8vC)KdvZw8B|Dciyt1KX5jkX*ymto574;q%Ka( z4Gk~$syy1Qag;R|zOc7sE}^H6V(3k}c}?pDvGZy(*4^VTRJPS9Og%qLtRDof?gh5m z8g+iZ)(@w=z^~2k-%o5BMK*}Sx?MHt-LuyA)bd`E)yGgx8d030>xYZ$Hsh%2;?&0@ zovFz~ohH9vxF2TDHUn?QXY4exSr|ms37a;sDRI@a&rTYNS$r>*?QqEd=UH+C-W51dDQD}y0e%jH>Te;3q97nOq@3xl3GCmK2 ztQkaJt>5+}X6Wet2PQo~uwJbeyJX>u`O7;0gZ64|QFys~s?ODLia+ZtYvfO6lP3SA z;U#uD*4oLz_UQftJ;~AikD6^^8b`IPnonfdOVT3P%rNmAm|=e`mS={ex*ryIJC3=o zrbd*dp#8~qH#vsa3M6KSVsuBhf7QqxGvX!5bQIT2>!Cf1&Wk0YZNAdtaW)dWJWWb2 znE@@GR_M3eOIcC$Z_B(`+=x7LO@6N{xr;}FM#&S6b1&ZHmX>LrBaNDVH7S0~Q(~tV zP8i(?BeT=hNphTBJj7nUd^=r2CbY`R&L|+Uc4oToT9_oeil|rem5-`OE~wFNL|ITH zy*jbq4j4;r1r)C_XKQ(S)UNH;*Yb9BSM?;TkMY4Y(O(&fBPE17n6~BY^JgzirkUNY zY;4j`61hvh!m=#RJymkaHErp%*0kMTVom4iEWW0rUNT{HYv*Q?RjVZLcqY#zQ7}b* zjN-kyj_vq`kZD;p>ZdqhRI6>NhxNP&j7BLHy4~E3Q}b!J9@+z!wZ8p+sVd)AY7O0a zv4ltFD=qF4X(i@X)+t{_b9B$>}#8FbpJCAlZB=7l=iYJW)RkQxoTS3R;%p6FZ9=M z6%NC$?!r9n?^r%^^JpdCr}(up$Q1kS`rf_`mEH}NzAJ6t<^6qEU)I~#+lyjlr*IVK1y_Ufoqjoj2Ll?k98i4Gt~ezHR$Z$H2a|z00-Na%QQF zvPJaU)ZLS=W7qY=O~pR%(#^X^w^Xj`=xMld7gvVDYE+{z_1mk){B-$km8~vxR3+Il z5;^UUeQU2=yMbSdX61S>aZN96^xL&-*A;)uM}jQ&g36XCo=o~}*vy;B=JlJ{`}ND_ zRiRz6+4im8)ahpzi(7Tt>ZyJGwy>`~$}Z={)y4?pRC@dEl<%gKu1`BZ_VwGlb;eSq zO_poR&O+8I;gB?rM|cr<*%)U5t&~5GfeK1CR-M|saGGUBD?e~d@TwkX&1UO`PIl#s zN?GIDig{KK(#M=P#SzXYoJf?|`5gTg(FxPW;U|NX!%+S3!65bP&fektYJ+hXYO`B( z6p!)yY-R&OrgOL}yIXy1{fR7|?Hush`t8{`tm=5N{TerSoJIL>vhO%M%}iaq#%7bY z#iVmOsj(9yZmj*vea>~)k+ml*7+eHwuj^-lF`12QqZMc zyu99pSvt!qbh^p)g2T@<7M->gy58^$){NnOlPixAO=FrtmwMN`Ev^x;qZMFT{mCMh z=A8w%bGWJu| z@IbB-5=1G!$V1)?`QiBCgAFAFhr)DL5x=XM2U^_B!U>Lm`BCwDe=P7L&I}XfX>pKo z2f|5C6ntu@z1S8VPegI-^jUF$q(TM}M=4is9ozl19{5z;)&pMKQ5+>RhL*4MwTx|R zRa&RW)C{t{j1*_fG@nX4Hx~Z1M6}QE1=Weg1AR|aY|Kiy^4U^AmrTr--n>=n>Y-ex zC6ckbk7HX`?8W)LC_rXQ%zmJw=ae?UMb4gVQ{g`0&B%1hsfpDp8)3Xu2RYTZ?QanY*iNo;@zVj}t38_3;&3RI%2b(46+;iGa|g zDimp$QZZucE(g%o7p;)!e3c(Tm-w`O3Z;zC=H;rGe9~zTQw9{mZH4j$M~1lD{Of(R*Ds8<2;dxglePfMOyZhlr=ug u`y`o*+Mxkx6gh~c9Qt%b(T + * @copyright 2014 Heinrich-Heine-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(); + } + + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all Achievements of a Seminary. + * + * @param int $seminaryId ID of Seminary to get Achievements of + * @return array Achievements data + */ + public function getAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? '. + 'ORDER BY achievements.pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ')', + 'ii', + $seminaryId, + $characterId + ); + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + + + return !empty($data); + } + + } + +?> diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..115d1fb2 --- /dev/null +++ b/models/AvatarsModel.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 Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..e042fd10 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,197 @@ + + * @copyright 2014 Heinrich-Heine-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, preferred '. + '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, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $groupsgroupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupUrl); + } + + + return $data[0]; + } + + + /** + * Get a Character groups-group by its ID. + * + * @throws IdNotFoundException + * @param string $groupsgroupId ID of the Character groups-group + * @return array Character groups-group data + */ + public function getGroupsgroupById($groupsgroupId) + { + $data = $this->db->query( + 'SELECT id, name, url, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE id = ?', + 'i', + $groupsgroupId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupId); + } + + + 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, motto '. + '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, motto '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + } + +?> diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc new file mode 100644 index 00000000..4490168d --- /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, xps '. + '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..7b27da8d --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,471 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_id, seminaries.url 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, characters.avatar_id, 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.xplevel, characters.avatar_id, 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. + 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. + 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * Get the superior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of superior Characters + */ + public function getSuperiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps > ? '. + 'ORDER BY characters.xps ASC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * Get the inferior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of inferior Characters + */ + public function getInferiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps < ? '. + 'ORDER BY characters.xps DESC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Check if a Character name already exists. + * + * @param string $name Character name to check + * @return boolean Whether Character name exists or not + */ + public function characterNameExists($name) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM characters '. + 'WHERE name = ? OR url = ?', + 'ss', + $name, + \nre\core\Linker::createLinkParam($name) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + + } + +?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> 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..2c2d5eae --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its URL. + * + * @throws IdNotFoundException + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $seminaryMediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its ID. + * + * @throws IdNotFoundException + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE 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..3e2ced21 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,589 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..5b7eda91 --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,410 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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 quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all Quests for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array Quests for this Seminary + */ + public function getQuestsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM questgroups '. + 'INNER JOIN quests ON quests.questgroup_id = questgroups.id '. + 'WHERE questgroups.seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get all Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return array Quests for this Questtopic + */ + public function getQuestsForQuesttopic($questtopicId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_questsubtopics '. + 'INNER JOIN quests ON quests.id = quests_questsubtopics.quest_id '. + 'WHERE quests_questsubtopics.questsubtopic_id = ?', + 'i', + $questtopicId + ); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..1fd82319 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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/QuesttopicsModel.inc b/models/QuesttopicsModel.inc new file mode 100644 index 00000000..abd246f7 --- /dev/null +++ b/models/QuesttopicsModel.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\models; + + + /** + * Model to interact with Questtopics-table. + * + * @author Oliver Hanraths + */ + class QuesttopicsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttopicsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get a Questtopic by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + * @return array Questtopic data + */ + public function getQuesttopicByUrl($seminaryId, $questtopicUrl) + { + $data = $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $questtopicUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questtopicUrl); + } + + + return $data[0]; + } + + + /** + * Get all Questtopics for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questtopics + */ + public function getQuesttopicsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get count of Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return int Count of Quests + */ + public function getQuestCountForQuesttopic($questtopicId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_questsubtopics.quest_id) AS c ' . + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'WHERE questsubtopics.questtopic_id = ?', + 'i', + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get count of Quests that are linked to a Questtopic and are + * unlocked by a Character. + * + * @param int $questtopicId ID of Questtopic + * @param int $characterId ID of Character + * @return int Count of Quests + */ + public function getCharacterQuestCountForQuesttopic($questtopicId, $characterId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_characters.quest_id) AS c '. + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'INNER JOIN quests_characters ON quests_characters.quest_id = quests_questsubtopics.quest_id AND quests_characters.character_id = ? AND quests_characters.status = 3 '. + 'WHERE questsubtopics.questtopic_id = ?', + 'ii', + $characterId, + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get all Questsubtopics for a Quest. + * + * @param int $questId ID of Quest + * @return array List of Questsubtopics + */ + public function getQuestsubtopicsForQuest($questId) + { + return $this->db->query( + 'SELECT DISTINCT id, questtopic_id, title, url '. + 'FROM quests_questsubtopics '. + 'INNER JOIN questsubtopics ON questsubtopics.id = quests_questsubtopics.questsubtopic_id '. + 'WHERE quests_questsubtopics.quest_id = ?', + 'i', + $questId + ); + } + + } + +?> 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..251ade4f --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, seminarymedia_id '. + '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, seminarymedia_id '. + '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, seminarymedia_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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..efad3bde --- /dev/null +++ b/models/SeminarycharacterfieldsModel.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 the Seminarycharacterfields-tables. + * + * @author Oliver Hanraths + */ + class SeminarycharacterfieldsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new SeminarycharacterfieldsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..c773a0b5 --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,370 @@ + + * @copyright 2014 Heinrich-Heine-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 users with the given user role. + * + * @param string $userrole User role + * @return array List of users + */ + public function getUsersWithRole($userrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + 'FROM users '. + 'LEFT JOIN users_userroles ON users_userroles.user_id = users.id '. + 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '. + 'WHERE userroles.name = ? '. + 'ORDER BY username ASC', + 's', + $userrole + ); + } + + + /** + * Get users with the given user Seminary role. + * + * @param int $seminaryId ID of Seminary + * @param string $userseminaryrole User Seminary role + * @return array List of users + */ + public function getUsersWithSeminaryRole($seminaryId, $userseminaryrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + '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_userseminaryroles.seminary_id = ? AND userseminaryroles.name = ? '. + 'ORDER BY username ASC', + 'is', + $seminaryId, $userseminaryrole + ); + } + + + /** + * 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; + } + + + /** + * Check if an username already exists. + * + * @param string $username Username to check + * @return boolean Whether username exists or not + */ + public function usernameExists($username) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE username = ? OR url = ?', + 'ss', + $username, + \nre\core\Linker::createLinkParam($username) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Check if an e‑mail address already exists. + * + * @param string $email E‑mail address to check + * @return boolean Whether e‑mail address exists or not + */ + public function emailExists($email) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE email = ?', + 's', + $email + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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/bossfight/BossfightQuesttypeAgent.inc b/questtypes/bossfight/BossfightQuesttypeAgent.inc new file mode 100644 index 00000000..6d99ec44 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeAgent.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 a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeController.inc b/questtypes/bossfight/BossfightQuesttypeController.inc new file mode 100644 index 00000000..366aabc8 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeController.inc @@ -0,0 +1,244 @@ + + * @copyright 2014 Heinrich-Heine-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 BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeController 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) + { + // Prepare session + $this->prepareSession($quest['id']); + + // Remove previous answers + $this->Bossfight->clearCharacterSubmissions($quest['id'], $character['id']); + + // Save answers + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) { + $this->Bossfight->setCharacterSubmission($stage['id'], $character['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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + + // Prepare session + $this->prepareSession($quest['id']); + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + + return ($lives['boss'] == 0 && $lives['character'] > 0); + } + + + /** + * Action: quest. + * + * Display a stage with a text and the answers for the following + * stages. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Prepare session + $this->prepareSession($quest['id']); + + // Get Stage + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit_stages'))) + { + $stages = $this->request->getPostParam('submit_stages'); + $stageId = array_keys($stages)[0]; + $stage = $this->Bossfight->getStageById($stageId); + } + else + { + $_SESSION['quests'][$quest['id']]['stages'] = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + } + + // Store Stage in session + if(count($_SESSION['quests'][$quest['id']]['stages']) == 0 || $_SESSION['quests'][$quest['id']]['stages'][count($_SESSION['quests'][$quest['id']]['stages'])-1]['id'] != $stage['id']) { + $_SESSION['quests'][$quest['id']]['stages'][] = $stage; + } + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + // Get Child-Stages + $childStages = $this->Bossfight->getChildStages($stage['id']); + + // Get answer of Character + if($this->request->getGetParam('show-answer') == 'true') { + foreach($childStages as &$childStage) { + $childStage['answer'] = $this->Bossfight->getCharacterSubmission($childStage['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stage', $stage); + $this->set('lives', $lives); + $this->set('childStages', $childStages); + } + + + /** + * Action: submission. + * + * Display all stages with the answers the character has + * choosen. + * + * @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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Get stages + $stages = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + while(!is_null($stage)) + { + $stages[] = $stage; + + $childStages = $this->Bossfight->getChildStages($stage['id']); + $stage = null; + foreach($childStages as &$childStage) + { + if($this->Bossfight->getCharacterSubmission($childStage['id'], $character['id'])) + { + $stage = $childStage; + break; + } + } + } + + // Calculate lives + $stages[0]['lives'] = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + for($i=1; $i $stages[$i-1]['lives']['character'] + $stages[$i]['livedrain_character'], + 'boss' => $stages[$i-1]['lives']['boss'] + $stages[$i]['livedrain_boss'], + ); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stages', $stages); + } + + + + + /** + * Prepare the session to store stage information in + * + * @param int $questId ID of Quest + */ + private function prepareSession($questId) + { + if(!array_key_exists('quests', $_SESSION)) { + $_SESSION['quests'] = array(); + } + if(!array_key_exists($questId, $_SESSION['quests'])) { + $_SESSION['quests'][$questId] = array(); + } + if(!array_key_exists('stages', $_SESSION['quests'][$questId])) { + $_SESSION['quests'][$questId]['stages'] = array(); + } + } + + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeModel.inc b/questtypes/bossfight/BossfightQuesttypeModel.inc new file mode 100644 index 00000000..a05927f0 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeModel.inc @@ -0,0 +1,183 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\questtypes; + + + /** + * Model of the BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get a Boss-Fight. + * + * @throws IdNotFoundException + * @param int $questId ID of Quest + * @return array Boss-Fight data + */ + public function getBossFight($questId) + { + $data = $this->db->query( + 'SELECT bossname, boss_seminarymedia_id, lives_character, lives_boss '. + 'FROM questtypes_bossfight '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Stage to begin the Boss-Fight with. + * + * @param int $questId ID of Quest + * @return array Data of first Stage + */ + public function getFirstStage($questId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ? AND parent_stage_id IS NULL', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get a Stage by its ID. + * + * @param int $stageId ID of Stage + * @return array Stage data or null + */ + public function getStageById($stageId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE id = ?', + 'i', + $stageId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get the follow-up Stages for a Stage. + * + * @param int $parentStageId ID of Stage to get follow-up Stages for + * @return array List of follow-up Stages + */ + public function getChildStages($parentStageId) + { + return $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE parent_stage_id = ?', + 'i', + $parentStageId + ); + } + + + /** + * Reset all Character submissions of a Boss-Fight. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + */ + public function clearCharacterSubmissions($questId, $characterId) + { + $this->db->query( + 'DELETE FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id IN ('. + 'SELECT id '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ?'. + ') AND character_id = ?', + 'ii', + $questId, + $characterId + ); + } + + + /** + * Save Character’s submitted answer for one Boss-Fight-Stage. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + */ + public function setCharacterSubmission($stageId, $characterId) + { + $this->db->query( + 'INSERT INTO questtypes_bossfight_stages_characters '. + '(questtypes_bossfight_stage_id, character_id) '. + 'VALUES '. + '(?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_bossfight_stage_id = ?', + 'iii', + $stageId, $characterId, $stageId + ); + } + + + /** + * Get answer of one Boss-Fight-Stage submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return boolean Stage taken + */ + public function getCharacterSubmission($stageId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_bossfight_stage_id '. + 'FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id = ? AND character_id = ? ', + 'ii', + $stageId, $characterId + ); + + + return (!empty($data)); + } + + } + +?> diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl new file mode 100644 index 00000000..ba427577 --- /dev/null +++ b/questtypes/bossfight/html/quest.tpl @@ -0,0 +1,58 @@ + + + + + + + + + + + +
            + + + + + +
            + : + 0) : ?> + + ♥ + + + + + + : + 0) : ?> + + ♥ + + + + +
            + +

            + + + +
              + +
            • + + → + + +
            • + + +
            • + + +
            • + +
            + diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl new file mode 100644 index 00000000..33cf172f --- /dev/null +++ b/questtypes/bossfight/html/submission.tpl @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + +
            + + + + + +
            + +
            + : + 0) : ?> + + ♥ + + + + + + : + 0) : ?> + + ♥ + + + + +
            diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..08029501 --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
            + &$text) : ?> + 0) : ?> + + + + + +

            + +
            diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..200b3c7f --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
            + &$text) : ?> + 0) : ?> + + + + +
            diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..ba3e5a0c --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,351 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..d202eb8f --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,49 @@ +
            + + + + + + + + + + +
            + + 0) : ?> + + +
            +
              + +
            1. + + : + + : + + +
            2. + +
            + +
            + diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..c47e414d --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,48 @@ +
            + + + + + + + + + + +
            + + 0) : ?> + + +
            +
              + +
            1. + + : + + : + + +
            2. + +
            +
            + 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..90843218 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Match drops with user answers + foreach($drops as &$drop) + { + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..1e772edf --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,146 @@ + + * @copyright 2014 Heinrich-Heine-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 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..5a5d3717 --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
            + +
            + + + +
            + +
            + +
            + + + +
            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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..456db617 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,261 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) + { + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..c2c32595 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
            +
            + +

            +
              + &$answer) : ?> +
            1. + /> + +
            2. + +
            +
            + + + + + + + +
            diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ae14f199 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
              + &$question) : ?> +
            1. +

              +
                + +
              1. + + × + +
              2. + +
              +
            2. + +
            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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..0c72c1de --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

            + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

            + + +
            +
            + : +
              + +
            • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
            • + +
            + +
            + +
            (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
            + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

            + + + + + + + +
            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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.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/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..fdaf2d7c --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,33 @@ + +
            + +
            + +

            +

            + +
              + +
            • + + + +

              + + + + + +
              + + +
              +
              + +
              +

               %

              +
              + +
            • + +
            diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..ba53c97b --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,52 @@ + +
            + +
            + +

            +

            +

            +
            +
            + +
            +
            +

            + +
            +
              +
            • .
            • +
            •  XPs
            • +
            • 1) ? _('Members') : _('Member')?>
            • +
            +
            + +
            +

            +
              + +
            • + +

              + +

              +

              XP

              +
            • + +
            +
            + +
            +

            +
              + +
            • +

              + format(new \DateTime($quest['created']))?> + + / XP +

              +
            • + +
            +
            diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..93aeaba7 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,22 @@ + +
            + +
            + +

            +

            +

            + + + + +

            +
              + +
            • + +
            diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..e0f54b94 --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,13 @@ + +
            + +
            + +

            +

            + +
              + +
            • + +
            diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..8112a913 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,51 @@ + +
            + +
            + +

            +

            +

            +

            + + + + + +
            +

            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..74ba7cae --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,109 @@ + +
            + +
            + +

            +

            +

            + +
            +
            +
            +
            + +
            +

            :  %

            +
            +
            +

            +

            +
            +
            +

            +

            XP

            +
            +
            +

            .

            +

            +
            + +

            +
              + +
            • + + + +

              +

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

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

            +
              + +
            • + +

               XPs

              +
            • + +
            +
            + +
            +

            +
              + &$rankCharacter) : ?> +
            • + + + +

              .

              +

              ( XPs)

              +
            • + +
            • + + + +

              .

              +

              ( XPs)

              +
            • + &$rankCharacter) : ?> +
            • + + + +

              .

              +

              ( XPs)

              +
            • + +
            +
            +
            + +
            +

            +
              + +
            • +

              (/)

              +
              + +
              +
            • + +
            +
            + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..a1227a5a --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,19 @@ + +
            + +
            + +

            +

            + +
              + +
            • + +

              + +

              +

              XP

              +
            • + +
            diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..d62c0d35 --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,82 @@ + +
            + +
            + +

            +

            + +
            + +
              + &$settings) : ?> +
            • +
                + $value) : ?> +
              • + +
              • + +
              +
            • + +
            + +
            + + +
            + + +
            + + +
              + &$settings) : ?> +
            • + +
            + +
            + + + + + required="required"/> + + + + + +
            + +
            + +
            diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..0f68bc07 --- /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..9cfcf8f3 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,58 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
            + +
            +
            + +
            + + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..ef919187 --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

            +

            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/library/index.tpl b/views/html/library/index.tpl new file mode 100644 index 00000000..be5b441d --- /dev/null +++ b/views/html/library/index.tpl @@ -0,0 +1,21 @@ + +
            + +
            + +

            +

            + +
              + +
            • +

              +
              +
              + +
              +

              /

              +
              +
            • + +
            diff --git a/views/html/library/topic.tpl b/views/html/library/topic.tpl new file mode 100644 index 00000000..be396d4f --- /dev/null +++ b/views/html/library/topic.tpl @@ -0,0 +1,28 @@ + +
            + +
            + +

            +

            +

            + +
            +
            + +
            +

            /

            +
            + +
              + +
            • + +
                + +
              • + +
              +
            • + +
            diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl new file mode 100644 index 00000000..c2662789 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
          • The Legend of Z
          • + 0) : ?>
          • +
          • + + +
          • + +
          • + diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..4d3ce2b9 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
            + +
            + +

            + + + + +

            :

            + +

            + + +

            + + + + + 0) : ?> +

            +
              + +
            • + +
              +
              +

              Fortschritt:

              +
              + +
              +

              / XP

              +
              +
            • + +
            + + + + + +

            + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl new file mode 100644 index 00000000..80a3e4fe --- /dev/null +++ b/views/html/quests/index.tpl @@ -0,0 +1,49 @@ + +
            + +
            + +

            +

            + +
            + + + + + + + + + + + + + + + + + + + + + +
            + + + + XPs
            + + + +
            diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl new file mode 100644 index 00000000..cbb21d25 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,104 @@ + +
            + +
            + +

            + +

            + + 0) : ?> +
            +

            + + + +
            + + +

            + + + + + + + +

            + 0 || !empty($questtext['abort_text'])) : ?> +
              + +
            • + + +
            • + +
            + + +
            +
            + + + +
            + +

            + +

            + +

            +
            + + + +
            + +

            +

            + + + + +

            :

            + + +
            + + + +
            + 0) : ?> +
              + + +
            • + : + + + + + +
            • + +
            • + : + + + + + +
            • + + +
            + + : + + Spiel vorbei + +
            + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..a29700db --- /dev/null +++ b/views/html/quests/submission.tpl @@ -0,0 +1,17 @@ + +
            + +
            + +

            + +

            + + + + + +

            +
            + +
            diff --git a/views/html/quests/submissions.tpl b/views/html/quests/submissions.tpl new file mode 100644 index 00000000..e2a17579 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,41 @@ + +
            + +
            + +

            + +

            + + + + + +
            +

            +
              + +
            • + +
            • + +
            + +

            +
              + +
            • + +
            • + +
            + +

            +
              + +
            • + +
            • + +
            +
            diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..30938357 --- /dev/null +++ b/views/html/seminaries/create.tpl @@ -0,0 +1,15 @@ + +
            + +
            + +

            +

            + +
            +
            + +
            +
            + +
            diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl new file mode 100644 index 00000000..8e53fdb5 --- /dev/null +++ b/views/html/seminaries/delete.tpl @@ -0,0 +1,13 @@ + +
            + +
            + +

            +

            + + +
            + + +
            diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl new file mode 100644 index 00000000..66d7efcd --- /dev/null +++ b/views/html/seminaries/edit.tpl @@ -0,0 +1,15 @@ + +
            + +
            + +

            +

            + +
            +
            + +
            +
            + +
            diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..986991a9 --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,33 @@ +

            + 0) : ?> + + +
              + +
            • + + + +

              + 0) : ?> + + + + +

              +
              + +
              + 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..28488018 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,42 @@ + +
            + +
            + + +

            + 0) : ?> + + +

            + + +

            + + diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..60752cc7 --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,50 @@ +
            +

            + + +
            + + +
            +

            +

            +
            + + + +
            +

            +
              +
            • + + + +

              +

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

              +
            • +
            +
            + + +
            + +

            +
              + +
            • + + + +

              +

              ( XPs)

              +
            • + +
            +

            + +
            diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..041c4f43 --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,6 @@ +
          • + 0) : ?>
          • +
          • +
          • +
          • + 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..d4f38bb7 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

            +

            + +
            +
            + +
            + +
            + +
            + +
            + +
            +
            + +
            diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..56ba5d7b --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

            +

            + +
            +
            + +
            + +
            + +
            + +
            + +
            +
            + +
            diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..5d2883af --- /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..932b4c20 --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

            + +

            + +

            .

            + +
            +
            + +
            + +
            +
            + +
            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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..20cbfc63 --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,90 @@ +

            + +

            + +
              + &$settings) : ?> +
            • +
                + $value) : ?> +
              • + getMessage(); + break; + } ?> +
              • + +
              +
            • + +
            + +
            +
            + + />
            + + />
            + + />
            + + />
            + + />
            +
            + +
            diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..745097b0 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,35 @@ +

            +

            + 0) : ?> + + +

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

            + +

            +
              + +
            • + +

              + +

              + 0) : ?> + + + + +

              +

              +
            • + +
            + +

            + 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..b9dabe4a --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,247 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} +aside{display:none} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + + +/** Quest Types **/ + +.crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} +.crossword td{background:#d7d4cf;padding:1px} +.crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} +.crossword ol{list-style-type:decimal;margin-left:25px} +.crossword li{margin-top:20px} +.crossword .index{position:absolute;font-size:0.625em;margin:-2px 0 0 2px} + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:75%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} +article{background:#f7f5f2;float:left} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:100%} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} +aside .cranks li:nth-child(odd){background:#f7f5f2} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Wed, 16 Apr 2014 15:55:49 +0200 Subject: [PATCH 241/340] use dedicated input-fields for Questtype ?Boss-Fight? --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 7430 -> 7459 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 22 ++++++++++++-------- questtypes/bossfight/html/quest.tpl | 9 ++++---- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 3525bcf513b478dda590a7bd9d9c7ee48b96a9c2..cabfa46b9f92800b5bc2981812c631cdd57e5e9c 100644 GIT binary patch delta 2574 zcmXxme@vBC9LMo5T*Qk~TrVO**d;(j`KdtA%@j35DoiQG%z}R0yeU^Qq90rAuB9!@ zl^Z(8TvKP8Wt$D}KTmXRY{q#Q z!5O&AjSpiv)9`o9WPZC&$B$!9KWS^l)35}Ka5grh9uUJ~>_t^zz>Pmbeg7;f z!Rx35ZsIibQD#*l2bEw5<5a;)-5)GKWwsDC(GpZ*El6(GhCItUP!q(QJ*cgD6*b{b zRAPIv8V_R)UUc{U{GsNZnMM88DdC1zT!Y@ks1>##AB(whA8LgMQHg$qg?I)_@i$ar z**uK7EYDenT1W%xur{Hdvo1jW>06(>@h+;=3Dkowp-MJ_I*h*{b65fOR0%7bwWvcG zcH@<(N~}R8+Ubm7D&tMa$6|4=JkzAUxC=G$9@GlQqRCj45QwLC|2WJ&f}=}_-E8v7(=ZvlUcN7 zx!8aq;i)Jptpy!JW=E3gr7 z#|T#8HfI8r$d9N*e?`5n|G04#e~+t@i<~P^XQQov`s;^LZm0rVQRCN8d%oTIHfrVX zA*a?3pdNS(RoWA%gioPX`VBI-edqiO_5A?-_2nsmO(d+Hyjh&`E>|og5wCRAMzThX@h(6Kb{NShtfy z=bgf5v6(0(HWJ#fDB%@j6GXg$*#lTd+~w6~3BBiu8e%rV%V19sYD!hER!OIgSmbW3bLwF0e^_@CdcAdSJ?`eJQ~h}2Y{u%8Nzv}^o=Bo#>fh;U m_2K&3{@h@RI(3P~!7oxLx1ahT+7;Q77@K}PEishW>iZv0o!Ei^ delta 2547 zcmXxlduWzb9Ki9jiN+i-NCDG*% z%VmWrqDAOm1+Q6@)Rau=k1>N*BCUcT$q0rRQK@~u?~aG}_kPZKeBS5$&hMOOzSr9^fh)Zx3 zZja-)aT4Pbczp=pDuZ=%K76UPUUPdLQaVEh=p z|5zNKLhtX!OzcHI;T&H>nLnJjgZ*e`|6nO*vy3vVMjz0MCD@Ku;DtEeh2DP@P4FC= zz%Q7Gf1s7Pf+jeWaA?}cX>S?ay$gHE88IgK91Z;@w*LDbU{7DS8CLs=5X zb!a7KqKURd=VLnKMaU<#^2H;=ngkthd=8!Xc{KAK=tP}pB74xo`98X$FQTWgD~;C_ zec&5pQ-BB2gg-^EpGGTi7TwBzw9<*IbSBd&;bknwg*X*AMh~Fx?-}$goJUu93G2|= zctn_izD0A9e+nz(cq=+#Cz4}0fIcUQB$xhNzme!$%Z7-TY7m`ExdJOe9V>t*nVlz&` z)zP=mL{6fKcB8LpUmWN0_uEQNh)zP!MlJfgIcNnI7g2vNba25vUlU!Au6z@EYF|Vj zxEn3)el+0^(3K{Uxx?qt@6r1&qR+pKyrtm|Vh(W^aT`(X{;wsjwMBF`6B$G!v4&Vr z+(IlRuC`!uBxn2vJ8^eBX_kgeT`AqLrv5mJ!yfnXq|8XA{X_dFRKjMQ5U;cmiB=pgEe351)KPAnkG2~ST8Ve^F8HVlw@Ie9n3O5z@( zj95juVXKMMU(6?9MQRYjeK?(%k*dusdQTCa@KVAP{U~8;B1#BP`?YpIoyBo59zFPz z<5;SQg~aU+Xb%n$YLMd-Y6<^PdT>)T@a_BFmi#emVOp{_`_kad=?yjYom28lQ=MdM V{_*r=alt2<=_@xRdkg1f{0}k!(+dCq diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 86648bc7..b658c97b 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-16 13:19+0100\n" -"PO-Revision-Date: 2014-04-16 13:19+0100\n" +"POT-Creation-Date: 2014-04-16 15:53+0100\n" +"PO-Revision-Date: 2014-04-16 15:54+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -16,14 +16,18 @@ msgstr "" "X-Poedit-SearchPath-0: views\n" "X-Poedit-SearchPath-1: questtypes\n" -#: questtypes/bossfight/html/quest.tpl:21 -#: questtypes/bossfight/html/quest.tpl:31 -#: questtypes/bossfight/html/submission.tpl:27 -#: questtypes/bossfight/html/submission.tpl:37 +#: questtypes/bossfight/html/quest.tpl:11 +#: questtypes/bossfight/html/quest.tpl:24 +#: questtypes/bossfight/html/submission.tpl:21 +#: questtypes/bossfight/html/submission.tpl:33 msgid "lost" msgstr "verloren" -#: questtypes/bossfight/html/quest.tpl:54 +#: questtypes/bossfight/html/quest.tpl:41 +msgid "Choose" +msgstr "Wählen" + +#: questtypes/bossfight/html/quest.tpl:47 #: questtypes/choiceinput/html/quest.tpl:14 #: questtypes/crossword/html/quest.tpl:30 #: questtypes/dragndrop/html/quest.tpl:16 @@ -326,10 +330,10 @@ msgstr "Lösung anzeigen" msgid "Quest" msgstr "Quest" -#: views/html/quests/submission.tpl:14 +#: views/html/quests/submission.tpl:10 #, php-format msgid "Submission of %s" -msgstr "Lösungen von %s" +msgstr "Lösung von %s" #: views/html/quests/submissions.tpl:15 msgid "submitted" diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl index d28a153e..bda3bf6a 100644 --- a/questtypes/bossfight/html/quest.tpl +++ b/questtypes/bossfight/html/quest.tpl @@ -34,10 +34,11 @@
            • - - → - - +

              + + +

              +
            • From 6e6378ffb08bae54f804033f13c414ec5ad44662 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 16 Apr 2014 15:56:24 +0200 Subject: [PATCH 242/340] correct display of lives for Character submission in Questtype ?Boss-Fight? --- questtypes/bossfight/html/submission.tpl | 48 ++++++++++++------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl index 6861e0ea..260d28b0 100644 --- a/questtypes/bossfight/html/submission.tpl +++ b/questtypes/bossfight/html/submission.tpl @@ -10,29 +10,29 @@

              -
              -

              -

              - 0) : ?> - - - - - - -

              -
              -
              -

              -

              - 0) : ?> - - - - - - -

              -
              +
              +

              +

              + 0) : ?> + + + + + + +

              +
              +
              +

              +

              + 0) : ?> + + + + + + +

              +
              From b809b5ebbb34eba3990df80d586f75c2f327eacf Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 16 Apr 2014 15:56:49 +0200 Subject: [PATCH 243/340] do not show Quest media on page for Character submission --- views/html/quests/submission.tpl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl index a29700db..10fde93a 100644 --- a/views/html/quests/submission.tpl +++ b/views/html/quests/submission.tpl @@ -7,11 +7,7 @@

              - - - - -

              +

              From 73f14419dddffb2cfb46dbc277c3f75414868324 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 16 Apr 2014 16:39:38 +0200 Subject: [PATCH 244/340] boss fight answer options design --- questtypes/bossfight/html/quest.tpl | 6 +++--- www/css/desktop.css | 5 +++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl index bda3bf6a..f1bba542 100644 --- a/questtypes/bossfight/html/quest.tpl +++ b/questtypes/bossfight/html/quest.tpl @@ -31,14 +31,14 @@
              -
                +
                  -
                • +
                • - +

                • diff --git a/www/css/desktop.css b/www/css/desktop.css index ed0f25b3..e114e547 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -188,6 +188,7 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .opponent .boss{max-width:100%} .opponent p{text-align:center} .opponent .fa{font-size:1.25em;color:#c7135b;padding-right:0} +.bossfight .option{background:#fff;margin-bottom:10px;padding:15px 15px 5px;border-radius:3px} /** Media Queries **/ @@ -221,6 +222,10 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gquests .date{display:inline;margin-right:15px} .gquests .xp{float:right} + +.bossfight li{float:left;width:44%} +.bossfight li:nth-child(even){float:right} +.bossfight input,.bossfight p{text-align:center} } @media only screen and (min-width:1024px){ From 027513400aa55ba01279a59ee8a8939b22ae37e1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 16 Apr 2014 17:10:15 +0200 Subject: [PATCH 245/340] login & register design refinement --- views/html/introduction/index.tpl | 4 ++-- views/html/users/login.tpl | 2 +- views/html/users/register.tpl | 2 +- www/css/desktop.css | 10 +++++++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index ef919187..3dc754ae 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -3,14 +3,14 @@

                  - +


                  - + diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl index 932b4c20..f141f4cd 100644 --- a/views/html/users/login.tpl +++ b/views/html/users/login.tpl @@ -4,7 +4,7 @@

                  .

                  -
                  +

                  diff --git a/views/html/users/register.tpl b/views/html/users/register.tpl index 20cbfc63..29843569 100644 --- a/views/html/users/register.tpl +++ b/views/html/users/register.tpl @@ -73,7 +73,7 @@
                - +
                />
                diff --git a/www/css/desktop.css b/www/css/desktop.css index e114e547..a882b22f 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -29,7 +29,7 @@ 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} +fieldset{border:0;margin:0;padding:0} legend{border:0;padding:0} button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} button,input{line-height:normal} @@ -126,6 +126,14 @@ aside{display:none} input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} +/** Login & Registration **/ + +.logreg{width:auto;display:inline-block;padding:15px 20px;background:#eae8e4;border-radius:3px} +.logreg label{display:block;font-size:.875em} +.logreg input{margin:5px 0 15px} +.logreg .cta{display:block} + + /** Character Profile **/ .cportrait img{width:100%} From 6417c1a4df6c3e1e162d55b416d49c3e4e5692aa Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 16 Apr 2014 17:13:50 +0200 Subject: [PATCH 246/340] boss fight answer design refinement --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 +++++++++++ agents/bottomlevel/MenuAgent.inc | 37 + .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarybarAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/AchievementsAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 ++ agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/LibraryAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 + agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 ++ agents/toplevel/BinaryAgent.inc | 41 ++ agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 70 ++ apis/WebApi.inc | 250 +++++++ app/Controller.inc | 103 +++ app/Model.inc | 42 ++ app/QuesttypeAgent.inc | 267 +++++++ app/QuesttypeController.inc | 349 +++++++++ app/QuesttypeModel.inc | 154 ++++ app/QuesttypeView.inc | 76 ++ app/ToplevelAgent.inc | 36 + app/Utils.inc | 112 +++ app/controllers/IntermediateController.inc | 139 ++++ app/controllers/SeminaryRoleController.inc | 277 ++++++++ app/exceptions/FileUploadException.inc | 75 ++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 ++ .../QuesttypeAgentNotValidException.inc | 77 ++ .../QuesttypeControllerNotFoundException.inc | 77 ++ .../QuesttypeControllerNotValidException.inc | 77 ++ .../QuesttypeModelNotFoundException.inc | 77 ++ .../QuesttypeModelNotValidException.inc | 77 ++ .../SubmissionNotValidException.inc | 77 ++ app/exceptions/WrongFiletypeException.inc | 75 ++ app/lib/Password.inc | 316 +++++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 180 +++++ configs/CoreConfig.inc | 167 +++++ controllers/AchievementsController.inc | 162 +++++ controllers/BinaryController.inc | 37 + controllers/CharactergroupsController.inc | 150 ++++ .../CharactergroupsquestsController.inc | 91 +++ controllers/CharactersController.inc | 296 ++++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 + controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 + controllers/LibraryController.inc | 129 ++++ controllers/MediaController.inc | 368 ++++++++++ controllers/MenuController.inc | 51 ++ controllers/QuestgroupsController.inc | 179 +++++ .../QuestgroupshierarchypathController.inc | 90 +++ controllers/QuestsController.inc | 665 ++++++++++++++++++ controllers/SeminariesController.inc | 252 +++++++ controllers/SeminarybarController.inc | 96 +++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 +++++ controllers/UserrolesController.inc | 47 ++ controllers/UsersController.inc | 363 ++++++++++ .../components/AchievementComponent.inc | 41 ++ controllers/components/AuthComponent.inc | 79 +++ .../components/ValidationComponent.inc | 140 ++++ core/Agent.inc | 607 ++++++++++++++++ core/Api.inc | 163 +++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 ++++ core/Component.inc | 85 +++ core/Config.inc | 49 ++ core/Controller.inc | 433 ++++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 ++++++++ core/Logger.inc | 132 ++++ core/Model.inc | 141 ++++ core/Request.inc | 64 ++ core/Response.inc | 158 +++++ core/View.inc | 124 ++++ core/WebUtils.inc | 75 ++ drivers/DatabaseDriver.inc | 87 +++ drivers/MysqliDriver.inc | 169 +++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 ++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 ++ exceptions/ClassNotValidException.inc | 77 ++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 ++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 ++ exceptions/ServiceUnavailableException.inc | 77 ++ exceptions/ViewNotFoundException.inc | 77 ++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 7459 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 605 ++++++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 508 +++++++++++++ models/AvatarsModel.inc | 62 ++ models/CharactergroupsModel.inc | 197 ++++++ models/CharactergroupsquestsModel.inc | 136 ++++ models/CharactersModel.inc | 471 +++++++++++++ models/CharactertypesModel.inc | 57 ++ models/DatabaseModel.inc | 83 +++ models/MediaModel.inc | 139 ++++ models/QuestgroupsModel.inc | 589 ++++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 ++++ models/QuestsModel.inc | 410 +++++++++++ models/QuesttextsModel.inc | 114 +++ models/QuesttopicsModel.inc | 154 ++++ models/QuesttypesModel.inc | 62 ++ models/SeminariesModel.inc | 193 +++++ models/SeminarycharacterfieldsModel.inc | 78 ++ models/UploadsModel.inc | 148 ++++ models/UserrolesModel.inc | 77 ++ models/UsersModel.inc | 370 ++++++++++ models/UserseminaryrolesModel.inc | 78 ++ .../bossfight/BossfightQuesttypeAgent.inc | 24 + .../BossfightQuesttypeController.inc | 244 +++++++ .../bossfight/BossfightQuesttypeModel.inc | 183 +++++ questtypes/bossfight/html/quest.tpl | 51 ++ questtypes/bossfight/html/submission.tpl | 38 + .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 + .../ChoiceinputQuesttypeController.inc | 157 +++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 ++++ questtypes/choiceinput/html/quest.tpl | 15 + questtypes/choiceinput/html/submission.tpl | 12 + .../crossword/CrosswordQuesttypeAgent.inc | 24 + .../CrosswordQuesttypeController.inc | 351 +++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 +++ questtypes/crossword/html/quest.tpl | 49 ++ questtypes/crossword/html/submission.tpl | 48 ++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 199 ++++++ .../dragndrop/DragndropQuesttypeModel.inc | 146 ++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 +++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 261 +++++++ .../MultiplechoiceQuesttypeModel.inc | 159 +++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 +++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 +++++ .../textinput/TextinputQuesttypeModel.inc | 117 +++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 +++++++++++ responses/WebResponse.inc | 250 +++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/achievement.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/achievements/index.tpl | 33 + views/html/charactergroups/group.tpl | 52 ++ views/html/charactergroups/groupsgroup.tpl | 22 + views/html/charactergroups/index.tpl | 13 + views/html/charactergroupsquests/quest.tpl | 51 ++ views/html/characters/character.tpl | 109 +++ views/html/characters/index.tpl | 19 + views/html/characters/register.tpl | 82 +++ views/html/error/index.tpl | 2 + views/html/html.tpl | 58 ++ views/html/introduction/index.tpl | 36 + views/html/library/index.tpl | 21 + views/html/library/topic.tpl | 28 + views/html/menu/index.tpl | 9 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/index.tpl | 49 ++ views/html/quests/quest.tpl | 104 +++ views/html/quests/submission.tpl | 13 + views/html/quests/submissions.tpl | 41 ++ views/html/seminaries/create.tpl | 15 + views/html/seminaries/delete.tpl | 13 + views/html/seminaries/edit.tpl | 15 + views/html/seminaries/index.tpl | 33 + views/html/seminaries/seminary.tpl | 42 ++ views/html/seminarybar/index.tpl | 50 ++ views/html/seminarymenu/index.tpl | 6 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 14 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 90 +++ views/html/users/user.tpl | 35 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 270 +++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 ++ www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 +++ 239 files changed, 23034 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/LibraryAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/LibraryController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/AvatarsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/CharactertypesModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttopicsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeAgent.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeController.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeModel.inc create mode 100644 questtypes/bossfight/html/quest.tpl create mode 100644 questtypes/bossfight/html/submission.tpl create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/achievements/index.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/characters/register.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/library/index.tpl create mode 100644 views/html/library/topic.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarybar/index.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..43dff51a --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* 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..49512791 --- /dev/null +++ b/agents/bottomlevel/MenuAgent.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\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) + { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); + } + + } + +?> 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/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\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/LibraryAgent.inc b/agents/intermediate/LibraryAgent.inc new file mode 100644 index 00000000..aedc890a --- /dev/null +++ b/agents/intermediate/LibraryAgent.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 Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryAgent 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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..049ba904 --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,70 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); + } + + + + + 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..5731aeed --- /dev/null +++ b/app/Controller.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; + + + /** + * 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 and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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..a445dcf5 --- /dev/null +++ b/app/Utils.inc @@ -0,0 +1,112 @@ + + * @copyright 2014 Heinrich-Heine-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); + } + + + /** + * Send an e‑mail. + * + * @param string $from Sender of mail + * @param mixed $to One (string) or many (array) receivers + * @param string $subject Subject of mail + * @param string $message Message of mail + * @param boolean $html Whether mail should be formatted as HTML or not + * @return Whether mail has been send or not + */ + public static function sendMail($from, $to, $subject, $message, $html=false) + { + // Set receivers + $to = is_array($to) ? implode(',', $to) : $to; + + // Set header + $headers = array(); + $headers[] = 'Content-type: text/'.($html ? 'html' : 'plain').'; charset=UTF-8'; + if(!is_null($from)) { + $headers[] = "From: $from"; + } + $header = implode("\r\n", $headers)."\r\n"; + + + // Send mail + return mail($to, $subject, $message, $header); + } + + } + +?> diff --git a/app/controllers/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..19647d7e --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + self::$user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById(self::$user['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + } + + + /** + * 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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..cb7f6b86 --- /dev/null +++ b/app/controllers/SeminaryRoleController.inc @@ -0,0 +1,277 @@ + + * @copyright 2014 Heinrich-Heine-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\controllers\IntermediateController + { + /** + * Required components + * + * @var array + */ + public $components = array('achievement', 'auth'); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements'); + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = 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); + + // Get Seminary and Character data + try { + self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Check achievements + $this->checkAchievements($request, $response); + + // Set Seminary and Character data + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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) + { + // Do not check index page + if(is_null($request->getParam(3))) { + return; + } + + + // 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(self::$user['seminaryroles'], $permissions)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Check if Character is present + if(is_null(self::$character)) { + return; + } + + // Get unachieved Achievments + $achievements = $this->Achievements->getUnachhievedAchievementsForCharacter(self::$seminary['id'], self::$character['id']); + if(in_array('user', self::$user['seminaryroles'])) { + $achievements = array_merge($achievements, $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary(self::$seminary['id'])); + } + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + self::$character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + self::$character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + self::$character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], self::$character['id']); + } + } + } + + } + +?> diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..8128f4d2 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,180 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace 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', + 'mailsender' => 'noreply@zyren.inf-d.de' + ); + + + /** + * 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Miscellaneous settings + * + * @static + * @var array + */ + public static $misc = array( + 'ranking_range' => 2 + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ) + ); + + + /** + * 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|register|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|register))', '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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..6f363012 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,162 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Achievements + $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); + foreach($achievements as &$achievement) + { + // Get status for Character + $achieved = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + + // Get Character progress + if(!$achieved && $achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + + // Get media + $achievement['media_index'] = 'unachieved_achievementsmedia_id'; + if($achieved) { + $achievement['media_index'] = 'achieved_achievementsmedia_id'; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('achievements', $achievements); + } + + } + +?> diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc new file mode 100644 index 00000000..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..d4cf6cee --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,150 @@ + + * @copyright 2014 Heinrich-Heine-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', 'avatars', '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: 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); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); + + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + // Get Character groups Quests + $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc new file mode 100644 index 00000000..fb9d18c8 --- /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->getSeminaryMediaById($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..8f7e02cf --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,296 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media', 'questtopics'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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']); + + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get ranking + $ranking = array( + 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']), + 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']) + ); + foreach($ranking['superior'] as &$rankCharacter) + { + if(!is_null($rankCharacter['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + foreach($ranking['inferior'] as &$rankCharacter) + { + if(!is_null($rankCharacter['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('characterfields', $characterfields); + $this->set('user', $user); + $this->set('groups', $groups); + $this->set('achievements', $achievements); + $this->set('ranking', $ranking); + $this->set('questtopics', $questtopics); + } + + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + if($this->Characters->characterNameExists($charactername)) { + $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true); + } + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Send mail + $this->sendRegistrationMail($charactername); + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + + + + + /** + * Send mail for new Character registration. + * + * @param string $charactername Name of newly registered Character + */ + private function sendRegistrationMail($charactername) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new Character registration: %s', $charactername); + $message = sprintf('User “%s” <%s> has registered a new Character “%s” for the Seminary “%s”', self::$user['username'], self::$user['email'], $charactername, self::$seminary['title']); + $moderators = $this->Users->getUsersWithSeminaryRole(self::$seminary['id'], 'moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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..dd4bc86c --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + $this->set('loggedCharacter', SeminaryRoleController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc new file mode 100644 index 00000000..a37618ae --- /dev/null +++ b/controllers/LibraryController.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\controllers; + + + /** + * Controller of the LibraryAgent to list Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('questtopics', 'seminaries', 'quests', 'questgroups'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Questtopics of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + // Get Quest count + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + + // Get Character progress + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopics', $questtopics); + } + + + /** + * Action: topic. + * + * Show a Questtopic and its Quests with Questsubtopics. + * + * @throws IdNotFoundException + * @param string $eminaryUrl URL-Title of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + */ + public function topic($seminaryUrl, $questtopicUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Questtopic + $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl); + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForQuesttopic($questtopic['id']) as $quest) + { + if($this->Quests->hasCharacterEnteredQuest($quest['id'], $character['id'])) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + + // Get Subtopics + $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']); + + $quests[] = $quest; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopic', $questtopic); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..16e01cbb --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,368 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'achievements', '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. + * + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + + + /** + * 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; + } + + + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + 480 + ); + } + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } + + + /** + * Get supported image types. + * + * @return array List of supported image types + */ + private static function getImageTypes() + { + $im = new \Imagick(); + + + return $im->queryFormats(); + } + + + /** + * Resize an image. + * + * @param string $fileName Absolute pathname of image to resize + * @param string $mimeType Mimetype of target image + * @param int $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..c254a617 --- /dev/null +++ b/controllers/MenuController.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..d7b8acf1 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,179 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && $currentQuest['solved']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..ee8a3c67 --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,665 @@ + + * @copyright 2014 Heinrich-Heine-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( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator', 'user'), + 'submission' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator') + ); + + + + + /** + * Action: index. + * + * List all Quests for a Seminary. + * + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Prepare filters + $filters = array( + 'questgroups' => array(), + 'questtypes' => array() + ); + + // Get selected filters + $selectedFilters = array( + 'questgroup' => "0", + 'questtype' => "" + ); + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('filters'))) { + $selectedFilters = $this->request->getPostParam('filters'); + } + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForSeminary($seminary['id']) as $quest) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + if($selectedFilters['questgroup'] != "0" && $selectedFilters['questgroup'] != $quest['questgroup']['id']) { + continue; + } + + // Get Questtype + $quest['questtype'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + if($selectedFilters['questtype'] != "" && $selectedFilters['questtype'] != $quest['questtype']['classname']) { + continue; + } + + // Add filter values + $filters['questgroups'][$quest['questgroup']['id']] = $quest['questgroup']; + $filters['questtypes'][$quest['questtype']['classname']] = $quest['questtype']; + + // Add open submissions count + $quest['opensubmissionscount'] = count($this->Characters->getCharactersSubmittedQuest($quest['id'])); + + $quests[] = $quest; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('quests', $quests); + $this->set('filters', $filters); + $this->set('selectedFilters', $selectedFilters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + //$questtexttypesUrls = array_map(function($t) { return $t['url']; }, $questtexttypes); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Quest status + $questStatus = $this->request->getGetParam('status'); + $questStatusText = null; + if(!is_null($questStatus)) + { + switch($questStatus) + { + case 'solved': + $questStatusText = $quest['right_text']; + break; + case 'unsolved': + $questStatusText = $quest['wrong_text']; + break; + } + } + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Task + $task = null; + if($questtexttypeUrl == 'Prolog') + { + // Questtype + $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + + // Render task + if(!is_null($questtype['classname'])) { + $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); + } + else { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + } + + // Has Character solved quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Next Quest/Questgroup + $nextQuests = null; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog') + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('queststatustext', $questStatusText); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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 + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..771c9787 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,252 @@ + + * @copyright 2014 Heinrich-Heine-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', 'userseminaryroles', '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) + { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($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 + $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/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..5ab1567b --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,96 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('characters', 'quests', 'questgroups', 'achievements', 'charactergroups', 'avatars', 'media'); + + + + + /** + * Action: index. + */ + public function index() + { + if(is_null(SeminaryRoleController::$seminary)) { + return; + } + + // Get Seminary + $seminary = SeminaryRoleController::$seminary; + + // Get Character + $character = SeminaryRoleController::$character; + if(is_null($character)) { + return; + } + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + + // Get Character group members + $characterGroups = array(); + foreach($this->Charactergroups->getGroupsForCharacter($character['id']) as $group) + { + $groupsgroup = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']); + if($groupsgroup['preferred']) + { + $group['members'] = $this->Characters->getCharactersForGroup($group['id']); + foreach($group['members'] as &$member) + { + if(!is_null($member['avatar_id'])) + { + $avatar = $this->Avatars->getAvatarById($member['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $member['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + } + $characterGroups[] = $group; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); + $this->set('characterGroups', $characterGroups); + } + + } + +?> diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..5b5d780f --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..90e50912 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,363 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + + // Level + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + if($this->Users->usernameExists($username)) { + $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true); + } + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + if($this->Users->emailExists($email)) { + $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true); + } + + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Send mail + $this->sendRegistrationMail($username, $email); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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); + } + + + + + /** + * Send mail for new user registration. + * + * @param string $username Name of newly registered user + * @param string $email E‑mail address of newly registered user + */ + private function sendRegistrationMail($username, $email) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new user registration: %s', $username); + $message = sprintf('User “%s” <%s> has registered themself to %s', $username, $email, \nre\configs\AppConfig::$app['name']); + $moderators = $this->Users->getUsersWithRole('moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..950d45ed --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,140 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $settings) + { + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Add a custom determined validation result to a validation + * store. + * + * @param mixed $validation Validation store to add result to + * @param string $param Name of parameter of the custom validation result + * @param string $setting Name of setting of the custom validation result + * @param mixed $result Validation result + * @return mixed The altered validation store + */ + public function addValidationResult($validation, $param, $setting, $result) + { + if(!is_array($validation)) { + $validation = array(); + } + if(!array_key_exists($param, $validation)) { + $validation[$param] = array(); + } + $validation[$param][$setting] = $result; + + + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function loadModels() + { + // Determine Models + $explicit = false; + $models = \nre\core\ClassLoader::stripClassType(\nre\core\ClassLoader::stripNamespace(get_class($this))); + if(property_exists($this, 'models')) + { + $models = $this->models; + $explicit = true; + } + if(!is_array($models)) { + $models = array($models); + } + // Models of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('models', $properties)) { + $models = array_merge($models, $properties['models']); + } + } + + // Load Models + foreach($models as &$model) + { + try { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + catch(\nre\exceptions\ModelNotValidException $e) { + if($explicit) { + throw $e; + } + } + catch(\nre\exceptions\ModelNotFoundException $e) { + if($explicit) { + throw $e; + } + } + } + } + + + /** + * Load the View of this Controller. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of the current Layout + * @param string $action Current Action + */ + protected function loadView($layoutName, $action) + { + // Check Layout name + if(is_null($layoutName)) { + return; + } + + // Determine controller name + $controllerName = \nre\core\ClassLoader::getClassName(get_class($this)); + + + // Load view + $isToplevel = is_subclass_of($this->agent, '\nre\agents\ToplevelAgent'); + $this->view = View::loadAndFactory($layoutName, $controllerName, $action, $isToplevel); + } + + } + +?> diff --git a/core/Driver.inc b/core/Driver.inc new file mode 100644 index 00000000..eec59143 --- /dev/null +++ b/core/Driver.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Driver. + * + * @author coderkun + */ + abstract class Driver + { + + + + + /** + * Load the class of a Driver. + * + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @param string $driverName Name of the Driver to load + */ + public static function load($driverName) + { + // Determine full classname + $className = self::getClassName($driverName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\DriverNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\DriverNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Driver (Factory Pattern). + * + * @param string $driverName Name of the Driver to instantiate + */ + public static function factory($driverName, $config) + { + // Determine full classname + $className = self::getClassName($driverName); + + + // Construct and return Driver + return $className::singleton($config); + } + + + /** + * Determine the classname for the given Driver name. + * + * @param string $driverName Driver name to get classname of + * @return string Classname fore the Driver name + */ + private static function getClassName($driverName) + { + $className = ClassLoader::concatClassNames($driverName, ClassLoader::stripNamespace(get_class())); + + + return "\\nre\\drivers\\$className"; + } + + + + + /** + * Construct a new Driver. + */ + protected function __construct() + { + } + + } + +?> diff --git a/core/Exception.inc b/core/Exception.inc new file mode 100644 index 00000000..a17a700f --- /dev/null +++ b/core/Exception.inc @@ -0,0 +1,65 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Exception class. + * + * @author coderkun + */ + class Exception extends \Exception + { + + + + + /** + * Construct a new exception. + * + * @param string $message Error message + * @param int $code Error code + * @param string $name Name to insert + */ + function __construct($message, $code, $name=null) + { + parent::__construct( + $this->concat( + $message, + $name + ), + $code + ); + } + + + + + /** + * Insert the name in a message + * + * @param string $message Error message + * @param string $name Name to insert + */ + private function concat($message, $name) + { + if(is_null($name)) { + return $message; + } + + + return "$message: $name"; + } + + } + +?> diff --git a/core/Linker.inc b/core/Linker.inc new file mode 100644 index 00000000..00c5846b --- /dev/null +++ b/core/Linker.inc @@ -0,0 +1,311 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to create web links based on the current request. + * + * @author coderkun + */ + class Linker + { + /** + * Current request + * + * @var Request + */ + private $request; + + + + + /** + * Construct a new linker. + * + * @param Request $request Current request + */ + function __construct(\nre\requests\WebRequest $request) + { + $this->request = $request; + } + + + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as string + */ + public static function createLinkParam($param1) + { + return implode( + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + call_user_func_array( + '\nre\core\Linker::createLinkParams', + func_get_args() + ) + ); + } + + + /** + * Mask parameters to be used in an URL. + * + * @param string $param1 First parameter + * @return string Masked parameters as array + */ + public static function createLinkParams($param1) + { + // Parameters + $linkParams = array(); + $params = func_get_args(); + + foreach($params as $param) + { + // Mask special signs seperately + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, rawurlencode(rawurlencode($special)), $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = rawurlencode($param); + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..cabfa46b9f92800b5bc2981812c631cdd57e5e9c GIT binary patch literal 7459 zcmai%eT-d26~G6IT2_=V%aNQ`JBBx?)_8VrUYe^{hQ(D;ENl8|V`-DB6cm*`@9C(kbKML1Ue+hm9{twD`XMa>F9@VAr!>|B1z>QGGsX!V3 zFgzQ69i9#!a(n{vQ_niS07cJNAS$aj;fLVcQ0DnAlzz`*au>su@I1H!o)2$@A~y;7 zsqb(T{T_zW&rhJ3=t)<92J%zSaufZ314aJ#u6_)P{G0F;_z%cWy~AxeeBW_7N{e1+ z!pq>Lup91z@_ikygc+3i-{;{rT@BP0%cvMp^WzpD0+Plu7{7pUigY@U(QPz_dLhTpv<=y z+Hs-GZ#U$p>aKnS%KUx+MbBSD4PSt(;NPL>bvB(blseyWHI#X5hT^B!LFwma$WI+{ z^>0G4^V3lJc@>JiUW4L?e}xR8F2tB(zjcmRLh;9R3;B6kmzaqoqq|NT(Lc?^mkPeSp_UqG49%Z|t3b0;bF7L@*egfc7OGf?z>5lZ`O zQ0D(HDC@YK;1fHa0}HSSB@U`^J^ZTU&!EKbU!eHG`%vb08iUAsoeMX^iy>XB15o0q z1`$NunUU*tKg+@v*SU?PaWZQIs7gZ zzd8!(LOl<~p2wi*`)5~w8_GQ2g)+_xjAGXnie1-1k>3PmU9NNWK`84o3`On`yZ~0= z74XZBk3+G`t5EcM9m=|$ic!|Um5zI$=r;vL?;}w3xEsoP-v>p%A38n;W!xvBeD`xG z<2(n&j?Y7}@5@l^{sxrq-*bE)GG+B)l#%Z*f-=vQun+dYBD@VsJl_vRpU0ut|Cdna z{~{DUj=|ONEhu(4gPYi4t>bQ>J2FK|1XqzEN9VWomaqh za0~ntG_VKW<@hudJ&r@s^KVe%^gpiN!{#k^yvFf*D1I>l<-01B`QPE{Ux2dSUvc~z zlzD#(ioZS#rN5`3*!3tBeP^M}^LLP;)gK)H4MqM84nrC50!Wyu>nKB%0m?Rt*uF+- zUSm9Dlx38wDLy5nY@*ykkxTT7DPmW-WF5pta&4xRD5Fy0N^)3RxZY4kVsA~6Ymg%AaT}#b5g!siKACb8<#LL|{bwk0 zU715gj<}!o#|gNLvWilnSYLs1-A*y~NvRv)M#`sMy%YUZ8kAm2H-!*UpQp&RopKpP zVxV~q^RUlVE`bsc;_J<;z{3dT8rN{MqxiezT31md=Ea}o+DZ{$|D06%OI6=Ynz{+o zMD+(jG-b+q!Vip2ryGW*MkAP(hSbD~+EMjluf(I?6-QZPVS~OYGYLJl1w*fH=2f*9 z#KtR6YjfCND6C6Sn0kJgXg>%{#S3()IqLj=wI5D;fnT2Azn|zdigXZ#6}`Id9q!TH zW!p=#<^-ZiHHuSY{cv&FMjSOvocdU#JvFgVyDD#@D4_-Y{V+4S5qQ%+Dx{Il!XPTu zbXrBV#FS!_>i)Ln)eh{D8r|x;+2Ac~3e6Nfpr{v5H^rnaOH8Z=Okn7z_1@LpyQCER zjnt1qHCXo3wv??|nZY=YVrB2ToW-KN^@XevL|(b5yAw5d{LXLJJwMQ1xg48hVZ;2@ zo&P~=(RPZLnZqSx${59;F`A|F>sejdH&rjuQ?V?e*jXRH^MUT<_?-``-C-I><*Z~U zGUO#`?sIAgHxAU0KOT!^hN6le=65}Tx~43ZC`&=>lRc(Bj@5D`YOlEJ-cI{U#TsVV zOOmN5F01B4YZmQ>C7f=)(Bkek92*-gj!OnYQ>GaKt@=`03bBr{^M}~8%Qs>YBB8mvj6?zP zHIm7FbYYN;=3Xx`=pJQbGC`SoHOhiA;nnv2mcwXbD_{>BH@a-YqkL_ryew~9c6E2M z<^&&1;r)f-IFf*n4yLr5y?yq=WQy61X5)1~NyJ=YD>TbuYbnPi*R(0qT+>#4i8Y-k zv-p~hc}Y#ltreR|Rw@y^6PZ{hiGoSuV+`xfHEi21xJ*;4F+arsW3t+sZLwlEfw3rM zqi$9AVN^Tq=0j^>x8=9mm)h)GLd~Ju4NF*LzR=<(5vfESV4dtLs#~HgZf~~jiOVh& zMk(H7Le0_VPqI;$^|VQqmv7)@?WJ~SEO}vx9f-{x5r?uKpT-TegioS!|5Kbb{)`-Q zfeP4+l$=nCeK&S7qNh2V2X;W^@M&c6^`He zL{(*>6P}bKt%Mr5_3fsV7Iv2l`>_lC6}7m-kg1r^hW){=;Ty*a_MGBvVSp(X^@iTQ zjfLKgg}$qF-=^ZGE7$h+_4XoI7%`K+^s>0z##~PJ?!s71YtC+%= z@#-D*B9VJ&V6bb??mdHT1^aq>yX34Tbt#N-L=<&u4yWrmb^UN_e%zaM+rF`#g)MD4 z8E(SFg~6~CmD!kzdds+0*lZPc-c6)fUJ~1fr8?c z9ITSOdEpexij++_YBW{itkGyTNP_1Y3T}^U3EHS0ppQ8%c_!!MP6SG9d@{gAWWuyM z`$&+I3{_^22B}{$`k?VkRmNQ?&1umVJi+^!Ob573JI8a~tvRuLEsJN`d0tDtH5-Fg zZ7a5#F>~8kMje=aq#B_1XoK^|=qYAw;&nQ!>oUg^J4MA*jZado2jht-AdA8%GnuRo z3(#SAO|+$kJXcmMf6~Se`LH!K8A(R{2DXF_>AZ2f3A1#D)o6EP<$~FtG8Ub-1Ug!H zgEr%=g0jNnc-6SfphLW)ZKq+XvC6^n`t{tW=H-NYNU{q3HYGGRN$hM`%s+giB{iHQ zHgC}pvF=O8l4=&4gk<(1ySBEOMPZ}5O>( zXu~>O+{nTjsh~|6xBKIPACWuMT-G?ixcyb1p~ z74fF*HxjMG94a&O($AP33K^GzQW4j1d*VzM&mJw+*pp{OB(gDyqzOW`E01P^xpSAb z(PQj=0$-8rkFPkY^0jWe=9C}T0$h__ArHfp9V2G{B@u0Y(R7LSS2h)0;?q_(%62|u zx2=5gNxMG8HXttC5@QrTa%Yik}g1ib9A>}f3%=K&=VCo9vo>0FJ;WXadWarieP`(>;-$y3=MNugBd zb8~rG*6M6nYr%>C5 zQyH#HPK>ehZPBZd70cPLOq`!@TC%t9M0w-mv9ae+`+RawX&%TpYh5H;$5TgcOm=Gb zSC=14faMvo4P!i^RPAU;T~CSI;X~Uep`7m=G60o40@0MzPZAY5FRTV~c6TRwUo~Fx Q9noZCI8`=xu8}+cA1}eLOaK4? literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..b658c97b --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,605 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-16 15:53+0100\n" +"PO-Revision-Date: 2014-04-16 15:54+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/bossfight/html/quest.tpl:11 +#: questtypes/bossfight/html/quest.tpl:24 +#: questtypes/bossfight/html/submission.tpl:21 +#: questtypes/bossfight/html/submission.tpl:33 +msgid "lost" +msgstr "verloren" + +#: questtypes/bossfight/html/quest.tpl:41 +msgid "Choose" +msgstr "Wählen" + +#: questtypes/bossfight/html/quest.tpl:47 +#: questtypes/choiceinput/html/quest.tpl:14 +#: questtypes/crossword/html/quest.tpl:30 +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/crossword/html/quest.tpl:22 +#: questtypes/crossword/html/submission.tpl:22 +msgid "vertical" +msgstr "vertikal" + +#: questtypes/crossword/html/quest.tpl:24 +#: questtypes/crossword/html/submission.tpl:24 +msgid "horizontal" +msgstr "horizontal" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:46 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:48 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/achievements/index.tpl:7 views/html/characters/character.tpl:31 +#: views/html/seminarymenu/index.tpl:4 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/achievements/index.tpl:15 +msgid "Secret Achievement" +msgstr "Geheime Errungenschaft" + +#: views/html/achievements/index.tpl:19 +msgid "Continue playing to unlock this secret Achievement" +msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" + +#: views/html/charactergroups/group.tpl:7 +#: views/html/charactergroups/groupsgroup.tpl:7 +#: views/html/charactergroups/index.tpl:7 +#: views/html/characters/character.tpl:53 views/html/seminarymenu/index.tpl:3 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:18 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/charactergroups/group.tpl:20 +msgid "Members" +msgstr "Mitglieder" + +#: views/html/charactergroups/group.tpl:20 +msgid "Member" +msgstr "Mitglied" + +#: views/html/charactergroups/group.tpl:25 +#: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:15 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:40 +#: views/html/questgroups/questgroup.tpl:43 views/html/quests/index.tpl:7 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:17 +#: views/html/charactergroupsquests/quest.tpl:7 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:17 +msgid "Description" +msgstr "Beschreibung" + +#: views/html/charactergroupsquests/quest.tpl:20 +msgid "Rules" +msgstr "Regeln" + +#: views/html/charactergroupsquests/quest.tpl:27 +msgid "Won Quest" +msgstr "Gewonnene Quest" + +#: views/html/charactergroupsquests/quest.tpl:33 +msgid "Lost Quest" +msgstr "Verlorene Quest" + +#: views/html/characters/character.tpl:16 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:20 +#: views/html/characters/character.tpl:73 +#: views/html/characters/character.tpl:81 +#: views/html/characters/character.tpl:89 views/html/seminarybar/index.tpl:44 +#: views/html/users/user.tpl:29 +msgid "Level" +msgstr "Level" + +#: views/html/characters/character.tpl:39 views/html/seminarybar/index.tpl:28 +#, php-format +msgid "achieved at: %s" +msgstr "erhalten am: %s" + +#: views/html/characters/character.tpl:65 +msgid "Ranking" +msgstr "Ranking" + +#: views/html/characters/character.tpl:97 +msgid "Topic progress" +msgstr "Thematischer Fortschritt" + +#: views/html/characters/register.tpl:7 +msgid "Create Character" +msgstr "Charakter erstellen" + +#: views/html/characters/register.tpl:20 +#, php-format +msgid "Character name is too short (min. %d chars)" +msgstr "Der Charaktername ist zu kurz (min. %d Zeichen)" + +#: views/html/characters/register.tpl:22 +#, php-format +msgid "Character name is too long (max. %d chars)" +msgstr "Der Charaktername ist zu lang (max. %d Zeichen)" + +#: views/html/characters/register.tpl:24 +msgid "Character name contains illegal characters" +msgstr "Der Charaktername enthält ungültige Zeichen" + +#: views/html/characters/register.tpl:26 +msgid "Character name already exists" +msgstr "Der Charaktername existiert bereits" + +#: views/html/characters/register.tpl:28 +msgid "Character name invalid" +msgstr "Der Charaktername ist ungültig" + +#: views/html/characters/register.tpl:40 +msgid "Character properties" +msgstr "Charaktereigenschaften" + +#: views/html/characters/register.tpl:41 views/html/characters/register.tpl:42 +msgid "Character name" +msgstr "Charaktername" + +#: views/html/characters/register.tpl:43 +msgid "Character type" +msgstr "Charaktertyp" + +#: views/html/characters/register.tpl:54 +#, php-format +msgid "The Seminary field “%s” is invalid" +msgstr "Das Kursfeld „%s“ ist ungültig" + +#: views/html/characters/register.tpl:59 +msgid "Seminary fields" +msgstr "Kursfelder" + +#: views/html/characters/register.tpl:81 views/html/seminaries/create.tpl:14 +#: views/html/users/create.tpl:17 +msgid "create" +msgstr "erstellen" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 +#: views/html/menu/index.tpl:6 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:86 views/html/users/register.tpl:87 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:14 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:14 +msgid "register yourself" +msgstr "registriere dich" + +#: views/html/library/index.tpl:7 views/html/library/topic.tpl:7 +msgid "Questtopics" +msgstr "Themen" + +#: views/html/menu/index.tpl:2 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:3 views/html/seminaries/create.tpl:6 +#: views/html/seminaries/delete.tpl:6 views/html/seminaries/edit.tpl:6 +#: views/html/seminaries/index.tpl:1 +msgid "Seminaries" +msgstr "Kurse" + +#: views/html/menu/index.tpl:8 +msgid "Logout" +msgstr "Logout" + +#: views/html/quests/index.tpl:15 +msgid "Questgroup" +msgstr "Questgruppe" + +#: views/html/quests/index.tpl:21 +msgid "Questname" +msgstr "Questname" + +#: views/html/quests/index.tpl:24 +msgid "Questtype" +msgstr "Questtyp" + +#: views/html/quests/index.tpl:47 +msgid "Apply filters" +msgstr "Filter anwenden" + +#: views/html/quests/index.tpl:48 +msgid "Reset filters" +msgstr "Filter zurücksetzen" + +#: views/html/quests/quest.tpl:57 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/quest.tpl:63 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:66 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:79 views/html/quests/quest.tpl:88 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:10 +#, php-format +msgid "Submission of %s" +msgstr "Lösung von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht am %s um %s Uhr" + +#: views/html/seminaries/create.tpl:7 +msgid "New seminary" +msgstr "Neuer Kurs" + +#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 +#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 +msgid "Title" +msgstr "Titel" + +#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:11 +msgid "Delete seminary" +msgstr "Kurs löschen" + +#: views/html/seminaries/delete.tpl:9 +#, php-format +msgid "Should the seminary “%s” really be deleted?" +msgstr "Soll der Kurs „%s“ wirklich gelöscht werden?" + +#: views/html/seminaries/delete.tpl:11 views/html/users/delete.tpl:6 +msgid "delete" +msgstr "löschen" + +#: views/html/seminaries/delete.tpl:12 views/html/users/delete.tpl:7 +msgid "cancel" +msgstr "abbrechen" + +#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:10 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:14 views/html/users/edit.tpl:17 +msgid "save" +msgstr "speichern" + +#: views/html/seminaries/index.tpl:4 +msgid "Create new seminary" +msgstr "Neuen Kurs erstellen" + +#: views/html/seminaries/index.tpl:23 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminaries/index.tpl:25 +msgid "Create a Character" +msgstr "Erstelle einen Charakter" + +#: views/html/seminaries/index.tpl:27 +#, php-format +msgid "Your Character “%s” has not been activated yet" +msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" + +#: views/html/seminaries/seminary.tpl:12 +msgid "Show Quests" +msgstr "Quests anzeigen" + +#: views/html/seminarybar/index.tpl:14 +msgid "Last Quest" +msgstr "Letzter Speicherpunkt" + +#: views/html/seminarybar/index.tpl:48 +#, php-format +msgid "Show %s-Profile" +msgstr "%s-Profil anzeigen" + +#: views/html/seminarymenu/index.tpl:5 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:82 views/html/users/register.tpl:83 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +#: views/html/users/user.tpl:12 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: views/html/users/delete.tpl:2 views/html/users/user.tpl:6 +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:5 +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:10 +#, php-format +msgid "registered on %s" +msgstr "registriert am %s" + +#: views/html/users/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:14 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:16 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:18 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:20 +msgid "Username already exists" +msgstr "Der Benutzername existiert bereits" + +#: views/html/users/register.tpl:22 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:27 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:29 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:31 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:33 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:38 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:40 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:42 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:44 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:49 views/html/users/register.tpl:53 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:51 +msgid "E‑mail address already exists" +msgstr "E‑Mail-Adresse existiert bereits" + +#: views/html/users/register.tpl:58 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:60 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:62 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:89 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:11 +msgid "Name" +msgstr "Name" + +#: views/html/users/user.tpl:34 +msgid "Roles" +msgstr "Rollen" + +#~ msgid "Usergroups" +#~ msgstr "Benutzergruppen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..f25689b3 --- /dev/null +++ b/models/AchievementsModel.inc @@ -0,0 +1,508 @@ + + * @copyright 2014 Heinrich-Heine-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(); + } + + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all Achievements of a Seminary. + * + * @param int $seminaryId ID of Seminary to get Achievements of + * @return array Achievements data + */ + public function getAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? '. + 'ORDER BY achievements.pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ')', + 'ii', + $seminaryId, + $characterId + ); + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + + + return !empty($data); + } + + } + +?> diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..115d1fb2 --- /dev/null +++ b/models/AvatarsModel.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 Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..e042fd10 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,197 @@ + + * @copyright 2014 Heinrich-Heine-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, preferred '. + '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, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $groupsgroupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupUrl); + } + + + return $data[0]; + } + + + /** + * Get a Character groups-group by its ID. + * + * @throws IdNotFoundException + * @param string $groupsgroupId ID of the Character groups-group + * @return array Character groups-group data + */ + public function getGroupsgroupById($groupsgroupId) + { + $data = $this->db->query( + 'SELECT id, name, url, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE id = ?', + 'i', + $groupsgroupId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupId); + } + + + 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, motto '. + '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, motto '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + } + +?> diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc new file mode 100644 index 00000000..4490168d --- /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, xps '. + '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..7b27da8d --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,471 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_id, seminaries.url 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, characters.avatar_id, 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.xplevel, characters.avatar_id, 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. + 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. + 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * Get the superior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of superior Characters + */ + public function getSuperiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps > ? '. + 'ORDER BY characters.xps ASC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * Get the inferior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of inferior Characters + */ + public function getInferiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps < ? '. + 'ORDER BY characters.xps DESC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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, seminarymedia.url AS avatar_url, seminarymedia.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.seminarymedia_id = avatars.avatarpicture_id '. + 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Check if a Character name already exists. + * + * @param string $name Character name to check + * @return boolean Whether Character name exists or not + */ + public function characterNameExists($name) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM characters '. + 'WHERE name = ? OR url = ?', + 'ss', + $name, + \nre\core\Linker::createLinkParam($name) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + + } + +?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> 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..2c2d5eae --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its URL. + * + * @throws IdNotFoundException + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $seminaryMediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its ID. + * + * @throws IdNotFoundException + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE 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..3e2ced21 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,589 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..5b7eda91 --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,410 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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 quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all Quests for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array Quests for this Seminary + */ + public function getQuestsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM questgroups '. + 'INNER JOIN quests ON quests.questgroup_id = questgroups.id '. + 'WHERE questgroups.seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get all Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return array Quests for this Questtopic + */ + public function getQuestsForQuesttopic($questtopicId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_questsubtopics '. + 'INNER JOIN quests ON quests.id = quests_questsubtopics.quest_id '. + 'WHERE quests_questsubtopics.questsubtopic_id = ?', + 'i', + $questtopicId + ); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..1fd82319 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,114 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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/QuesttopicsModel.inc b/models/QuesttopicsModel.inc new file mode 100644 index 00000000..abd246f7 --- /dev/null +++ b/models/QuesttopicsModel.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\models; + + + /** + * Model to interact with Questtopics-table. + * + * @author Oliver Hanraths + */ + class QuesttopicsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttopicsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get a Questtopic by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + * @return array Questtopic data + */ + public function getQuesttopicByUrl($seminaryId, $questtopicUrl) + { + $data = $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $questtopicUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questtopicUrl); + } + + + return $data[0]; + } + + + /** + * Get all Questtopics for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questtopics + */ + public function getQuesttopicsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get count of Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return int Count of Quests + */ + public function getQuestCountForQuesttopic($questtopicId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_questsubtopics.quest_id) AS c ' . + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'WHERE questsubtopics.questtopic_id = ?', + 'i', + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get count of Quests that are linked to a Questtopic and are + * unlocked by a Character. + * + * @param int $questtopicId ID of Questtopic + * @param int $characterId ID of Character + * @return int Count of Quests + */ + public function getCharacterQuestCountForQuesttopic($questtopicId, $characterId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_characters.quest_id) AS c '. + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'INNER JOIN quests_characters ON quests_characters.quest_id = quests_questsubtopics.quest_id AND quests_characters.character_id = ? AND quests_characters.status = 3 '. + 'WHERE questsubtopics.questtopic_id = ?', + 'ii', + $characterId, + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get all Questsubtopics for a Quest. + * + * @param int $questId ID of Quest + * @return array List of Questsubtopics + */ + public function getQuestsubtopicsForQuest($questId) + { + return $this->db->query( + 'SELECT DISTINCT id, questtopic_id, title, url '. + 'FROM quests_questsubtopics '. + 'INNER JOIN questsubtopics ON questsubtopics.id = quests_questsubtopics.questsubtopic_id '. + 'WHERE quests_questsubtopics.quest_id = ?', + 'i', + $questId + ); + } + + } + +?> 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..251ade4f --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, seminarymedia_id '. + '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, seminarymedia_id '. + '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, seminarymedia_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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..efad3bde --- /dev/null +++ b/models/SeminarycharacterfieldsModel.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 the Seminarycharacterfields-tables. + * + * @author Oliver Hanraths + */ + class SeminarycharacterfieldsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new SeminarycharacterfieldsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..c773a0b5 --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,370 @@ + + * @copyright 2014 Heinrich-Heine-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 users with the given user role. + * + * @param string $userrole User role + * @return array List of users + */ + public function getUsersWithRole($userrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + 'FROM users '. + 'LEFT JOIN users_userroles ON users_userroles.user_id = users.id '. + 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '. + 'WHERE userroles.name = ? '. + 'ORDER BY username ASC', + 's', + $userrole + ); + } + + + /** + * Get users with the given user Seminary role. + * + * @param int $seminaryId ID of Seminary + * @param string $userseminaryrole User Seminary role + * @return array List of users + */ + public function getUsersWithSeminaryRole($seminaryId, $userseminaryrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + '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_userseminaryroles.seminary_id = ? AND userseminaryroles.name = ? '. + 'ORDER BY username ASC', + 'is', + $seminaryId, $userseminaryrole + ); + } + + + /** + * 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; + } + + + /** + * Check if an username already exists. + * + * @param string $username Username to check + * @return boolean Whether username exists or not + */ + public function usernameExists($username) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE username = ? OR url = ?', + 'ss', + $username, + \nre\core\Linker::createLinkParam($username) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Check if an e‑mail address already exists. + * + * @param string $email E‑mail address to check + * @return boolean Whether e‑mail address exists or not + */ + public function emailExists($email) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE email = ?', + 's', + $email + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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/bossfight/BossfightQuesttypeAgent.inc b/questtypes/bossfight/BossfightQuesttypeAgent.inc new file mode 100644 index 00000000..6d99ec44 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeAgent.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 a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeController.inc b/questtypes/bossfight/BossfightQuesttypeController.inc new file mode 100644 index 00000000..366aabc8 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeController.inc @@ -0,0 +1,244 @@ + + * @copyright 2014 Heinrich-Heine-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 BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeController 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) + { + // Prepare session + $this->prepareSession($quest['id']); + + // Remove previous answers + $this->Bossfight->clearCharacterSubmissions($quest['id'], $character['id']); + + // Save answers + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) { + $this->Bossfight->setCharacterSubmission($stage['id'], $character['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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + + // Prepare session + $this->prepareSession($quest['id']); + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + + return ($lives['boss'] == 0 && $lives['character'] > 0); + } + + + /** + * Action: quest. + * + * Display a stage with a text and the answers for the following + * stages. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Prepare session + $this->prepareSession($quest['id']); + + // Get Stage + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit_stages'))) + { + $stages = $this->request->getPostParam('submit_stages'); + $stageId = array_keys($stages)[0]; + $stage = $this->Bossfight->getStageById($stageId); + } + else + { + $_SESSION['quests'][$quest['id']]['stages'] = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + } + + // Store Stage in session + if(count($_SESSION['quests'][$quest['id']]['stages']) == 0 || $_SESSION['quests'][$quest['id']]['stages'][count($_SESSION['quests'][$quest['id']]['stages'])-1]['id'] != $stage['id']) { + $_SESSION['quests'][$quest['id']]['stages'][] = $stage; + } + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + // Get Child-Stages + $childStages = $this->Bossfight->getChildStages($stage['id']); + + // Get answer of Character + if($this->request->getGetParam('show-answer') == 'true') { + foreach($childStages as &$childStage) { + $childStage['answer'] = $this->Bossfight->getCharacterSubmission($childStage['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stage', $stage); + $this->set('lives', $lives); + $this->set('childStages', $childStages); + } + + + /** + * Action: submission. + * + * Display all stages with the answers the character has + * choosen. + * + * @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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Get stages + $stages = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + while(!is_null($stage)) + { + $stages[] = $stage; + + $childStages = $this->Bossfight->getChildStages($stage['id']); + $stage = null; + foreach($childStages as &$childStage) + { + if($this->Bossfight->getCharacterSubmission($childStage['id'], $character['id'])) + { + $stage = $childStage; + break; + } + } + } + + // Calculate lives + $stages[0]['lives'] = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + for($i=1; $i $stages[$i-1]['lives']['character'] + $stages[$i]['livedrain_character'], + 'boss' => $stages[$i-1]['lives']['boss'] + $stages[$i]['livedrain_boss'], + ); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stages', $stages); + } + + + + + /** + * Prepare the session to store stage information in + * + * @param int $questId ID of Quest + */ + private function prepareSession($questId) + { + if(!array_key_exists('quests', $_SESSION)) { + $_SESSION['quests'] = array(); + } + if(!array_key_exists($questId, $_SESSION['quests'])) { + $_SESSION['quests'][$questId] = array(); + } + if(!array_key_exists('stages', $_SESSION['quests'][$questId])) { + $_SESSION['quests'][$questId]['stages'] = array(); + } + } + + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeModel.inc b/questtypes/bossfight/BossfightQuesttypeModel.inc new file mode 100644 index 00000000..a05927f0 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeModel.inc @@ -0,0 +1,183 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\questtypes; + + + /** + * Model of the BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get a Boss-Fight. + * + * @throws IdNotFoundException + * @param int $questId ID of Quest + * @return array Boss-Fight data + */ + public function getBossFight($questId) + { + $data = $this->db->query( + 'SELECT bossname, boss_seminarymedia_id, lives_character, lives_boss '. + 'FROM questtypes_bossfight '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Stage to begin the Boss-Fight with. + * + * @param int $questId ID of Quest + * @return array Data of first Stage + */ + public function getFirstStage($questId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ? AND parent_stage_id IS NULL', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get a Stage by its ID. + * + * @param int $stageId ID of Stage + * @return array Stage data or null + */ + public function getStageById($stageId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE id = ?', + 'i', + $stageId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get the follow-up Stages for a Stage. + * + * @param int $parentStageId ID of Stage to get follow-up Stages for + * @return array List of follow-up Stages + */ + public function getChildStages($parentStageId) + { + return $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE parent_stage_id = ?', + 'i', + $parentStageId + ); + } + + + /** + * Reset all Character submissions of a Boss-Fight. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + */ + public function clearCharacterSubmissions($questId, $characterId) + { + $this->db->query( + 'DELETE FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id IN ('. + 'SELECT id '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ?'. + ') AND character_id = ?', + 'ii', + $questId, + $characterId + ); + } + + + /** + * Save Character’s submitted answer for one Boss-Fight-Stage. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + */ + public function setCharacterSubmission($stageId, $characterId) + { + $this->db->query( + 'INSERT INTO questtypes_bossfight_stages_characters '. + '(questtypes_bossfight_stage_id, character_id) '. + 'VALUES '. + '(?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_bossfight_stage_id = ?', + 'iii', + $stageId, $characterId, $stageId + ); + } + + + /** + * Get answer of one Boss-Fight-Stage submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return boolean Stage taken + */ + public function getCharacterSubmission($stageId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_bossfight_stage_id '. + 'FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id = ? AND character_id = ? ', + 'ii', + $stageId, $characterId + ); + + + return (!empty($data)); + } + + } + +?> diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl new file mode 100644 index 00000000..9ed95f81 --- /dev/null +++ b/questtypes/bossfight/html/quest.tpl @@ -0,0 +1,51 @@ +
                +
                +

                +

                +

                + 0) : ?> + + + + + + +

                +
                +
                +

                +

                +

                + 0) : ?> + + + + + + +

                +
                +
                + +

                + + + +
                  + +
                • +

                  + + +

                  +

                  +
                • + + +
                • + + +
                • + +
                + diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl new file mode 100644 index 00000000..260d28b0 --- /dev/null +++ b/questtypes/bossfight/html/submission.tpl @@ -0,0 +1,38 @@ +
                +
                +

                +
                +
                +

                +
                +
                + + +

                +
                +
                +

                +

                + 0) : ?> + + + + + + +

                +
                +
                +

                +

                + 0) : ?> + + + + + + +

                +
                +
                + diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..08029501 --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
                + &$text) : ?> + 0) : ?> + + + + + +

                + +
                diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..200b3c7f --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
                + &$text) : ?> + 0) : ?> + + + + +
                diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..ba3e5a0c --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,351 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..d202eb8f --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,49 @@ +
                + + + + + + + + + + +
                + + 0) : ?> + + +
                +
                  + +
                1. + + : + + : + + +
                2. + +
                + +
                + diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..c47e414d --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,48 @@ +
                + + + + + + + + + + +
                + + 0) : ?> + + +
                +
                  + +
                1. + + : + + : + + +
                2. + +
                +
                + 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..90843218 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Match drops with user answers + foreach($drops as &$drop) + { + if(!array_key_exists($drop['id'], $answers) || intval(substr($answers[$drop['id']], 4)) !== $drop['questtypes_dragndrop_drag_id']) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..1e772edf --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,146 @@ + + * @copyright 2014 Heinrich-Heine-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 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..5a5d3717 --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                + +
                + + + +
                + +
                + +
                + + + +
                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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..456db617 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,261 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + var_dump($userAnswers); + var_dump($answers); + + // Match answers with user answers + foreach($answers as &$answer) + { + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..c2c32595 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                +
                + +

                +
                  + &$answer) : ?> +
                1. + /> + +
                2. + +
                +
                + + + + + + + +
                diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ae14f199 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                  + &$question) : ?> +
                1. +

                  +
                    + +
                  1. + + × + +
                  2. + +
                  +
                2. + +
                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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..0c72c1de --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                + + +
                +
                + : +
                  + +
                • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                • + +
                + +
                + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                + + + + + + + +
                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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.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/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..fdaf2d7c --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,33 @@ + +
                + +
                + +

                +

                + +
                  + +
                • + + + +

                  + + + + + +
                  + + +
                  +
                  + +
                  +

                   %

                  +
                  + +
                • + +
                diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..ba53c97b --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,52 @@ + +
                + +
                + +

                +

                +

                +
                +
                + +
                +
                +

                + +
                +
                  +
                • .
                • +
                •  XPs
                • +
                • 1) ? _('Members') : _('Member')?>
                • +
                +
                + +
                +

                +
                  + +
                • + +

                  + +

                  +

                  XP

                  +
                • + +
                +
                + +
                +

                +
                  + +
                • +

                  + format(new \DateTime($quest['created']))?> + + / XP +

                  +
                • + +
                +
                diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..93aeaba7 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,22 @@ + +
                + +
                + +

                +

                +

                + + + + +

                +
                  + +
                • + +
                diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..e0f54b94 --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,13 @@ + +
                + +
                + +

                +

                + +
                  + +
                • + +
                diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..8112a913 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,51 @@ + +
                + +
                + +

                +

                +

                +

                + + + + + +
                +

                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..74ba7cae --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,109 @@ + +
                + +
                + +

                +

                +

                + +
                +
                +
                +
                + +
                +

                :  %

                +
                +
                +

                +

                +
                +
                +

                +

                XP

                +
                +
                +

                .

                +

                +
                + +

                +
                  + +
                • + + + +

                  +

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

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

                +
                  + +
                • + +

                   XPs

                  +
                • + +
                +
                + +
                +

                +
                  + &$rankCharacter) : ?> +
                • + + + +

                  .

                  +

                  ( XPs)

                  +
                • + +
                • + + + +

                  .

                  +

                  ( XPs)

                  +
                • + &$rankCharacter) : ?> +
                • + + + +

                  .

                  +

                  ( XPs)

                  +
                • + +
                +
                +
                + +
                +

                +
                  + +
                • +

                  (/)

                  +
                  + +
                  +
                • + +
                +
                + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..a1227a5a --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,19 @@ + +
                + +
                + +

                +

                + +
                  + +
                • + +

                  + +

                  +

                  XP

                  +
                • + +
                diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..d62c0d35 --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,82 @@ + +
                + +
                + +

                +

                + +
                + +
                  + &$settings) : ?> +
                • +
                    + $value) : ?> +
                  • + +
                  • + +
                  +
                • + +
                + +
                + + +
                + + +
                + + +
                  + &$settings) : ?> +
                • + +
                + +
                + + + + + required="required"/> + + + + + +
                + +
                + +
                diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..0f68bc07 --- /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..9cfcf8f3 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,58 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                + +
                +
                + +
                + + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..3dc754ae --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                +

                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/library/index.tpl b/views/html/library/index.tpl new file mode 100644 index 00000000..be5b441d --- /dev/null +++ b/views/html/library/index.tpl @@ -0,0 +1,21 @@ + +
                + +
                + +

                +

                + +
                  + +
                • +

                  +
                  +
                  + +
                  +

                  /

                  +
                  +
                • + +
                diff --git a/views/html/library/topic.tpl b/views/html/library/topic.tpl new file mode 100644 index 00000000..be396d4f --- /dev/null +++ b/views/html/library/topic.tpl @@ -0,0 +1,28 @@ + +
                + +
                + +

                +

                +

                + +
                +
                + +
                +

                /

                +
                + +
                  + +
                • + +
                    + +
                  • + +
                  +
                • + +
                diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl new file mode 100644 index 00000000..c2662789 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
              • The Legend of Z
              • + 0) : ?>
              • +
              • + + +
              • + +
              • + diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..4d3ce2b9 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
                + +
                + +

                + + + + +

                :

                + +

                + + +

                + + + + + 0) : ?> +

                +
                  + +
                • + +
                  +
                  +

                  Fortschritt:

                  +
                  + +
                  +

                  / XP

                  +
                  +
                • + +
                + + + + + +

                + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl new file mode 100644 index 00000000..80a3e4fe --- /dev/null +++ b/views/html/quests/index.tpl @@ -0,0 +1,49 @@ + +
                + +
                + +

                +

                + +
                + + + + + + + + + + + + + + + + + + + + + +
                + + + + XPs
                + + + +
                diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl new file mode 100644 index 00000000..cbb21d25 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,104 @@ + +
                + +
                + +

                + +

                + + 0) : ?> +
                +

                + + + +
                + + +

                + + + + + + + +

                + 0 || !empty($questtext['abort_text'])) : ?> +
                  + +
                • + + +
                • + +
                + + +
                +
                + + + +
                + +

                + +

                + +

                +
                + + + +
                + +

                +

                + + + + +

                :

                + + +
                + + + +
                + 0) : ?> +
                  + + +
                • + : + + + + + +
                • + +
                • + : + + + + + +
                • + + +
                + + : + + Spiel vorbei + +
                + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..10fde93a --- /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..e2a17579 --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,41 @@ + +
                + +
                + +

                + +

                + + + + + +
                +

                +
                  + +
                • + +
                • + +
                + +

                +
                  + +
                • + +
                • + +
                + +

                +
                  + +
                • + +
                • + +
                +
                diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..30938357 --- /dev/null +++ b/views/html/seminaries/create.tpl @@ -0,0 +1,15 @@ + +
                + +
                + +

                +

                + +
                +
                + +
                +
                + +
                diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl new file mode 100644 index 00000000..8e53fdb5 --- /dev/null +++ b/views/html/seminaries/delete.tpl @@ -0,0 +1,13 @@ + +
                + +
                + +

                +

                + + +
                + + +
                diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl new file mode 100644 index 00000000..66d7efcd --- /dev/null +++ b/views/html/seminaries/edit.tpl @@ -0,0 +1,15 @@ + +
                + +
                + +

                +

                + +
                +
                + +
                +
                + +
                diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..986991a9 --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,33 @@ +

                + 0) : ?> + + +
                  + +
                • + + + +

                  + 0) : ?> + + + + +

                  +
                  + +
                  + 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..28488018 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,42 @@ + +
                + +
                + + +

                + 0) : ?> + + +

                + + +

                + + diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..60752cc7 --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,50 @@ +
                +

                + + +
                + + +
                +

                +

                +
                + + + +
                +

                +
                  +
                • + + + +

                  +

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

                  +
                • +
                +
                + + +
                + +

                +
                  + +
                • + + + +

                  +

                  ( XPs)

                  +
                • + +
                +

                + +
                diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..041c4f43 --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,6 @@ +
              • + 0) : ?>
              • +
              • +
              • +
              • + 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..d4f38bb7 --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                +

                + +
                +
                + +
                + +
                + +
                + +
                + +
                +
                + +
                diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..56ba5d7b --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                +

                + +
                +
                + +
                + +
                + +
                + +
                + +
                +
                + +
                diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..5d2883af --- /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..f141f4cd --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                + +

                + +

                .

                + +
                +
                + +
                + +
                +
                + +
                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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..29843569 --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,90 @@ +

                + +

                + +
                  + &$settings) : ?> +
                • +
                    + $value) : ?> +
                  • + getMessage(); + break; + } ?> +
                  • + +
                  +
                • + +
                + +
                +
                + + />
                + + />
                + + />
                + + />
                + + />
                +
                + +
                diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..745097b0 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,35 @@ +

                +

                + 0) : ?> + + +

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

                + +

                +
                  + +
                • + +

                  + +

                  + 0) : ?> + + + + +

                  +

                  +
                • + +
                + +

                + 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..a882b22f --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,270 @@ +@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%;margin-top:30px} +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:1px 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:87.5%} +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:0;margin:0;padding:0} +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} +a:hover{color:#5bbac2} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} +aside{display:none} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.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;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + + +/** Login & Registration **/ + +.logreg{width:auto;display:inline-block;padding:15px 20px;background:#eae8e4;border-radius:3px} +.logreg label{display:block;font-size:.875em} +.logreg input{margin:5px 0 15px} +.logreg .cta{display:block} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup Profile **/ + +.gdata{margin-top:20px} +.gdata img{border-radius:3px;margin-bottom:15px} +.gdata h1{margin:0;padding:0} +.gdata .fa{color:#aca8a1} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + + +/** Quest Types **/ + +.crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} +.crossword td{background:#d7d4cf;padding:1px} +.crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} +.crossword ol{list-style-type:decimal;margin-left:25px} +.crossword li{margin-top:20px} +.crossword .index{position:absolute;font-size:0.625em;margin:-2px 0 0 2px} + +.opponent{width:50%;float:left} +.opponent .portrait{height:250px;overflow:hidden;margin-bottom:15px} +.opponent .hero{background:#fff;max-width:130px} +.opponent .boss{max-width:100%} +.opponent p{text-align:center} +.opponent .fa{font-size:1.25em;color:#c7135b;padding-right:0} +.bossfight .option{background:#fff;margin-bottom:10px;padding:15px 15px 5px;border-radius:3px} + + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gdata .gbanner{float:left;min-width:100px} +.gdata .gdesc{float:left;width:75%} +.gdata ul{clear:both} +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} + +.opponent .hero{max-width:200px} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gdata{margin-top:40px} +.gdata .gdesc{width:60%} +.gdata li{font-size:.875em;padding-bottom:5px} +.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{float:right} + +.bossfight li{float:left;width:44%} +.bossfight li:nth-child(even){float:right} +.bossfight input,.bossfight p{text-align:center} +} + +@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 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px} +.breadcrumbs li{display:inline;padding-right:10px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} +article{background:#f7f5f2;float:left} +.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic img{width:100%} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} +aside .cranks li:nth-child(odd){background:#f7f5f2} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Wed, 16 Apr 2014 17:41:44 +0200 Subject: [PATCH 247/340] bigger margin to top for first menu item when logged out --- www/css/desktop.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/css/desktop.css b/www/css/desktop.css index a882b22f..89a656d9 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -238,10 +238,10 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo @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%} +header nav{margin:30px 10% 0} menu{display:block;opacity:1;position:relative} menu a{padding:10px 0} -#profile{float:none;margin:30px 0 15px 12px} +#profile{float:none;margin:0 0 15px 12px} #toggle,.toggle{display:none} .wrap{margin:0 0 0 300px} article{padding:20px 40px 80px 40px} From fbd87907409fc756820de430057165b4aa86d580 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 16 Apr 2014 21:33:03 +0200 Subject: [PATCH 248/340] small lost design update for boss fights --- questtypes/bossfight/html/quest.tpl | 2 +- questtypes/bossfight/html/submission.tpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl index 9ed95f81..d3abfabb 100644 --- a/questtypes/bossfight/html/quest.tpl +++ b/questtypes/bossfight/html/quest.tpl @@ -21,7 +21,7 @@ - +

                diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl index 260d28b0..7f145cec 100644 --- a/questtypes/bossfight/html/submission.tpl +++ b/questtypes/bossfight/html/submission.tpl @@ -30,7 +30,7 @@ - +

                From 6822d8c1ddb20c610601e952a9656d198edab285 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Apr 2014 00:11:18 +0200 Subject: [PATCH 249/340] achievements design --- views/html/achievements/index.tpl | 69 ++++++++++++++++++++++++++----- www/css/desktop.css | 24 +++++++++++ 2 files changed, 83 insertions(+), 10 deletions(-) diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index fdaf2d7c..cee00f82 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -4,28 +4,77 @@

                -

                +

                -
                  +
                  +
                  +

                  Die seltensten Errungenschaften

                  +
                    +
                  1. + +

                    Des Königs neue Kleider

                    +

                    wurde erst 5 mal gefunden

                    +
                  2. +
                  3. + +

                    Sein oder Sterben

                    +

                    wurde erst 7 mal gefunden

                    +
                  4. +
                  5. + +

                    Hungersnot? Ohne mich.

                    +

                    wurde erst 9 mal gefunden

                    +
                  6. +
                  +
                  + +
                  +

                  Die erfolgreichsten Sammler

                  +
                    +
                  1. + +

                    VHS

                    +

                    hat 15 Errungenschaften gefunden

                    +
                  2. +
                  3. + +

                    Betamax

                    +

                    hat 14 Errungenschaften gefunden

                    +
                  4. +
                  5. + +

                    Laserdisc

                    +

                    hat 12 Errungenschaften gefunden

                    +
                  6. +
                  +
                  +
                  + +

                  Persönlicher Fortschritt: 75%

                  +

                  12. Platz: Du hast bislang 13 von insgesamt 75 Errungenschaften erreicht.

                  +
                    +
                  • + +

                    Freigeschaltetes Achievementerreicht am 17.06.104

                    +

                    Das Bild ist entsprechend farbig, ein eventueller Fortschrittsbalken mit 100% soll entfallen.

                    +
                  • -
                  • +
                  • -

                    +

                    erreicht am 17.06.104

                    - +

                    - +

                    -
                    - -
                    +
                    -

                     %

                    +

                    %

                  • diff --git a/www/css/desktop.css b/www/css/desktop.css index 89a656d9..9226aa06 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -181,6 +181,23 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gquests .xp{display:block} +/** Achievements **/ + +.rare ol{margin-bottom:40px} +.rare li{margin-bottom:15px} +.rare img{float:left;width:45px;height:45px;margin:4px 10px 0 0;border-radius:3px} +.rare p{margin:0} + +.achmnts li{background:#fff;margin-bottom:10px;padding:15px;border-radius:3px} +.achmnts img{width:55px;height:55px;float:left;margin-right:15px;border-radius:3px} +.achmnts p{margin:0 0 4px} +.achmnts .unlcked{margin-top:3px;font-size:.875em;font-weight:normal;display:block} +.achmnts .desc{font-size:.875em;padding-top:10px} +.achmnts .prgrss{margin-top:15px} +.achmnts .xpbar{margin:8px 0 0 0;width:80%} +.achmnts .xpnumeric{margin:0} + + /** Quest Types **/ .crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} @@ -216,6 +233,8 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gchars li{width:32%;margin-right:5px} .gchars li:nth-child(even){float:left} +.achmnts .xpbar{width:89%} + .opponent .hero{max-width:200px} } @@ -231,6 +250,11 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gquests .date{display:inline;margin-right:15px} .gquests .xp{float:right} +.rare,.hunter{float:left;width:49%} +.hunter{float:right} +.achmnts .desc{padding-top:0} +.achmnts .unlcked{font-size:.75em;float:right;margin:-6px -4px 0 0;color:#878787} + .bossfight li{float:left;width:44%} .bossfight li:nth-child(even){float:right} .bossfight input,.bossfight p{text-align:center} From 9a906e2fd6dbe5db74a1b01a923d5cca570375b6 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 00:13:25 +0200 Subject: [PATCH 250/340] handle Avatar pictures by MediaAgent --- configs/AppConfig.inc | 18 +++++ controllers/CharactersController.inc | 26 ------- controllers/MediaController.inc | 88 ++++++++++++++++++++---- controllers/SeminarybarController.inc | 10 --- models/AvatarsModel.inc | 30 ++++++++ models/CharactersModel.inc | 44 +++--------- questtypes/bossfight/html/quest.tpl | 2 +- questtypes/bossfight/html/submission.tpl | 2 +- views/binary/media/avatar.tpl | 1 + views/html/characters/character.tpl | 16 ++--- views/html/characters/index.tpl | 4 +- views/html/seminarybar/index.tpl | 6 +- 12 files changed, 145 insertions(+), 102 deletions(-) create mode 100644 views/binary/media/avatar.tpl diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 8128f4d2..841903c6 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -69,6 +69,24 @@ ); + /** + * Media sizes + * + * @static + * @var array + */ + public static $media = array( + 'questgroup' => array( + 'width' => 480, + 'height' => 5000 + ), + 'avatar' => array( + 'width' => 500, + 'height' => 500 + ) + ); + + /** * Miscellaneous settings * diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 8f7e02cf..1adab0d3 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -75,12 +75,6 @@ { // Level $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); - - // Avatar - $avatar = $this->Avatars->getAvatarById($character['avatar_id']); - if(!is_null($avatar['small_avatarpicture_id'])) { - $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); - } } @@ -128,26 +122,6 @@ 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']), 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']) ); - foreach($ranking['superior'] as &$rankCharacter) - { - if(!is_null($rankCharacter['avatar_id'])) - { - $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); - if(!is_null($avatar['small_avatarpicture_id'])) { - $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); - } - } - } - foreach($ranking['inferior'] as &$rankCharacter) - { - if(!is_null($rankCharacter['avatar_id'])) - { - $avatar = $this->Avatars->getAvatarById($rankCharacter['avatar_id']); - if(!is_null($avatar['small_avatarpicture_id'])) { - $rankCharacter['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); - } - } - } // Get Quest topics $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc index 16e01cbb..e6bb69d2 100644 --- a/controllers/MediaController.inc +++ b/controllers/MediaController.inc @@ -26,9 +26,10 @@ */ public $permissions = array( 'index' => array('admin', 'moderator', 'user', 'guest'), - 'seminaryheader' => array('admin', 'moderator', 'user', 'guest'), - 'seminary' => array('admin', 'moderator', 'user', 'guest'), - 'achievement' => array('admin', 'moderator', 'user', 'guest') + 'seminaryheader' => array('admin', 'moderator', 'user'), + 'seminary' => array('admin', 'moderator', 'user'), + 'avatar' => array('admin', 'moderator', 'user'), + 'achievement' => array('admin', 'moderator', 'user') ); /** * User seminary permissions @@ -44,7 +45,7 @@ * * @var array */ - public $models = array('seminaries', 'achievements', 'media'); + public $models = array('seminaries', 'achievements', 'media', 'avatars'); @@ -143,7 +144,56 @@ // Get file $file = $this->getMediaFile($media, $action); - if(is_null($media)) { + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: avatar. + * + * Display an Avatar as full size or portrait. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $charactertypeUrl URL-title of Character type + * @param int $xplevel XP-level + * @param string $action Size to show (avatar or portrait) + */ + public function avatar($seminaryUrl, $charactertypeUrl, $xplevel, $action='avatar') + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Avatar + $avatar = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $charactertypeUrl, $xplevel); + + // Get media + switch($action) + { + case null: + case 'avatar': + $media = $this->Media->getSeminaryMediaById($avatar['avatarpicture_id']); + $file = $this->getMediaFile($media, 'avatar'); + break; + case 'portrait': + $media = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + $file = $this->getMediaFile($media); + break; + default: + throw new \nre\exceptions\ParamsNotValidException($action); + break; + } + + // Get file + if(is_null($file)) { return; } @@ -287,10 +337,19 @@ $file = self::resizeImage( $media['filename'], $format, - 480 + \nre\configs\AppConfig::$media['questgroup']['width'], + \nre\configs\AppConfig::$media['questgroup']['height'] ); } break; + case 'avatar': + $file = self::resizeImage( + $media['filename'], + $format, + \nre\configs\AppConfig::$media['avatar']['width'], + \nre\configs\AppConfig::$media['avatar']['height'] + ); + break; default: throw new ParamsNotValidException($action); break; @@ -321,12 +380,14 @@ * * @param string $fileName Absolute pathname of image to resize * @param string $mimeType Mimetype of target image - * @param int $size New size to resize to + * @param int $width Max. width to resize to + * @param int $height Max. height to resize to + * @return mixed Resized image */ - private static function resizeImage($fileName, $mimeType, $size) + private static function resizeImage($fileName, $mimeType, $width, $height) { // Read image from cache - $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$size; + $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$width.'x'.$height; if(file_exists($tempFileName)) { // Check age of file @@ -346,12 +407,15 @@ // Calculate new size $geometry = $im->getImageGeometry(); - if($geometry['width'] < $size) { - $size = $geometry['width']; + if($geometry['width'] < $width) { + $width = $geometry['width']; + } + if($geometry['height'] < $height) { + $height = $geometry['width']; } // Process - $im->thumbnailImage($size, 5000, true); + $im->thumbnailImage($width, $height, true); $im->contrastImage(1); $im->setImageFormat($mimeType); diff --git a/controllers/SeminarybarController.inc b/controllers/SeminarybarController.inc index 5ab1567b..88c1037e 100644 --- a/controllers/SeminarybarController.inc +++ b/controllers/SeminarybarController.inc @@ -68,16 +68,6 @@ if($groupsgroup['preferred']) { $group['members'] = $this->Characters->getCharactersForGroup($group['id']); - foreach($group['members'] as &$member) - { - if(!is_null($member['avatar_id'])) - { - $avatar = $this->Avatars->getAvatarById($member['avatar_id']); - if(!is_null($avatar['small_avatarpicture_id'])) { - $member['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); - } - } - } $characterGroups[] = $group; } } diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc index 115d1fb2..9c3d3701 100644 --- a/models/AvatarsModel.inc +++ b/models/AvatarsModel.inc @@ -57,6 +57,36 @@ return null; } + + /** + * Get an Avatar by its Character type and XP-level. + * + * @param int $seminaryId ID of Seminary + * @param string $charactertypeUrl URL-title of Character type + * @param int $xplevel XP-level + * @return array Avatar data + */ + public function getAvatarByTypeAndLevel($seminaryId, $charactertypeUrl, $xplevel) + { + $data = $this->db->query( + 'SELECT avatars.id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'INNER JOIN charactertypes ON charactertypes.id = avatars.charactertype_id '. + 'INNER JOIN xplevels ON xplevels.id = avatars.xplevel_id AND xplevels.seminary_id = charactertypes.seminary_id '. + 'WHERE charactertypes.seminary_id = ? AND charactertypes.url = ? AND xplevels.level = ?', + 'isi', + $seminaryId, + $charactertypeUrl, + $xplevel + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($charactertypeUrl); + } + + + return $data[0]; + } + } ?> diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 7b27da8d..25dc6a76 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -43,7 +43,7 @@ public function getCharactersForUser($userId) { return $this->db->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminaries.id AS seminary_id, seminaries.url AS seminary_url, seminaries.title AS seminary_title, seminaries.url AS seminary_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url, seminaries.id AS seminary_id, seminaries.url 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 '. @@ -63,7 +63,7 @@ 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, characters.avatar_id, 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 '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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 '. @@ -83,7 +83,7 @@ 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.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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 '. @@ -105,12 +105,9 @@ 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. '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.seminarymedia_id = avatars.avatarpicture_id '. - 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE characters.user_id = ? AND charactertypes.seminary_id = ?', 'ii', $userId, $seminaryId @@ -135,14 +132,9 @@ 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. '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.seminarymedia_id = avatars.avatarpicture_id '. - 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. - 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. - 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_id '. 'WHERE charactertypes.seminary_id = ? AND characters.url = ?', 'is', $seminaryId, $characterUrl @@ -166,14 +158,9 @@ 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description, small_seminarymedia.url AS small_avatar_url, small_seminarymedia.description AS small_avatar_description '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. '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.seminarymedia_id = avatars.avatarpicture_id '. - 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. - 'LEFT JOIN avatarpictures AS small_avatarpictures ON small_avatarpictures.seminarymedia_id = avatars.small_avatarpicture_id '. - 'LEFT JOIN seminarymedia AS small_seminarymedia ON small_seminarymedia.id = small_avatarpictures.seminarymedia_id '. 'WHERE characters.id = ?', 'i', $characterId @@ -273,7 +260,7 @@ public function getSuperiorCharacters($seminaryId, $xps, $count) { return $this->db->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. 'FROM v_characters AS characters '. 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. 'WHERE charactertypes.seminary_id = ? AND characters.xps > ? '. @@ -296,7 +283,7 @@ public function getInferiorCharacters($seminaryId, $xps, $count) { return $this->db->query( - 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertypes_url '. + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. 'FROM v_characters AS characters '. 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. 'WHERE charactertypes.seminary_id = ? AND characters.xps < ? '. @@ -317,12 +304,9 @@ public function getCharactersSolvedQuest($questId) { return $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, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + '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 charactertype_url '. '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.seminarymedia_id = avatars.avatarpicture_id '. - 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE EXISTS ('. 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. ')', @@ -342,12 +326,9 @@ public function getCharactersUnsolvedQuest($questId) { return $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, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + '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 charactertype_url '. '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.seminarymedia_id = avatars.avatarpicture_id '. - 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE EXISTS ('. 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. ') AND NOT EXISTS ('. @@ -369,12 +350,9 @@ public function getCharactersSubmittedQuest($questId) { return $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, seminarymedia.url AS avatar_url, seminarymedia.description AS avatar_description '. + '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 charactertype_url '. '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.seminarymedia_id = avatars.avatarpicture_id '. - 'LEFT JOIN seminarymedia ON seminarymedia.id = avatarpictures.seminarymedia_id '. 'WHERE EXISTS ('. 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. ') AND NOT EXISTS ('. diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl index f1bba542..58fd27c6 100644 --- a/questtypes/bossfight/html/quest.tpl +++ b/questtypes/bossfight/html/quest.tpl @@ -1,7 +1,7 @@

                    -

                    +

                    0) : ?> diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl index 260d28b0..433a619e 100644 --- a/questtypes/bossfight/html/submission.tpl +++ b/questtypes/bossfight/html/submission.tpl @@ -1,6 +1,6 @@

                    -

                    +

                    diff --git a/views/binary/media/avatar.tpl b/views/binary/media/avatar.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/avatar.tpl @@ -0,0 +1 @@ + diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index 74ba7cae..0ea56007 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -42,9 +42,7 @@
                  - - <?=$character['avatar_description']?> - +
                  @@ -66,25 +64,19 @@
                    &$rankCharacter) : ?>
                  • - - - +

                    .

                    ( XPs)

                  • - - - +

                    .

                    ( XPs)

                  • &$rankCharacter) : ?>
                  • - - - +

                    .

                    ( XPs)

                  • diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl index a1227a5a..91833a0e 100644 --- a/views/html/characters/index.tpl +++ b/views/html/characters/index.tpl @@ -9,9 +9,7 @@
                    • - -

                      - +

                      XP

                    • diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl index 60752cc7..c71cf164 100644 --- a/views/html/seminarybar/index.tpl +++ b/views/html/seminarybar/index.tpl @@ -1,6 +1,6 @@

                      - +
                      •  XPs
                      • @@ -37,9 +37,7 @@
                        • - - - +

                          ( XPs)

                        • From 67bff1f5a6691ccc7e705e141fa609106220121d Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 00:27:59 +0200 Subject: [PATCH 251/340] few fixed for Character permissions --- app/controllers/SeminaryRoleController.inc | 2 +- controllers/CharactersController.inc | 10 ++++++++++ controllers/MenuController.inc | 1 + controllers/SeminarymenuController.inc | 6 +++--- views/html/html.tpl | 4 +--- views/html/menu/index.tpl | 2 +- views/html/seminarymenu/index.tpl | 1 - 7 files changed, 17 insertions(+), 9 deletions(-) diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index cb7f6b86..f5bba984 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -137,7 +137,7 @@ // Check permissions - if(count(array_intersect(self::$user['seminaryroles'], $permissions)) == 0) { + if(!array_key_exists('seminaryroles', self::$user) || count(array_intersect(self::$user['seminaryroles'], $permissions)) == 0) { throw new \nre\exceptions\AccessDeniedException(); } } diff --git a/controllers/CharactersController.inc b/controllers/CharactersController.inc index 1adab0d3..7046e1a1 100644 --- a/controllers/CharactersController.inc +++ b/controllers/CharactersController.inc @@ -158,6 +158,16 @@ // Get seminary $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + // Check for already existing Character + try { + $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + throw new \nre\exceptions\AccessDeniedException(); + } + catch(\nre\exceptions\IdNotFoundException $e) { + // The should be the case + } + + // Character types $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc index c254a617..b7557c1f 100644 --- a/controllers/MenuController.inc +++ b/controllers/MenuController.inc @@ -35,6 +35,7 @@ // Set userdata $this->set('loggedUser', IntermediateController::$user); + $this->set('loggedCharacter', SeminaryRoleController::$character); $this->set('loggedSeminary', SeminaryRoleController::$seminary); } diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc index 5b5d780f..8f2957d7 100644 --- a/controllers/SeminarymenuController.inc +++ b/controllers/SeminarymenuController.inc @@ -18,7 +18,7 @@ * * @author Oliver Hanraths */ - class SeminarymenuController extends \hhu\z\Controller + class SeminarymenuController extends \hhu\z\controllers\SeminaryRoleController { @@ -35,8 +35,8 @@ parent::preFilter($request, $response); // Set userdata - $this->set('loggedUser', IntermediateController::$user); - $this->set('loggedSeminary', SeminaryRoleController::$seminary); + $this->set('loggedUser', self::$user); + $this->set('loggedSeminary', self::$seminary); } diff --git a/views/html/html.tpl b/views/html/html.tpl index 9cfcf8f3..a62f4e11 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -47,11 +47,9 @@
          diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl index c2662789..dbab43c2 100644 --- a/views/html/menu/index.tpl +++ b/views/html/menu/index.tpl @@ -1,7 +1,7 @@
        • The Legend of Z
        • 0) : ?>
        • - + 0) : ?>
        • diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl index 041c4f43..56b9307b 100644 --- a/views/html/seminarymenu/index.tpl +++ b/views/html/seminarymenu/index.tpl @@ -3,4 +3,3 @@
        • - From 4d831ced8f87134ced12abe6991c101002f547af Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 01:10:05 +0200 Subject: [PATCH 252/340] set dynamic value for Achievement list --- controllers/AchievementsController.inc | 27 ++++---- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 7459 -> 7676 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 58 ++++++++++------ models/AchievementsModel.inc | 69 ++++++++++++-------- views/html/achievements/index.tpl | 24 +++++-- 5 files changed, 112 insertions(+), 66 deletions(-) diff --git a/controllers/AchievementsController.inc b/controllers/AchievementsController.inc index 6f363012..ceeec968 100644 --- a/controllers/AchievementsController.inc +++ b/controllers/AchievementsController.inc @@ -61,15 +61,15 @@ // Get Character $character = SeminaryRoleController::$character; - // Get Achievements - $achievements = $this->Achievements->getAchievementsForSeminary($seminary['id']); - foreach($achievements as &$achievement) + // Get achieved Achievements + $achievedAchievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get unachieved Achievements + $unachievedAchievements = $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id'], true); + foreach($unachievedAchievements as &$achievement) { - // Get status for Character - $achieved = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); - // Get Character progress - if(!$achieved && $achievement['progress']) + if($achievement['progress']) { $conditions = array(); switch($achievement['condition']) @@ -143,18 +143,17 @@ $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); } - - // Get media - $achievement['media_index'] = 'unachieved_achievementsmedia_id'; - if($achieved) { - $achievement['media_index'] = 'achieved_achievementsmedia_id'; - } } + // Get ranking + $character['rank'] = $this->Achievements->getCountRank($seminary['id'], count($achievedAchievements)); + // Pass data to view $this->set('seminary', $seminary); - $this->set('achievements', $achievements); + $this->set('character', $character); + $this->set('achievedAchievements', $achievedAchievements); + $this->set('unachievedAchievements', $unachievedAchievements); } } diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index cabfa46b9f92800b5bc2981812c631cdd57e5e9c..cd24a7de766f06323de98d8d9e4d389515443bb7 100644 GIT binary patch delta 2796 zcmYM#e@szl)q^ zl8Yvg!sz<`G*o{!>ihFB1$~&o_@<1;RIJ5W*kJo@sG0A=BJ4vS4xt`&9v{asR3&cN z{v_)A1(a2ZHlPw|#u>N~RgnP3lvxiAl`d*OIEc#dZPY;TqY@lO&G1uX408%Kz&}5uoP=io70a< z>=>$&C#)w?oAj*hUqn^pGAiL~*6T4Ek8tA#a+(P)N@NoC#i`U&2~0=LBnLInLR3Pf zsLi?rHM4crHvBe;y@Pt5!@H)vRgFq`HR^t>orX#gLapgRRLPHE1)fE{9e-dY>M61c z^9CgmFsc$iqTa5X$fL~tL_cO`@DvSPhN?gv>Vd7OOxsZp2%=`b z9}DmhY6iz~A)dDWgq-FcmlDiicG_Dt$e5-9Rnay~*ZaT2-Uy;*+JhSCJyZh6P^CSA zn&Bzb(wwvXZ%|8f36;oI)c60vIhf3}%W%H61y!LamN33~orc!%1Zs0#u--;xn$3rr zK^`ihBGlT~pb}kaZ9)ya7WH?VQ3Gv9RWyLAbRTL74q*KKKT1On`VdvR&rlCIhnn#P zti~U(9{)x5m08O4lu!$5DYl|2u@{xdK70ZXA*VUXMU}W{y*r2cE5o@gXeAb*Hs2;x z0H^UEARtcj92DTe|?b3zbutB z*IJ0$Eaj*NG@>feYWtf}dtj$EfT}ukZhdl2pb|A8ky_iD~tg*v}e~ofaQPL$nZ^2_4IcK|+hFcjzfXY3t}rV0Peo zLJOorzeZZLQsO0|hEU2nUP%x$?KGZEbW9KS5e}i%*Re5y*@>FgHlm7XCbR=|G!W0L z!4V`_6SIoYf@>3OC;AE1GeSH#y7khQ)6)(qv3FlZ?TBRJMM8_E!$)}W7QcD;0F8)-I?c;@c66?S1X0mA2i6yNG9q zYI~3MjxYRf+wVnQv-tMbtwKVtzy`v7P8XtFw0DF>$BI4M!tRq}S>6d!x6rRwbp@I#t#6Rdq)fWfwkZkG5r}PbqZ0$k^B6 z?yf+{^_=EjFB%Diysl{U;aEF!kEbl}cS8P1)Y;P&>Gp?%8fu_7>~w`AK{w*>iRwm^ b=k=%1;G^E@qe+Vroi>W)@VEq~6Mmer&O4E!(nM zxshwkS~@o^+jMyUiE_%8b8c2P8#DjtlBu)S51Px(O&7gC4~Mh+eO~80-us;I`JVHr zw;K1yykt0ax6!(ZJmO@)>=WFY#)bCFWU~ysh++I2r(jCDSqL*R2lMfEY`_`VjB~IP zr{XR*K8R(E&*2?rp8ZEhKg_<(jII^nB&@)BSc{ry6DomMF&p2-O#IY&1o_wr=NVL@ z=aHQ43Qoik)IzUgi1{td@}k@*#A&z~r{f0H7hgs`*2_f+?nOQ53sjN%-FN``*araMbf3#msP)+W?*)`h4)ed}>I-ba->fqKvdRLO=>hw*o04$GsSDq*>^26ZT7 zZoCpzi8ZK1JDi<3f$?VKV{wlw&orqo?m|tx8?}NxsEH1t66r@B&aY7`8geFaFvaXD z>Up1$O#}x}37mVC zu^uiZ_#x^{9YY>rr%@G6q7uIB#v>Tj`+uE|CW=rOe{WHxEl2&J4z)E+ZoCAwHLFnH zYsXyd!a4Y+^DwG17f^{^L~Y$<$}$@ZrQZJ)bd=#MsLXp%iR?t}c^@jl{mw(EiThE% z`wBJDAgYq5Q6(NimHray_cxrQ$eL{u*?4-u3_4nAAtSG zPOQWo&IBrvUr~wvfqGqUx^X3ckE@akol8+?qcxBE>xW(3PzAQ3#;>FH{4M7@sFi6KdxTCm z5g-;2&l1lOb;P5Dni6}FP-)dl2~|n0o_LygSOePjG5l((J6o5nuCoobIyDlur38o8 zZ|%MN(z3mW=s!usiI@EQW{=?pqJdEHbu#WH^b)Tp))G1mPZ6qB3!$bX`T(JNtsoW? zYO2#pqKzmc)HaUc`J1A?&aaM;ikwfVEg`fC9Yl=K$_Mz0?)Gc5h~5iCH8G3eWw0j*wfl%7LWh04t)jEW z4Q8Saz7F+xtDw_LEOa;4Id!o0KdkwLUT>XSpGK}a)sH74>1BaL+my?J#Kx?~l(hNz gbL(PtHN6MIGhM$n@qIXbV&Z1b$Ek^-ypojv0aZ@cN&o-= diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index b658c97b..4444a9c3 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-16 15:53+0100\n" -"PO-Revision-Date: 2014-04-16 15:54+0100\n" +"POT-Creation-Date: 2014-04-17 01:08+0100\n" +"PO-Revision-Date: 2014-04-17 01:08+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -102,26 +102,43 @@ msgstr "Fehler" msgid "Achievements" msgstr "Errungenschaften" -#: views/html/achievements/index.tpl:15 +#: views/html/achievements/index.tpl:53 +#, php-format +msgid "Own progress: %d %%" +msgstr "Persönlicher Fortschritt: %d %%" + +#: views/html/achievements/index.tpl:54 +#: views/html/charactergroups/group.tpl:18 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/achievements/index.tpl:54 +#, php-format +msgid "You achieved %d of %d Achievements so far" +msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" + +#: views/html/achievements/index.tpl:68 views/html/characters/character.tpl:39 +#: views/html/seminarybar/index.tpl:28 +#, php-format +msgid "achieved at: %s" +msgstr "erhalten am: %s" + +#: views/html/achievements/index.tpl:79 msgid "Secret Achievement" msgstr "Geheime Errungenschaft" -#: views/html/achievements/index.tpl:19 +#: views/html/achievements/index.tpl:84 msgid "Continue playing to unlock this secret Achievement" msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" #: views/html/charactergroups/group.tpl:7 #: views/html/charactergroups/groupsgroup.tpl:7 #: views/html/charactergroups/index.tpl:7 -#: views/html/characters/character.tpl:53 views/html/seminarymenu/index.tpl:3 +#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:3 msgid "Character Groups" msgstr "Charaktergruppen" -#: views/html/charactergroups/group.tpl:18 -#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 -msgid "Rank" -msgstr "Platz" - #: views/html/charactergroups/group.tpl:20 msgid "Members" msgstr "Mitglieder" @@ -167,23 +184,18 @@ msgid "Total progress" msgstr "Fortschritt" #: views/html/characters/character.tpl:20 -#: views/html/characters/character.tpl:73 -#: views/html/characters/character.tpl:81 -#: views/html/characters/character.tpl:89 views/html/seminarybar/index.tpl:44 +#: views/html/characters/character.tpl:69 +#: views/html/characters/character.tpl:75 +#: views/html/characters/character.tpl:81 views/html/seminarybar/index.tpl:42 #: views/html/users/user.tpl:29 msgid "Level" msgstr "Level" -#: views/html/characters/character.tpl:39 views/html/seminarybar/index.tpl:28 -#, php-format -msgid "achieved at: %s" -msgstr "erhalten am: %s" - -#: views/html/characters/character.tpl:65 +#: views/html/characters/character.tpl:63 msgid "Ranking" msgstr "Ranking" -#: views/html/characters/character.tpl:97 +#: views/html/characters/character.tpl:89 msgid "Topic progress" msgstr "Thematischer Fortschritt" @@ -399,7 +411,7 @@ msgstr "Quests anzeigen" msgid "Last Quest" msgstr "Letzter Speicherpunkt" -#: views/html/seminarybar/index.tpl:48 +#: views/html/seminarybar/index.tpl:46 #, php-format msgid "Show %s-Profile" msgstr "%s-Profil anzeigen" @@ -555,6 +567,10 @@ msgstr "Name" msgid "Roles" msgstr "Rollen" +#, fuzzy +#~ msgid "achieved at %s" +#~ msgstr "erhalten am: %s" + #~ msgid "Usergroups" #~ msgstr "Benutzergruppen" diff --git a/models/AchievementsModel.inc b/models/AchievementsModel.inc index f25689b3..bf7f56f4 100644 --- a/models/AchievementsModel.inc +++ b/models/AchievementsModel.inc @@ -60,26 +60,6 @@ } - /** - * Get all Achievements of a Seminary. - * - * @param int $seminaryId ID of Seminary to get Achievements of - * @return array Achievements data - */ - public function getAchievementsForSeminary($seminaryId) - { - return $this->db->query( - 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. - 'FROM achievements '. - 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. - 'WHERE seminary_id = ? '. - 'ORDER BY achievements.pos ASC', - 'i', - $seminaryId - ); - } - - /** * Get all not yet achieved Achievements for a Seminary that can * only be achieved once (only by one Character). @@ -131,26 +111,60 @@ * @param int $characterId ID of Character * @return array Achievements data */ - public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId) + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId, $includeOnlyOnce=false) { return $this->db->query( 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. 'FROM achievements '. 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. - 'WHERE achievements.seminary_id = ? AND only_once = 0 AND NOT EXISTS ('. + 'WHERE achievements.seminary_id = ? AND only_once <= ? AND NOT EXISTS ('. 'SELECT character_id '. 'FROM achievements_characters '. 'WHERE '. 'achievements_characters.achievement_id = achievements.id AND '. 'achievements_characters.character_id = ?'. - ')', - 'ii', + ') '. + 'ORDER BY achievements.pos ASC', + 'iii', $seminaryId, + $includeOnlyOnce, $characterId ); } + /** + * Get the rank for the number of achieved Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $xps Amount of achieved Achievements + * @return int Rank of Achievements count + */ + public function getCountRank($seminaryId, $count) + { + $data = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM ('. + 'SELECT count(DISTINCT achievement_id) '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'WHERE achievements.seminary_id = ? '. + 'GROUP BY character_id '. + 'HAVING count(DISTINCT achievement_id) > ?'. + ') AS ranking', + 'ii', + $seminaryId, + $count + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + /** * Get all date conditions for an Achievement. * @@ -492,15 +506,18 @@ public function hasCharacterAchievedAchievement($achievementId, $characterId) { $data = $this->db->query( - 'SELECT character_id '. + 'SELECT achievement_id, character_id, created '. 'FROM achievements_characters '. 'WHERE achievement_id = ? AND character_id = ?', 'ii', $achievementId, $characterId ); + if(!empty($data)) { + return $data[0]; + } - return !empty($data); + return false; } } diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index cee00f82..79672cdc 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -50,20 +50,34 @@ -

          Persönlicher Fortschritt: 75%

          -

          12. Platz: Du hast bislang 13 von insgesamt 75 Errungenschaften erreicht.

          +

          +

          . : .

          • Freigeschaltetes Achievementerreicht am 17.06.104

            Das Bild ist entsprechend farbig, ein eventueller Fortschrittsbalken mit 100% soll entfallen.

          • - +
          • - + -

            erreicht am 17.06.104

            +

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

            +

            +
          • + + +
          • + + + +

            + +

            From c47eb1e314fb5715be11d6dfa0c43d8e421fc055 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 01:31:23 +0200 Subject: [PATCH 253/340] determine seldom Achievements (Issue #57) --- configs/AppConfig.inc | 3 ++- controllers/AchievementsController.inc | 7 +++++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 7676 -> 7831 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 13 +++++++-- models/AchievementsModel.inc | 24 +++++++++++++++++ views/html/achievements/index.tpl | 28 ++++++++++---------- 6 files changed, 58 insertions(+), 17 deletions(-) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 841903c6..1bfbc2d6 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -94,7 +94,8 @@ * @var array */ public static $misc = array( - 'ranking_range' => 2 + 'ranking_range' => 2, + 'seldom_achievements_range' => 3 ); diff --git a/controllers/AchievementsController.inc b/controllers/AchievementsController.inc index ceeec968..00adea6d 100644 --- a/controllers/AchievementsController.inc +++ b/controllers/AchievementsController.inc @@ -61,6 +61,12 @@ // Get Character $character = SeminaryRoleController::$character; + // Get seldom Achievements + $seldomAchievements = $this->Achievements->getSeldomAchievements($seminary['id'], \nre\configs\AppConfig::$misc['seldom_achievements_range']); + foreach($seldomAchievements as &$achievement) { + $achievement['achieved'] = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + } + // Get achieved Achievements $achievedAchievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); @@ -152,6 +158,7 @@ // Pass data to view $this->set('seminary', $seminary); $this->set('character', $character); + $this->set('seldomAchievements', $seldomAchievements); $this->set('achievedAchievements', $achievedAchievements); $this->set('unachievedAchievements', $unachievedAchievements); } diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index cd24a7de766f06323de98d8d9e4d389515443bb7..48535d0652cd85986ab72c60a0375df4c87e3bed 100644 GIT binary patch delta 2768 zcmYk;drZ}39LMoTE~1bi2TTx*pBIh@AqW{4#Zth^OAw}Hly%_zxD*_W!zI?%X_j+s z*dMWE=32|j{?NwiY?{R;Hj_VWE%}4iY^=O&Ia{{oa+F=(pL3pC&-k9#^Zb6l^LxI( z@ALeC+iORvLerkOHw`U&>rja{;au#%cno4T^P53By758G!4EJ4ComJgM^)x`PAJVVy&^` zD%5q=s0XM=UEgTO&8X`;Fpl}nb9D5Dew>3Z;{qJA<0Gg9kKuA0!(99Ub)y8zwhU8H zl_<62YSi^@s6>ZQiM)=f_!fp#l2JO!^c1ReglpO75P3~GV| zW|#9&OO%FMA}?yr`*I_RDP&e9-s?Y&k zi6f{@dIpu)O;knWD1%HwRl;M(UQ|U2Lv)mJxwQ&o7;i-GVQNu{G}`m+s06xDPZC5; zuosoktEf#ojC!ySttW7NmNBPs5yxNRmD7Dghv{gu9Yo&38vlVR^;|A4 z#6r~D(u76$g7p~cJ->+BBfp}a^l#J>CUUY2JxE1DW-}eVZo82=%zzyap(cJGRf#a_ zhF_o(okW%N5~>2fVHW<0da_KiUyAwGZOAFJ2Up;JOw#*5N{8gkXQ5r(1=8&xtNI{i22X%cOYAH+YxE!@a)u==oP}ldNmf%IqXMS@?9XyMXU5?7|7HTbX z_|PV+u(qHQJ%D;!hEWNyu^kXF+ME04vfJ*ENYDsRQDiY5+DWUW%>YqnvF$XwjHlZrv zvksv$J&8ql8np>;pb|*q_cBE?nW##XpdM&Fs^nYnZVcdEcoFQ~mRWBm(NfmpK8W===l&s$GNB`!u~ zUWR(&8ss6(R%<)z3$LIid<}V>|7*H=DN!~2PC{Bpsoi7GZA8^bchw?qAsPv_b;KaS z&WikNCF*^ipfpRo6UwG>FJ&_`4g+J$PYcVq)JM@Ib9fO=&L?RY6_13g0cYWZj*%85F{A({xaXNboM z{qB60=qJ>Qi031{=!afZwTFp^h@He%qL^4UoSK;CIPJcm)9ktd$Jf%{;r6?Jr#sL& z=;ZpH-i|Ie7|uvM8<*PY@9uIoL@(%a1HHlUx5?*YV@k?~k9xci{>sxZYtcYokKc9N zo?x%O-sS6b+T7N@fZq*-bJ7pR=hSq#PSEY_bpt`dsqN|M3$!t4Y4^2qAbc{jHtrv< C^8r5q delta 2626 zcmYM#S!`8B7{Ku7a0eQ81@5)#BT#6(FT#w`+pJQ-q06Xn7G|K3q2=R3bM=iZ+A=9@XU-F3I; zB!AA!Iud9d#CYOTW(Y^|^}(EIqq9TE!7-SJr8o>{;c#4xqi{XmiEUVbNi4_raU@=d z{a^z$`4m9L5jjTnxclybJ4NzY(4JRxHLgEWsn_gHGe!cn+<^_1N!6zdw$$ znrJ_(1oI`;d}8UBGLn$I5<;TSB%*=S-- z$e5uex)+_u%jl*(G@SbTpij8L5&AgU{}C;92KDkmWoXGNa5C1So3jZ`>@Zr%W6|U2 zCOsAV=h2E>L=(Oe{VB=C0B-z-9N`)#6X{34ID~qdz%XO+45*3 zewD%AL7!K|yXM}iMiX9)-cPRM!cy!+*Yp5d@7v$6_YaF2@PcC1{1(v5fJ<3tYH{$I#7nCi**?X&xUsgVAV0#pv4Cpoz|l zE<^`jhW_1Jbf8UWMVrw|x1meWiK+L$iwht04qCd8(FgRRGd_dW_zlj+zma_vW-vVy zT7oXc2DB2p&_wp(J$Mi~!f{Sk;(YYxSn6+v<5|#3EJioq8Z?3Dks^dnv;y7eOpc5r5KD-)gl!JdLAa>CL-!G;ZQGG1Y{nIY3uN=x$VHn>tR`v*Q?@;s zCK=Xo@nE_WTCt5NB3ymj$~0jMI<1XF6|snL2iWR~hrFP*5UfeKpK!t51e=KG2#j=$Uds0{Y=HVm6V?-0-0z6L4Ac_gwLSkv^>i<(S zmrup+Z1ttJ)ujoua96x`7tAD5+Yw9SeRtEmc-@Fwi3f=4c#ri?E&R6Fe;RqsQrp{G z6A52|hl!>IoVF9S#B!pL*g)8J66=XP;t8Uiu=!r>Odb->query( + 'SELECT id, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id, count(DISTINCT character_id) AS c '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 '. + 'GROUP BY achievement_id '. + 'ORDER BY count(DISTINCT character_id) ASC '. + 'LIMIT ?', + 'ii', + $seminaryId, + $count + ); + } + + /** * Get all achieved Achievements for a Character. * diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 79672cdc..9253052c 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -8,23 +8,23 @@
            -

            Die seltensten Errungenschaften

            +

              +
            1. - -

              Des Königs neue Kleider

              -

              wurde erst 5 mal gefunden

              -
            2. -
            3. - -

              Sein oder Sterben

              -

              wurde erst 7 mal gefunden

              -
            4. -
            5. - -

              Hungersnot? Ohne mich.

              -

              wurde erst 9 mal gefunden

              + + + + + + + + + +

              +

            6. +
            From ce44a3c3b83dda7cbe61c22dd2b4957153fcd86f Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 01:48:33 +0200 Subject: [PATCH 254/340] determine Characters with the most achieved Achievements (Issue #57) --- configs/AppConfig.inc | 4 +-- controllers/AchievementsController.inc | 8 ++++-- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 7831 -> 8006 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 25 +++++++++++++------ models/CharactersModel.inc | 26 ++++++++++++++++++++ views/html/achievements/index.tpl | 20 +++++---------- 6 files changed, 57 insertions(+), 26 deletions(-) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 1bfbc2d6..f7ed6931 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -94,8 +94,8 @@ * @var array */ public static $misc = array( - 'ranking_range' => 2, - 'seldom_achievements_range' => 3 + 'ranking_range' => 2, + 'achievements_range' => 3 ); diff --git a/controllers/AchievementsController.inc b/controllers/AchievementsController.inc index 00adea6d..d99671b6 100644 --- a/controllers/AchievementsController.inc +++ b/controllers/AchievementsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('achievements', 'seminaries', 'media'); + public $models = array('achievements', 'seminaries', 'media', 'characters'); /** * User permissions * @@ -62,11 +62,14 @@ $character = SeminaryRoleController::$character; // Get seldom Achievements - $seldomAchievements = $this->Achievements->getSeldomAchievements($seminary['id'], \nre\configs\AppConfig::$misc['seldom_achievements_range']); + $seldomAchievements = $this->Achievements->getSeldomAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range']); foreach($seldomAchievements as &$achievement) { $achievement['achieved'] = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); } + // Get Characters with the most Achievements + $successfulCharacters = $this->Characters->getCharactersWithMostAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range']); + // Get achieved Achievements $achievedAchievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); @@ -159,6 +162,7 @@ $this->set('seminary', $seminary); $this->set('character', $character); $this->set('seldomAchievements', $seldomAchievements); + $this->set('successfulCharacters', $successfulCharacters); $this->set('achievedAchievements', $achievedAchievements); $this->set('unachievedAchievements', $unachievedAchievements); } diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index 48535d0652cd85986ab72c60a0375df4c87e3bed..da63f7b3b151c186d6df09f02dc48db8f943fe82 100644 GIT binary patch delta 2804 zcmX}ue@vBC9LMp)ZzK@~OGLpd38I96kU$D4p^*h)MA|R%cDZn`TrR~05?On)Wm`XN zEuixMKI^+Ai&hv1e=X}rioae$j z<)BoQnOJjJq)rkK%MZiL>xq%*Nkw8m8Q0 zOgd)bVqA&0Ve7=XV_u|_%Y}nD6VIYz$bsUs{8#Plu>hs&} z`4DQRpP&-Hh%WpMwK=CUTP0XDgZfhiQ^o~ZjhfMV)NbC0deCO%UNdCR51~r_4eCLE zqbm0=F2X5HSDUp6l~^;XqTSZ#QI&Yfu@?qVCE10__yg;Q7|Hn&D$;;1 zY(uT}8>l6C5A$#Y_4ZuBe4NJ1E-O&)eUO9p$~I)ej(LlY)^Z3-a1WA``2qF%{ek*k zNMLlG&qEDdimJ#O)B`u56856*??P4L6`X@_q9$?xGx0=N*8e;m#xPMleIcfyHeDq$ z2~&$IaT98ZHrewo)J%I(1HFq%U^lANhf$yZ7?r>&)Y4wC*MG%njBl>eQAQCglx|#r zT9Q0mjMdf3k2%<)}oPQJXA?+Kf9<34Dqa*PKRG;wRLEE~6@bV=nc- zgHAesqB3wTE@F@N$RK0vIn=gPW*h$1`$FHd;MmZREJges)j77^J*C!wY#$RgGd8wgt8 zXk237SX{KTmXqfQC9l1q)+u(C0dBb z34Nh95Uqq(iLKOf(a*B>tRwV2q!;5! zLfb{{QKFLQ)(NeVSj70@_Vfhd<1-w+#65)Gnumxdh{>jHp}jlVHrtN2M>X*Xv5@c+ zn+Re?&(WK@ zipV3{34L{_W%d;&^r!jUA(z|Z^Lx9!O|Gma*Q$wrn>RRoJmI6r)SC8Crz_Ou@pwa_ z=B|Lt(;f(TJ)P~t!Nj*?B8!*x#inG#@RF3TA|ie6zPj|(Sf4x4$^AEGmPO63@_Sw0 bj^_42ONZC*@rC$>tKQw#78ou_ZHfI4^v4BF delta 2673 zcmYk;YfP6_9Ki7-ps1uMm&C;I23`nJ$N&>75i2igmYPu(im8>C#2aht-!$ji&=)af zuS&i zEJCke7RM{m>(^l#^M~i?_=RoQA9rCcHplTHG{H6;hn-l6KcF{Cr)*;}6Rkvf9M_=N zuR#-SMiY4jhvMs)uq5qt%=9Q)x-RtiSD1#~=tO7G1ka%f|AgcauAvj8GrJB#A5k{? zh>FmOrz1b1mXA_gl1cri(AgbN96=K}5j~Brya!F-GP=^M$WIu-Nyo+LN*AIDZ^2R6 zghluon%Hq9*Kji0J%swZq6-{w_x_CDs2}yA8;a5K3@pTY^hSHo3hl*-*otn_<7i@6 z(2Ax}2F*Y#krl^9XhljAbj-LiT8$};XCe0pwP+&C;`y~`0_)M0G@=viL=$=$-Lx&} z!rqS_!LB|b9K%~U{vxlO_e~t6<7PXIX8u_`aT=}2IrJI-ftGpz7nfiO`nJ?zDLx-< zL*Mf==pOkMUFkpQBOJ)d3d}+(k_dC@_`0n?<_O#4xEY=J9kdc1=ncO>6FrTV^ekF| z-*5!}g|2Kk*^j|V(FMqK|Y;ZwAQA;NkcpPK z0KI+!`Y6lexDtItHE1G>(d)OOkKhHI#Qfn^JNP*!cR8BjRrFaF@Zlz_impNv-HX00 zEoee-pnISlP4sBA3!V5R`n_+^dAc!SNqgv6>PzS&xQ5;(c9y27Fsdq@R29x@v)4s%^;cxHhOX&+<}(awzikB0v8b` zXGjp%W-z>Z^S2v(F7HD|Fe;jpCLSnmhmyd4RtS3OxW%r<|cdm z=|n0T?k3o)$zRcr<95Q0x0U#>ZJ^^Gu*wtS!A5i+r8)n@blg0fi6UYPVY`o5Of(Ry z2=+{Pil`t8i4Au)=mBW$%#^YJL*uhM6TZG^3q*pck@ ze)!(n9w6=~mJ;)cDa7QK`hoj9MrO37betR1(l@27qGd;R5p=wn{dJ#?eYusX9cS|z G`uz*g7uetc diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index c041d6c7..8f3f73a2 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-17 01:27+0100\n" -"PO-Revision-Date: 2014-04-17 01:27+0100\n" +"POT-Creation-Date: 2014-04-17 01:46+0100\n" +"PO-Revision-Date: 2014-04-17 01:46+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -111,33 +111,42 @@ msgstr "Die seltensten Errungenschaften" msgid "Achievement has been achieved only %d times" msgstr "wurde erst %d mal gefunden" -#: views/html/achievements/index.tpl:53 +#: views/html/achievements/index.tpl:32 +msgid "Most successful collectors" +msgstr "Die erfolgreichsten Sammler" + +#: views/html/achievements/index.tpl:38 +#, php-format +msgid "Character has achieved %d Achievements" +msgstr "hat %d Errungenschaften erhalten" + +#: views/html/achievements/index.tpl:45 #, php-format msgid "Own progress: %d %%" msgstr "Persönlicher Fortschritt: %d %%" -#: views/html/achievements/index.tpl:54 +#: views/html/achievements/index.tpl:46 #: views/html/charactergroups/group.tpl:18 #: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 msgid "Rank" msgstr "Platz" -#: views/html/achievements/index.tpl:54 +#: views/html/achievements/index.tpl:46 #, php-format msgid "You achieved %d of %d Achievements so far" msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" -#: views/html/achievements/index.tpl:68 views/html/characters/character.tpl:39 +#: views/html/achievements/index.tpl:60 views/html/characters/character.tpl:39 #: views/html/seminarybar/index.tpl:28 #, php-format msgid "achieved at: %s" msgstr "erhalten am: %s" -#: views/html/achievements/index.tpl:79 +#: views/html/achievements/index.tpl:71 msgid "Secret Achievement" msgstr "Geheime Errungenschaft" -#: views/html/achievements/index.tpl:84 +#: views/html/achievements/index.tpl:76 msgid "Continue playing to unlock this secret Achievement" msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index 25dc6a76..b3eac8b7 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -174,6 +174,32 @@ } + /** + * Get Characters with the most amount of Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $conut Amount of Characters to retrieve + * @return array List of Characters + */ + public function getCharactersWithMostAchievements($seminaryId, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url, count(DISTINCT achievement_id) AS c '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'LEFT JOIN v_characters AS characters ON characters.id = achievements_characters.character_id '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE achievements.seminary_id = ? '. + 'GROUP BY character_id '. + 'ORDER BY count(DISTINCT achievement_id) DESC '. + 'LIMIT ?', + 'ii', + $seminaryId, + $count + ); + } + + /** * Calculate only XPs for a Character achieved through Quests. * diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 9253052c..3bd61f20 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -29,23 +29,15 @@
            -

            Die erfolgreichsten Sammler

            +

              +
            1. - -

              VHS

              -

              hat 15 Errungenschaften gefunden

              -
            2. -
            3. - -

              Betamax

              -

              hat 14 Errungenschaften gefunden

              -
            4. -
            5. - -

              Laserdisc

              -

              hat 12 Errungenschaften gefunden

              + +

              +

            6. +
            From ed2965711571d3a0c2eee7cc79c02a1b31a0d9d3 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 11:11:29 +0200 Subject: [PATCH 255/340] implement rudimental form for creating Quests and Questgroups --- controllers/QuestgroupsController.inc | 45 ++++++++++++- controllers/QuestsController.inc | 92 +++++++++++++++++++++++++-- models/QuestgroupsModel.inc | 46 ++++++++++++++ models/QuestsModel.inc | 30 +++++++++ models/QuesttypesModel.inc | 15 +++++ views/html/questgroups/create.tpl | 16 +++++ views/html/quests/create.tpl | 34 ++++++++++ 7 files changed, 271 insertions(+), 7 deletions(-) create mode 100644 views/html/questgroups/create.tpl create mode 100644 views/html/quests/create.tpl diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index d7b8acf1..b5e7e509 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -31,7 +31,8 @@ * @var array */ public $permissions = array( - 'questgroup' => array('admin', 'moderator', 'user') + 'questgroup' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator', 'user') ); /** * User seminary permissions @@ -39,7 +40,8 @@ * @var array */ public $seminaryPermissions = array( - 'questgroup' => array('admin', 'moderator', 'user') + 'questgroup' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator') ); @@ -174,6 +176,45 @@ $this->set('quests', $quests); } + + /** + * Action: create. + * + * Create a new Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function create($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Create Questgroup + $validation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // TODO Validation + $title = $this->request->getPostParam('title'); + + // Create new Questgroup + if($validation === true) + { + $questgroupId = $this->Questgroups->createQuestgroup( + $this->Auth->getUserId(), + $seminary['id'], + $title + ); + + // Redirect + $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url']))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + } + } ?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index ee8a3c67..d35e4fd2 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -34,7 +34,8 @@ 'index' => array('admin', 'moderator', 'user'), 'quest' => array('admin', 'moderator', 'user'), 'submissions' => array('admin', 'moderator', 'user'), - 'submission' => array('admin', 'moderator', 'user') + 'submission' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator', 'user') ); /** * User seminary permissions @@ -42,10 +43,11 @@ * @var array */ public $seminaryPermissions = array( - 'index' => array('admin', 'moderator', 'user'), - 'quest' => array('admin', 'moderator', 'user'), - 'submissions' => array('admin', 'moderator'), - 'submission' => array('admin', 'moderator') + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator'), + 'create' => array('admin', 'moderator') ); @@ -418,6 +420,86 @@ } + /** + * Action: create. + * + * Create a new Quest. + * + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function create($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Quest groups + $questgroups = $this->Questgroups->getQuestgroupsForSeminary($seminary['id']); + + // Quest types + $questtypes = $this->Questtypes->getQuesttypes(); + + // Create Quest + $validation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // TODO Validation + $name = $this->request->getPostParam('name'); + $xps = $this->request->getPostParam('xps'); + $wrongtext = $this->request->getPostParam('wrongtext'); + $task = $this->request->getPostParam('task'); + + // Validate Questgroup + $questgroupIndex = null; + foreach($questgroups as $index => &$questgroup) + { + $questgroup['selected'] = ($questgroup['url'] == $this->request->getPostParam('questgroup')); + if($questgroup['selected']) { + $questgroupIndex = $index; + } + } + if(is_null($questgroupIndex)) { + throw new \nre\exceptions\ParamsNotValidException($questgroup); + } + + // Validate Questtype + $questtypeIndex = null; + foreach($questtypes as $index => &$questtype) + { + $questtype['selected'] = ($questtype['url'] == $this->request->getPostParam('questtype')); + if($questtype['selected']) { + $questtypeIndex = $index; + } + } + if(is_null($questtypeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($questtype); + } + + // Create new Quest + if($validation === true) + { + $questId = $this->Quests->createQuest( + $this->Auth->getUserId(), + $name, + $questgroups[$questgroupIndex]['id'], + $questtypes[$questtypeIndex]['id'], + $xps, + $wrongtext, + $task + ); + + // Redirect + $this->redirect($this->linker->link(array('quests', 'index', $seminary['url']))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroups', $questgroups); + $this->set('questtypes', $questtypes); + } + + /** diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index 3e2ced21..26ae9a10 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -89,6 +89,25 @@ } + /** + * Get all Questgroups for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questgroups + */ + public function getQuestgroupsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questgroups '. + 'WHERE seminary_id = ? '. + 'ORDER BY title ASC', + 'i', + $seminaryId + ); + } + + /** * Get a Questgroup by its ID. * @@ -503,6 +522,33 @@ } + /** + * Create a new Questgroup. + * + * @param int $userId User-ID that creates the new character + * @param int $seminaryId ID of Seminary + * @param string $title Title for new Questgroup + * @return int ID of new Questgroup + */ + public function createQuestgroup($userId, $seminaryId, $title) + { + $this->db->query( + 'INSERT INTO questgroups '. + '(created_user_id, seminary_id, title, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $seminaryId, + $title, + \nre\core\Linker::createLinkParam($title) + ); + + + return $this->db->getInsertId(); + } + + /** diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 5b7eda91..13d113bc 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -405,6 +405,36 @@ ); } + + /** + * Create a new Quest. + * + * @param int $userId User-ID that creates the new character + * @param string $name Name for new Quest + * @param int $questgroupId ID of Questgroup + * @param int $questtypeId ID of Questtype + * @param int $xps XPs for new Quest + * @param string $wrongtext Wrongtext for new Quest + * @param string $task Task for new Quest + * @return int ID of new Quest + */ + public function createQuest($userId, $name, $questgroupId, $questtypeId, $xps, $wrongtext, $task) + { + $this->db->query( + 'INSERT INTO quests '. + '(created_user_id, questgroup_id, questtype_id, title, url, xps, wrong_text, task) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?, ?, ?)', + 'iiississ', + $userId, $questgroupId, $questtypeId, + $name, \nre\core\Linker::createLinkParam($name), + $xps, $wrongtext, $task + ); + + + return $this->db->getInsertId(); + } + } ?> diff --git a/models/QuesttypesModel.inc b/models/QuesttypesModel.inc index 3e0ef265..58b36648 100644 --- a/models/QuesttypesModel.inc +++ b/models/QuesttypesModel.inc @@ -34,6 +34,21 @@ + /** + * Get all registered Questtypes. + * + * @return array List of registered Questtypes + */ + public function getQuesttypes() + { + return $this->db->query( + 'SELECT id, title, url, classname '. + 'FROM questtypes '. + 'ORDER BY title ASC' + ); + } + + /** * Get a Questtype by its ID * diff --git a/views/html/questgroups/create.tpl b/views/html/questgroups/create.tpl new file mode 100644 index 00000000..233713cc --- /dev/null +++ b/views/html/questgroups/create.tpl @@ -0,0 +1,16 @@ + +
            + +
            + +

            +

            +

            + +
            +
            + +
            +
            + +
            diff --git a/views/html/quests/create.tpl b/views/html/quests/create.tpl new file mode 100644 index 00000000..3c05e6d9 --- /dev/null +++ b/views/html/quests/create.tpl @@ -0,0 +1,34 @@ + +
            + +
            + +

            +

            +

            + +
            +
            + +
            + +
            + +
            + +
            +
            +
            +
            +
            +
            + +
            From 8d6ea14e6880a7f50b973846f6dcd3334f63eef2 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 11:11:49 +0200 Subject: [PATCH 256/340] fix link to Quests in Quest list --- views/html/quests/index.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl index 80a3e4fe..00d7e576 100644 --- a/views/html/quests/index.tpl +++ b/views/html/quests/index.tpl @@ -35,7 +35,7 @@ - + From 78b494500ff5842ca9c5808779fec0a5ac8fa63a Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 13:53:21 +0200 Subject: [PATCH 257/340] add ?entry text? to Quests and show them instead of the name --- controllers/QuestsController.inc | 2 +- models/QuestsModel.inc | 8 +++---- models/QuesttextsModel.inc | 41 ++++++++++++++++++++++++++++++++ views/html/quests/quest.tpl | 18 +++++++++++--- 4 files changed, 61 insertions(+), 8 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index d35e4fd2..9ba3bf62 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -270,7 +270,7 @@ $nextQuests = null; $charactedHasChoosenNextQuest = false; $nextQuestgroup = null; - if($questtexttypeUrl == 'Epilog') + if($questtexttypeUrl == 'Epilog' || $this->Questtexts->getQuesttextCountOfQuest($quest['id'], 'Epilog') == 0) { // Next Quest $nextQuests = $this->Quests->getNextQuests($quest['id']); diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index 13d113bc..d38dba68 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -70,7 +70,7 @@ public function getQuestByUrl($seminaryId, $questgroupId, $questUrl) { $data = $this->db->query( - 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. 'FROM quests '. 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', @@ -96,7 +96,7 @@ public function getQuestById($questId) { $data = $this->db->query( - 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, quests.right_text, quests.wrong_text, quests.questsmedia_id '. 'FROM quests '. 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. 'WHERE quests.id = ?', @@ -146,7 +146,7 @@ public function getNextQuests($questId) { return $this->db->query( - 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'SELECT quests.id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. 'FROM quests_previousquests '. 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. @@ -166,7 +166,7 @@ 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 '. + 'SELECT quests.id, quests.title, quests.url, quests.entry_text, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. 'FROM quests_previousquests '. 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc index 1fd82319..53430ec9 100644 --- a/models/QuesttextsModel.inc +++ b/models/QuesttextsModel.inc @@ -69,6 +69,47 @@ } + /** + * Get count of Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return int Amount of Questtexts for a Quest + */ + public function getQuesttextCountOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + $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 = ?', + 'i', + $questId + ); + } + else + { + $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, + $questtexttypeUrl + ); + } + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + /** * Get corresponding Questtext for a Sidequest. * diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index cbb21d25..f914471b 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -63,8 +63,12 @@

            :

            @@ -85,12 +89,20 @@
          • - : + + + + : + + + + +
          • From 2fbea36e12e49a577fe62fcc81c5f7d8dcfdd7e1 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 13:53:37 +0200 Subject: [PATCH 258/340] determine user Seminary roles before Character --- app/controllers/SeminaryRoleController.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index f5bba984..69e9a38b 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -80,8 +80,8 @@ // Get Seminary and Character data try { self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); - self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); } catch(\nre\exceptions\IdNotFoundException $e) { } From bf9df40b5f8f946842f7add299578482fa7ff2c9 Mon Sep 17 00:00:00 2001 From: coderkun Date: Thu, 17 Apr 2014 14:10:41 +0200 Subject: [PATCH 259/340] update form for creating Qusets --- controllers/QuestsController.inc | 2 ++ models/QuestsModel.inc | 11 ++++++----- views/html/quests/create.tpl | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 9ba3bf62..a8dbdcb7 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -445,6 +445,7 @@ // TODO Validation $name = $this->request->getPostParam('name'); $xps = $this->request->getPostParam('xps'); + $entrytext = $this->request->getPostParam('entrytext'); $wrongtext = $this->request->getPostParam('wrongtext'); $task = $this->request->getPostParam('task'); @@ -483,6 +484,7 @@ $questgroups[$questgroupIndex]['id'], $questtypes[$questtypeIndex]['id'], $xps, + $entrytext, $wrongtext, $task ); diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index d38dba68..ff77498a 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -414,21 +414,22 @@ * @param int $questgroupId ID of Questgroup * @param int $questtypeId ID of Questtype * @param int $xps XPs for new Quest + * @param string $entrytext Entrytext for new Quest * @param string $wrongtext Wrongtext for new Quest * @param string $task Task for new Quest * @return int ID of new Quest */ - public function createQuest($userId, $name, $questgroupId, $questtypeId, $xps, $wrongtext, $task) + public function createQuest($userId, $name, $questgroupId, $questtypeId, $xps, $entrytext, $wrongtext, $task) { $this->db->query( 'INSERT INTO quests '. - '(created_user_id, questgroup_id, questtype_id, title, url, xps, wrong_text, task) '. + '(created_user_id, questgroup_id, questtype_id, title, url, xps, entry_text, wrong_text, task) '. 'VALUES '. - '(?, ?, ?, ?, ?, ?, ?, ?)', - 'iiississ', + '(?, ?, ?, ?, ?, ?, ?, ?, ?)', + 'iiississs', $userId, $questgroupId, $questtypeId, $name, \nre\core\Linker::createLinkParam($name), - $xps, $wrongtext, $task + $xps, $entrytext, $wrongtext, $task ); diff --git a/views/html/quests/create.tpl b/views/html/quests/create.tpl index 3c05e6d9..31089ccb 100644 --- a/views/html/quests/create.tpl +++ b/views/html/quests/create.tpl @@ -25,6 +25,8 @@

            +
            +



            From 86c86b81f46925eb11cd6515f85dca975d257893 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Apr 2014 14:17:24 +0200 Subject: [PATCH 260/340] style for seminar list --- views/html/seminaries/index.tpl | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index 986991a9..0f2e8cac 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -4,30 +4,28 @@
          • -
              +
              • -

                +
                +

                0) : ?> -

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

                +

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

                +

                + + + +

                + +
              From 2a7f5461ff472c049d15d5c1a65ac92bd3549f09 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Apr 2014 15:36:20 +0200 Subject: [PATCH 261/340] form design update --- views/html/seminaries/create.tpl | 2 +- views/html/users/create.tpl | 2 +- www/css/desktop.css | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl index 30938357..22a9acaa 100644 --- a/views/html/seminaries/create.tpl +++ b/views/html/seminaries/create.tpl @@ -6,7 +6,7 @@

              -
              +

              diff --git a/views/html/users/create.tpl b/views/html/users/create.tpl index d4f38bb7..2d5484dc 100644 --- a/views/html/users/create.tpl +++ b/views/html/users/create.tpl @@ -1,7 +1,7 @@

              - +

              diff --git a/www/css/desktop.css b/www/css/desktop.css index 9226aa06..cfff8097 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -102,6 +102,7 @@ aside{display:none} .qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} .qgicon.locked{margin-top:-2px} .qgtitle a{background:#5cb6bd;border-radius:3px 3px 0 0;display:block;padding:20px;color:#fff;font-weight:700} +.qgtitle a:hover{background:#5bbac2} .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} @@ -269,7 +270,7 @@ menu a{padding:10px 0} #toggle,.toggle{display:none} .wrap{margin:0 0 0 300px} article{padding:20px 40px 80px 40px} -.moodpic{margin:-20px -40px} +.moodpic{margin:-20px -40px 0 -40px} .breadcrumbs li{display:inline;padding-right:10px} .gchars li{width:19%} @@ -280,7 +281,7 @@ html{overflow-y:scroll;height:100%} body{background:#eae8e4;height:100%} .wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} article{background:#f7f5f2;float:left} -.moodpic{margin:-20px 0 0 -40px;width:880px;height:230px} +.moodpic{width:880px;height:230px} .moodpic img{width:100%} } From f780507ce4a173dd830fc0a74d81111c700a612f Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Apr 2014 17:39:54 +0200 Subject: [PATCH 262/340] charactergroups design update --- views/html/charactergroups/group.tpl | 32 ++++++++--------- views/html/charactergroups/groupsgroup.tpl | 23 ++++++------ views/html/charactergroups/index.tpl | 9 ++--- views/html/charactergroupsquests/quest.tpl | 42 +++++++++++----------- www/css/desktop.css | 35 +++++++++++------- 5 files changed, 78 insertions(+), 63 deletions(-) diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl index ba53c97b..0f6f88d3 100644 --- a/views/html/charactergroups/group.tpl +++ b/views/html/charactergroups/group.tpl @@ -3,23 +3,21 @@ -

              -

              -

              -
              -
              - -
              -
              -

              - -
              -
                -
              • .
              • -
              •  XPs
              • -
              • 1) ? _('Members') : _('Member')?>
              • -
              -
              + +
              + +

              +

              ""

              +
              +
                +
              • .
              • +
              • XP
              • +
              • 1) ? _('Members') : _('Member')?>
              • +

              diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl index 93aeaba7..3d337cf3 100644 --- a/views/html/charactergroups/groupsgroup.tpl +++ b/views/html/charactergroups/groupsgroup.tpl @@ -3,19 +3,22 @@ -

              -

              -

              - -
                - -
              • ( XPs)
              • - + +

                -

                -
                  +
                    + +
                  1. XP
                  2. + +
                  + + +

                  +
                  • diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl index e0f54b94..d88d25eb 100644 --- a/views/html/charactergroups/index.tpl +++ b/views/html/charactergroups/index.tpl @@ -3,10 +3,11 @@ -

                    -

                    - -
                      + +

                      +
                      • diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl index 8112a913..060cf3ab 100644 --- a/views/html/charactergroupsquests/quest.tpl +++ b/views/html/charactergroupsquests/quest.tpl @@ -3,19 +3,23 @@ -

                        -

                        -

                        -

                        + - - - +

                        +Maximale Belohnung: XP
                        -

                        XPs:

                        -

                        +

                        + + + + +

                        @@ -37,15 +41,13 @@

                        - - - - - - - - - - -
                        format(new \DateTime($group['created']))?>/ XPs
                        +
                          + +
                        • + format(new \DateTime($group['created']))?> + + XP +
                        • + +
                        diff --git a/www/css/desktop.css b/www/css/desktop.css index cfff8097..fa06d795 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -113,7 +113,7 @@ aside{display:none} #qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} .qtext{padding-right:15px} -.qtext img{float:right;margin-left:15px;max-width:30%;border-radius:3px} +.qtext img,.grpqimg{float:right;margin-left:15px;max-width:30%;border-radius:3px} .xpinfo{display:none} .xpbar{width:60%;float:left;height:10px;position:relative;background:#eee;border-radius:25px;margin:8px 0 16px} @@ -163,12 +163,19 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .ctopics .xpbar span{background:#50a4ab} +/** Charactergroup List **/ + +.cglist li{list-style-type:decimal;background:#fff;margin:0 0 6px 25px;padding:5px 15px;border-radius:3px} +.cglist .xp{float:right} +.cgqlist li{list-style-type:square;margin-left:25px;padding:2px 15px} + /** Charactergroup Profile **/ -.gdata{margin-top:20px} -.gdata img{border-radius:3px;margin-bottom:15px} -.gdata h1{margin:0;padding:0} -.gdata .fa{color:#aca8a1} +.gbanner img{margin-top:10px;border-radius:3px} +.gbanner h1{margin:10px 0 5px 0} + +.gdata{margin:20px 0 40px 0} +.gdata li{float:left;background:#fff;padding:5px 15px;margin:0 5px 5px 0;border-radius:3px} .gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} .gchars li:nth-child(even){float:right} @@ -182,6 +189,14 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .gquests .xp{display:block} +/** Charactergroup Quest **/ + +.grpqlist li{background:#fff;margin-bottom:6px;padding:5px 15px 5px 0;font-size:.875em;border-radius:3px} +.grpqlist .date{padding:0 15px;border-right:1px solid #dad8d5} +.grpqlist .group{padding:0 15px} +.grpqlist .xp{float:right} + + /** Achievements **/ .rare ol{margin-bottom:40px} @@ -228,9 +243,9 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .cinfo{float:left;width:70%} .cportrait{float:right;width:25%} -.gdata .gbanner{float:left;min-width:100px} -.gdata .gdesc{float:left;width:75%} -.gdata ul{clear:both} +.gbanner img{float:left;margin:20px 20px 0 0} +.gbanner h1{margin:25px 0 2px} + .gchars li{width:32%;margin-right:5px} .gchars li:nth-child(even){float:left} @@ -242,10 +257,6 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo @media only screen and (min-width:768px){ .xpbar{width:70%} -.gdata{margin-top:40px} -.gdata .gdesc{width:60%} -.gdata li{font-size:.875em;padding-bottom:5px} -.gdata ul{clear:none;float:right;width:20%;margin:0;padding:0} .gchars li{width:19%} .gquests .date{display:inline;margin-right:15px} From 5ee9c929c9ab6cfc6d3b4647cea189e81a094a76 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Apr 2014 20:17:32 +0200 Subject: [PATCH 263/340] admin navigation style + character group quest icons --- views/html/charactergroupsquests/quest.tpl | 10 +++++----- views/html/seminaries/edit.tpl | 2 +- views/html/seminaries/index.tpl | 2 +- views/html/seminaries/seminary.tpl | 4 +--- views/html/users/edit.tpl | 2 +- views/html/users/index.tpl | 2 +- views/html/users/user.tpl | 2 +- www/css/desktop.css | 7 +++++-- 8 files changed, 16 insertions(+), 15 deletions(-) diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl index 060cf3ab..6c696737 100644 --- a/views/html/charactergroupsquests/quest.tpl +++ b/views/html/charactergroupsquests/quest.tpl @@ -13,7 +13,7 @@ Maximale Belohnung: XP
                        -

                        +

                        @@ -21,26 +21,26 @@

                        -

                        +

                        -

                        +

                        -

                        +

                        -

                        +

                        • diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl index 66d7efcd..27974c1b 100644 --- a/views/html/seminaries/edit.tpl +++ b/views/html/seminaries/edit.tpl @@ -6,7 +6,7 @@

                          - +

                          diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl index 0f2e8cac..40cdcd7c 100644 --- a/views/html/seminaries/index.tpl +++ b/views/html/seminaries/index.tpl @@ -1,6 +1,6 @@

                          0) : ?> -
                        - +
                        - -

                        -

                        - -

                        +

                        @@ -61,15 +57,15 @@

                        :

                          +
                        • -
                        • +
                        • +
                        - - +

                        -
                        @@ -109,8 +105,6 @@
                      : - - Spiel vorbei
              From 8df82486210d2ea691e0b74c1343cc0a2ee704e4 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 19 Apr 2014 23:23:02 +0200 Subject: [PATCH 282/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 8006 -> 8498 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 155 +++++++++++++------- 2 files changed, 99 insertions(+), 56 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index da63f7b3b151c186d6df09f02dc48db8f943fe82..b809dbd5ed60e006591b7c98acd859e08b552da3 100644 GIT binary patch delta 3243 zcmY+`d2EzL7{~ExxoIgqum}`+fr3CeiwXrnTByh+g(8P?ZM$!`uPpDDeRsKKArg;( z@<%O!#4EuNj-Zs)zi14Hq6vzC#zaUs5)&0P1|t{`#P6>iO`PnrpP6^}oq6VXw>>lY zSVihee)}dv*+vu*#qEtbg2OxWM!DA2n4WkYOYnEh#z9%eYSj0`I2bo$ z4(`LgcnpW(c`P(0W&X4m^69mN4<=y`^iVf$LB=$%U`O18rTCU@UqlW34Jx4=eP!pMqT^Zk0+6z^v8x~=2T!HL{*@(I^h1!*!cpo;|_F+__?_)83 zjDzq7>U+6NU;PfofjAPiBJ+7m=|T+^-S`43^TVi_G^3WR1@rJz)Jk1K&EQ-6`AvKM zH&ntIWTOE)q7u$QO`s>TSf&^?PH8UdugZ8XXzwPY_HG_30T=1hG~g)QilgzUy?z~) z;4SN2RDxZ|mZ~X0B~px<=x|hjRkpn{kM-Bgw{t<6A4CV=MV;O+Pzl~aR@3}t%_3XP ztT*bk4@C7d5jF5i+g^i%Xzxb#(}GIy6b{ADQdCN*+(BhFl<8|}%TNPMvF#bi%T(ER z7_~yHP>HX#Zo*93TajGN^QeTH?C1MYi5x~vDAi0w1D-%-_6h2AUqB^r)A}o($l$TU z^IZRslchtti-%YP96}woDhmEa+K5Kp2G2TBDWa07BUTXnJhW~Fm>Oc3 zDz^eMK!*jkP{Xev5KPq)_E zwlZ$(I*=}*ZPnRP(Q2A4dZR>$!NfE*C=a#KdO!zKUqY-W^aQBr$xzWFth2LP4T^qC zHW3dKlZjb`%1UA*Q9!87*Z3Zlc7#s-Gej|=vVqVeQ%30It|1;Jo+MNrYs0KV$JX_C zKxGWEl}HlL5h`I~De;sV<+b&myVhOp=J*LG>;|=QuOZ>Z{B(WSYZ>YC?6S-rZaj$j zp{VoUSN%lLxIX9gj*)mQ*$||2x=+pMyvl2E5}{yKdT#Eh%uX>s;rU6oadZCdPGwH% z*a@Yjjq3~g|65PLT`)f*RT+;b{fO%awe_L8gzGzj=ZBs0WU$foYU};h&z!o`EphVl zeAlUr*Ttd{*YQ?63*C4$l&s@}xHI4N0(YcS?YU0abA9>=MFXea3lg!oN8h^9^L@8% zFel_Yb6hX#`tyQ>x*hJ+xXh29TxUT@X(Y+Q^AlVMf)F|}4HHcU36Ie$t)HZmD6-8^}6+w9oO(&ULYPqApY)Z>)dd%0I7pde#5{ delta 2773 zcmYk;e@vBC9LMoC`ly3%1BagAc~biB5O}JX{)qa zS2jkWo6R3T4j!+1=( z)0kA8hO==IPQ;e6bH}_wCz}gL(2M6$H(p1cVgANDFow|!(1RMV9F<5dCZQiaxXt=H za;xdJ9zZ4BkK=FqSlAAnNl+?fJ(T&G=@J&Qv^uk@z#}#w!?uxA86SqeN42I?llvSc^)a z1MkM|sEX{j=f_c>KaWay6dh$0$)^&~gDOo1D&uTa2@CA?GE{<x{DM$}B3QJ>#s z&-+j_J&8*AGWzf;YIDXhTP0YSK>ewLna2fLiJDP8YB#SzJ!mU(uj#YrM^PpJ2KAsn zQI-1#XW=-etIb-7O3aU{XuI_#R3&ye_CgP;BzsXAe_;I(-JBmoo^1wDiF|H<|2-;! zVblbEK@D^ZmCy)k^F}e6CYEl^!a*17pHD}d?*{Ll9z33%rp=bdL75k!u2-NcQind= zgj(x2QA_Y1=HMXe?YWM*7|+Wt%TVuqD+leBE@Z-vd54bHvJZ=KKa!KVfO`FYM}05E zGP=&wQ3IEtDzXgqz*VS(*Q4%lLsjB6OvblS6FH32@I*+~{~{g6FcCaG6Q`m!T{$ud zQ;jO|I@A(vu;*>4nRcQEdKZgd| zBnM|>rF9FcQhQK|9zcG~*BrFzu2>VfSqU%1&_qy)RipMq11jMrbkx~QM+3K`Zgfxs zb)ibT3sv%7)KVNm-FL$J8LEO`qAGF;^&B_TRs~`(ACquCE=BgGc{$m>{{b#&ZQe)i z;$x_UhA<1yBF{9pQ5BiM_pn@mO4N_qWUZ*pxCfQMr$}+l8B`^HLQUuzs^Wj8Q2%@A zr1B>!4XbbtHd}j8iJV52@En%nMJ&KHZqoG{RB0QnEvUWIhPwX^R3&!X^S!9OaLBQp zBd7}WqjvQW>Ve;(N64^vX_#!?;EVSp!x{Qb*))Krtp(R^@YEKcWkY-y*%p_hQ)U*UMh-Jhog653z zzhf2s7YWTmn?&t-Vhf?ASxRgow61EMgijsYv_Vg@$>HnldQktibkL^ZL3SWeUs`a;qh@+6^sqxKk4 zuKBm?jMhNR3iXV6+A5wU))Pwzy*?|6)kL^ye`phj+g97rKB*)gB{GR-VgsRlq^AAm zJj~$%o!O?>CYNX;?zh*nu!P7b_>KwvKgYfc{bhtudyu0xU@@VMxsK2)yO_u!HWT^| zQPZmzYKzwfM`ACyg2QnI?m(ueH~5vO)D`q5NjIa5eR=uwi{=I@ycywsaHDsn hEBMyLtFGXV$tT@`)yW?Pr=<8@!S2)ncc9+)!@sB6@kIat diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 8f3f73a2..ddf07c44 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-17 01:46+0100\n" -"PO-Revision-Date: 2014-04-17 01:46+0100\n" +"POT-Creation-Date: 2014-04-19 23:22+0100\n" +"PO-Revision-Date: 2014-04-19 23:22+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -83,12 +83,12 @@ msgid "submitted at %s on %s h" msgstr "eingereicht am %s um %s Uhr" #: questtypes/submit/html/submission.tpl:6 -#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:46 +#: questtypes/submit/html/submission.tpl:8 #: views/html/quests/submissions.tpl:33 msgid "solved" msgstr "gelöst" -#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:48 +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:45 #: views/html/quests/submissions.tpl:24 msgid "unsolved" msgstr "ungelöst" @@ -97,103 +97,115 @@ msgstr "ungelöst" msgid "Error" msgstr "Fehler" -#: views/html/achievements/index.tpl:7 views/html/characters/character.tpl:31 +#: views/html/achievements/index.tpl:9 views/html/characters/character.tpl:31 #: views/html/seminarymenu/index.tpl:4 msgid "Achievements" msgstr "Errungenschaften" -#: views/html/achievements/index.tpl:11 +#: views/html/achievements/index.tpl:10 +msgid "Achievement description" +msgstr "" +"Errungenschaften sind Auszeichnungen für deine Erfolge im Verlauf der Reise. " +"Sie dienen als historische Erinnerungen an Meilensteine, besondere Taten und " +"interessante oder lustige Ereignisse, die du erlebst." + +#: views/html/achievements/index.tpl:13 msgid "Seldom Achievements" msgstr "Die seltensten Errungenschaften" -#: views/html/achievements/index.tpl:25 +#: views/html/achievements/index.tpl:27 #, php-format msgid "Achievement has been achieved only %d times" msgstr "wurde erst %d mal gefunden" -#: views/html/achievements/index.tpl:32 +#: views/html/achievements/index.tpl:33 msgid "Most successful collectors" msgstr "Die erfolgreichsten Sammler" -#: views/html/achievements/index.tpl:38 +#: views/html/achievements/index.tpl:39 #, php-format msgid "Character has achieved %d Achievements" msgstr "hat %d Errungenschaften erhalten" #: views/html/achievements/index.tpl:45 +msgid "Personal Achievements" +msgstr "Deine Errungenschaften" + +#: views/html/achievements/index.tpl:47 #, php-format msgid "Own progress: %d %%" msgstr "Persönlicher Fortschritt: %d %%" -#: views/html/achievements/index.tpl:46 -#: views/html/charactergroups/group.tpl:18 +#: views/html/achievements/index.tpl:52 +#: views/html/charactergroups/group.tpl:17 #: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 msgid "Rank" msgstr "Platz" -#: views/html/achievements/index.tpl:46 +#: views/html/achievements/index.tpl:52 #, php-format msgid "You achieved %d of %d Achievements so far" msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" -#: views/html/achievements/index.tpl:60 views/html/characters/character.tpl:39 +#: views/html/achievements/index.tpl:66 views/html/characters/character.tpl:39 #: views/html/seminarybar/index.tpl:28 #, php-format msgid "achieved at: %s" msgstr "erhalten am: %s" -#: views/html/achievements/index.tpl:71 +#: views/html/achievements/index.tpl:77 msgid "Secret Achievement" msgstr "Geheime Errungenschaft" -#: views/html/achievements/index.tpl:76 +#: views/html/achievements/index.tpl:82 msgid "Continue playing to unlock this secret Achievement" msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" -#: views/html/charactergroups/group.tpl:7 -#: views/html/charactergroups/groupsgroup.tpl:7 -#: views/html/charactergroups/index.tpl:7 +#: views/html/charactergroups/group.tpl:8 +#: views/html/charactergroups/groupsgroup.tpl:8 +#: views/html/charactergroups/index.tpl:9 #: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:3 msgid "Character Groups" msgstr "Charaktergruppen" -#: views/html/charactergroups/group.tpl:20 +#: views/html/charactergroups/group.tpl:19 msgid "Members" msgstr "Mitglieder" -#: views/html/charactergroups/group.tpl:20 +#: views/html/charactergroups/group.tpl:19 msgid "Member" msgstr "Mitglied" -#: views/html/charactergroups/group.tpl:25 +#: views/html/charactergroups/group.tpl:23 #: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 #: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:15 msgid "Characters" msgstr "Charaktere" -#: views/html/charactergroups/group.tpl:40 -#: views/html/questgroups/questgroup.tpl:43 views/html/quests/index.tpl:7 +#: views/html/charactergroups/group.tpl:38 +#: views/html/questgroups/questgroup.tpl:43 views/html/quests/create.tpl:7 +#: views/html/quests/index.tpl:7 msgid "Quests" msgstr "Quests" -#: views/html/charactergroups/groupsgroup.tpl:17 -#: views/html/charactergroupsquests/quest.tpl:7 +#: views/html/charactergroups/groupsgroup.tpl:20 +#: views/html/charactergroupsquests/quest.tpl:9 msgid "Character Groups Quests" msgstr "Charactergruppen-Quests" -#: views/html/charactergroupsquests/quest.tpl:17 +#: views/html/charactergroupsquests/quest.tpl:16 msgid "Description" msgstr "Beschreibung" -#: views/html/charactergroupsquests/quest.tpl:20 +#: views/html/charactergroupsquests/quest.tpl:24 msgid "Rules" msgstr "Regeln" -#: views/html/charactergroupsquests/quest.tpl:27 +#: views/html/charactergroupsquests/quest.tpl:31 msgid "Won Quest" msgstr "Gewonnene Quest" -#: views/html/charactergroupsquests/quest.tpl:33 +#: views/html/charactergroupsquests/quest.tpl:37 msgid "Lost Quest" msgstr "Verlorene Quest" @@ -303,7 +315,7 @@ msgstr "oder" msgid "register yourself" msgstr "registriere dich" -#: views/html/library/index.tpl:7 views/html/library/topic.tpl:7 +#: views/html/library/index.tpl:9 views/html/library/topic.tpl:8 msgid "Questtopics" msgstr "Themen" @@ -324,10 +336,46 @@ msgstr "Kurse" msgid "Logout" msgstr "Logout" -#: views/html/quests/index.tpl:15 +#: views/html/questgroups/create.tpl:7 +msgid "Questgroups" +msgstr "Questgruppen" + +#: views/html/questgroups/create.tpl:8 views/html/questgroups/create.tpl:15 +#: views/html/quests/create.tpl:8 views/html/quests/create.tpl:40 +msgid "Create" +msgstr "Erstellen" + +#: views/html/questgroups/create.tpl:12 views/html/questgroups/create.tpl:13 +#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 +#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 +msgid "Title" +msgstr "Titel" + +#: views/html/quests/create.tpl:12 views/html/quests/create.tpl:13 +#: views/html/users/user.tpl:11 +msgid "Name" +msgstr "Name" + +#: views/html/quests/create.tpl:14 views/html/quests/index.tpl:15 msgid "Questgroup" msgstr "Questgruppe" +#: views/html/quests/create.tpl:27 +msgid "XPs" +msgstr "" + +#: views/html/quests/create.tpl:34 +msgid "Entry text" +msgstr "" + +#: views/html/quests/create.tpl:36 +msgid "Wrong text" +msgstr "" + +#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:53 +msgid "Task" +msgstr "Aufgabe" + #: views/html/quests/index.tpl:21 msgid "Questname" msgstr "Questname" @@ -344,19 +392,23 @@ msgstr "Filter anwenden" msgid "Reset filters" msgstr "Filter zurücksetzen" -#: views/html/quests/quest.tpl:57 -msgid "Task" -msgstr "Aufgabe" - -#: views/html/quests/quest.tpl:63 +#: views/html/quests/quest.tpl:58 msgid "Task already successfully solved" msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" -#: views/html/quests/quest.tpl:66 +#: views/html/quests/quest.tpl:61 msgid "Show answer" msgstr "Lösung anzeigen" -#: views/html/quests/quest.tpl:79 views/html/quests/quest.tpl:88 +#: views/html/quests/quest.tpl:62 +msgid "Skip task" +msgstr "Aufgabe überspringen" + +#: views/html/quests/quest.tpl:67 +msgid "continue" +msgstr "fortfahren" + +#: views/html/quests/quest.tpl:79 views/html/quests/quest.tpl:92 msgid "Quest" msgstr "Quest" @@ -373,12 +425,7 @@ msgstr "eingereicht am %s um %s Uhr" msgid "New seminary" msgstr "Neuer Kurs" -#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 -#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 -msgid "Title" -msgstr "Titel" - -#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:11 +#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:10 msgid "Delete seminary" msgstr "Kurs löschen" @@ -395,7 +442,7 @@ msgstr "löschen" msgid "cancel" msgstr "abbrechen" -#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:10 +#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:9 msgid "Edit seminary" msgstr "Kurs bearbeiten" @@ -407,21 +454,21 @@ msgstr "speichern" msgid "Create new seminary" msgstr "Neuen Kurs erstellen" -#: views/html/seminaries/index.tpl:23 +#: views/html/seminaries/index.tpl:21 #, php-format msgid "created by %s on %s" msgstr "erstellt von %s am %s" -#: views/html/seminaries/index.tpl:25 +#: views/html/seminaries/index.tpl:24 msgid "Create a Character" msgstr "Erstelle einen Charakter" -#: views/html/seminaries/index.tpl:27 +#: views/html/seminaries/index.tpl:26 #, php-format msgid "Your Character “%s” has not been activated yet" msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" -#: views/html/seminaries/seminary.tpl:12 +#: views/html/seminaries/seminary.tpl:11 msgid "Show Quests" msgstr "Quests anzeigen" @@ -461,7 +508,7 @@ msgstr "Nachname" msgid "E‑mail address" msgstr "E‑Mail-Adresse" -#: views/html/users/delete.tpl:2 views/html/users/user.tpl:6 +#: views/html/users/delete.tpl:2 views/html/users/user.tpl:5 msgid "Delete user" msgstr "Benutzer löschen" @@ -470,7 +517,7 @@ msgstr "Benutzer löschen" 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:5 +#: views/html/users/edit.tpl:2 views/html/users/user.tpl:4 msgid "Edit user" msgstr "Benutzer bearbeiten" @@ -478,7 +525,7 @@ msgstr "Benutzer bearbeiten" msgid "Create new user" msgstr "Neuen Benutzer erstellen" -#: views/html/users/index.tpl:10 views/html/users/user.tpl:10 +#: views/html/users/index.tpl:7 views/html/users/user.tpl:10 #, php-format msgid "registered on %s" msgstr "registriert am %s" @@ -577,10 +624,6 @@ msgstr "Das Passwort ist ungültig" msgid "Register" msgstr "Registrieren" -#: views/html/users/user.tpl:11 -msgid "Name" -msgstr "Name" - #: views/html/users/user.tpl:34 msgid "Roles" msgstr "Rollen" From ec8e7cfef0607027f5fc17e9e6eb5f8594820433 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sat, 19 Apr 2014 23:23:54 +0200 Subject: [PATCH 283/340] translations for Achievements page and set Characters progress --- views/html/achievements/index.tpl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 796eecf0..33c967e1 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -7,7 +7,7 @@

            -

            Errungenschaften sind Auszeichnungen für deine Erfolge im Verlauf der Reise. Sie dienen als historische Erinnerungen an Meilensteine, besondere Taten und interessante oder lustige Ereignisse, die du erlebst.

            +

            @@ -42,11 +42,11 @@
            -

            Deine Errungenschaften

            +

            - +

            . : .

            From 94f6a892f1c9f5d129d3027a7cd7d52526438a27 Mon Sep 17 00:00:00 2001 From: coderkun Date: Sun, 20 Apr 2014 00:39:25 +0200 Subject: [PATCH 284/340] fix TextFormatter seminarymedia tagging --- app/TextFormatter.inc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/TextFormatter.inc b/app/TextFormatter.inc index 0bdf1ef9..38fea174 100644 --- a/app/TextFormatter.inc +++ b/app/TextFormatter.inc @@ -61,17 +61,17 @@ $string = nl2br(htmlspecialchars($string)); // Handle Seminarymedia - preg_match_all('/\[seminarymedia:(\d+)\]/iu', $string, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); - foreach($matches as &$match) + $seminarymedia = array(); + preg_match_all('/\[seminarymedia:(\d+)\]/iu', $string, $matches); //, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); + $seminarymediaIds = array_unique($matches[1]); + foreach($seminarymediaIds as &$seminarymediaId) { - // Get Seminary media - $seminarymediaId = intval($match[1][0]); - $htmlImage = null; + $replacement = null; if(!is_null(\hhu\z\controllers\SeminaryRoleController::$seminary) && $this->loadMediaModel()) { try { $medium = self::$Media->getSeminaryMediaById($seminarymediaId); - $htmlImage = sprintf( + $replacement = sprintf( '%s', $this->linker->link(array('media','seminary', \hhu\z\controllers\SeminaryRoleController::$seminary['url'],$medium['url'])), $medium['description'] @@ -81,10 +81,10 @@ } } - // Replace placholder - $startPos = mb_strlen(substr($string, 0, $match[0][1]), 'UTF-8'); - $endPos = $startPos + mb_strlen($match[0][0]); - $string = mb_substr($string, 0, $startPos, 'UTF-8'). $htmlImage . mb_substr($string, $endPos, null, 'UTF-8'); + $seminarymedia[$seminarymediaId] = $replacement; + } + foreach($seminarymedia as $seminarymediaId => $replacement) { + $string = str_replace("[seminarymedia:$seminarymediaId]", $replacement, $string); } From 89b2bee2867eedbfdbc3cb7ea4be0226f0cae940 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 21 Apr 2014 13:17:39 +0200 Subject: [PATCH 285/340] allow NULL-values for answers for Questtype ?Multiple Choice? --- .../multiplechoice/MultiplechoiceQuesttypeController.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc index 456db617..bca8c33a 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -81,12 +81,13 @@ // Get answers $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); - var_dump($userAnswers); - var_dump($answers); // Match answers with user answers foreach($answers as &$answer) { + if(is_null($answer['tick'])) { + continue; + } if($answer['tick']) { if(!array_key_exists($answer['pos']-1, $userAnswers)) { return false; From 8d9d5c3bd51c75ce1a8582bc3deb041a4b4f8f94 Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 21 Apr 2014 13:59:58 +0200 Subject: [PATCH 286/340] add rudimental support for formatting links and tables to class TextFormatter --- app/TextFormatter.inc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/app/TextFormatter.inc b/app/TextFormatter.inc index 38fea174..e226c7e5 100644 --- a/app/TextFormatter.inc +++ b/app/TextFormatter.inc @@ -57,8 +57,22 @@ */ public function t($string) { - // Remove chars and replace linefeeds - $string = nl2br(htmlspecialchars($string)); + // Remove chars + $string = htmlspecialchars($string, ENT_NOQUOTES); + + // Create tables + $string = str_replace('[table]', '

            ', $string); + $string = str_replace('[/table]', '

            ', $string); + $string = str_replace('[tr]', '', $string); + $string = str_replace('[/tr]', '', $string); + $string = str_replace('[th]', '', $string); + $string = str_replace('[/th]', '', $string); + $string = str_replace('[td]', '', $string); + $string = str_replace('[/td]', '', $string); + + // Create links + $string = preg_replace('!(^|\s)"([^"]+)":(https?://[^\s]+)(\s|$)!i', '$1$2$4', $string); + $string = preg_replace('!(^|\s)(https?://[^\s]+)(\s|$)!i', '$1$2$4', $string); // Handle Seminarymedia $seminarymedia = array(); @@ -89,7 +103,7 @@ // Return processed string - return $string; + return nl2br($string); } From 601077518cd43bdc8230712b7bf5927764f5819e Mon Sep 17 00:00:00 2001 From: coderkun Date: Mon, 21 Apr 2014 21:13:58 +0200 Subject: [PATCH 287/340] update view for Questgroup and solved Quest --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 8498 -> 8666 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 29 +++++++++++++------- views/html/questgroups/questgroup.tpl | 2 +- views/html/quests/quest.tpl | 11 +++++--- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index b809dbd5ed60e006591b7c98acd859e08b552da3..e4ce1818495c7fdf88a21227fd34f6caa4c6c141 100644 GIT binary patch delta 3009 zcmY+`drZ}39LMno4tNw6W%hsIBNp88YXlrgw|1dLG*;e-c{LVwGXXkrf&-3uRe4pFV4~zSk zga*b%A2O6KBAJ*OZA=);;`pK@j4);*CSn%m<4AlG$6+Im!kst<51_8^#;JG`@4+j0 zKVHX7jN>5_j0u@SD(Wc3ENsJgd<%8sM>rh6z*xM9e9UFO3h=7!pGX!;AO)3JE{?}y z9F1$On~)q#yKBgpy;S&^7x)^6-57&Cs2Mqqdhj_+#ePh{KQR%uq}r>biD}<^86Mif(vL9e4&Y*5Qi&~vNychdz`z9*UJD7&U z8Fo76qOPk%J+}#`U;smeIbu6{Q8%7PWquPg@HbR~sXSd%mx*I>HfqL}qY__Z|6XPL zeW=9!s22xOiSIxSWZxLppII~q>Cg+`wDzFZ@HlD>&!7^xgydodFc)uN9;T3u`pZ!X zZm>3?65NVhYWAQKIf$Bp*Typcy79E_xPY30Td2$@l7)lGsNK8(m8ch)Wz%TwK#lBK z)b4*7^_+K5FTP;gKcJQ*no;RFnIS65Fb}8WTr5B@DzPJ|sqIC*;CIDf*gErlC)Ib)Y`pc1z*~pip^S_OX_P|lpn)G5eo<|+GJD7`!oH@Ay zd57shZK9V@BYz#Ww#QI=-~`UWQ>cO5Mh0*GMm}a7ql)MKCX0$X7NZiWLQPd2>IEIB z5$s1j;4o^2-o+&R05!6&a54^9qnHjQnugPG9xAa~)XexXM(2MY6^-D4I`A-RIcfm(Qsg}ZzYWJ^0J-7ii^#RlfI#DBk2H76wMeBR0>%T`=|BgB7;mxh)`v$aI$ zU!!u@9JSUevxx1)GGZ6uBXso2i5)~OQB15ORH_KAvzBPEC<&ESL?NMRCntBgz}+`n zbpG3EJVhvbKk+D`QWL?<$CbotqJt=qImW9yQ#;@N`~PN+XyR*h=VlsRRj~02BUx__?T<>R^MvCH%(71kV(w*1vg6voGlL z<~UXUHmAP2-RJnKTU&fyC*A9;uL$IXPmF#(dT2quQ;6257{Ku<Kq;UWl$%yS1VI$!kS&p*29yt4 z0$=n4h5${~QvGCO0OOIM2xv4w0+N`h5MwZk@j(3lyW^zK{$}3Yedn2H=3O{9`AAjr zLQdzVK-)&-5=EUuXu;t(@k0BqM+p7!5|-khn2E(1A>?2Jdtx!t#AnfjE@N-JjyGdA z+38?ps!$f4fIXQ%)X?e98+F(P7o(L}hTeEP=3x`|$M{`}qEqc>NDF;WV;w z!men-z0d{pLy8rO(0LMB)ZflHF1U9S(Y>3ACeVP~GpxZ;*odR?-FW>Hn&8#wbu_{5 zWJ@>nMiVJQ7djlhUu_&O%BKFVd^;D+{1E2j+vw^25>424lk zCpe5Awhz#SoQ~Hop%wZS^RWj5&p;`%CCN}uX9O4KqmS86bg!GE=aKJ*44yp?Q31O0 zQRv=Q;8?6e7qAJ*F>FWv!d_ndJ!y^OPtXJ|Vz$r!&vd-;4K(vimhTO6(aMa*TX8J9 z(%D#m_0bp61P|lwcnm$1KcN-*9W8kV@@ zKX+Ct6HRmgTJjQf0b`;SXd;#9_ot#0)}r5^gKj}XF7KiUts;kl5+fo@% z!BJeFf&PFb(M0wm$0Zy>D{~xO*-3OO&f{?W7x@dNtYRcijc!B}Ihdql#;rI3kDypRlSo#y_3eKQ= zbrIS2@Iy3xaOw+r=!7MhhLeePgp-dZtcInYPxv|8lSDDGo~R{kQ;0FlAMT^$VYr{L zEhRjZPZ93zBSaNpGxH=-O*~F4B=~vg*a`?sh+%eO<9LKE#6xlXKy)ZBAeMW#C*no6 zKFlO6i)}G6ju=7sR7@pi6M4j4gsqh5OLQV;5OsuYWr|P}eFmq;zHB2rCmpM?{O*;_ z!)RMWjPL02KbPq0s3(0=?9Yi-q6f|9IeN6Cm)gpwW8VYWK)9`*9h=n*TYaG|BZd(7 zI-uQ=qT_%T(w|E_NB9KTd@^i4!k(Q~4ru1r z8rljn+SA&uWM-we6=m({+^wuUKQX2v@mg!2!Ty{*0fN9k=Xxg)y#2h3jW Aga7~l diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index ddf07c44..c85999cc 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-19 23:22+0100\n" -"PO-Revision-Date: 2014-04-19 23:22+0100\n" +"POT-Creation-Date: 2014-04-21 21:12+0100\n" +"PO-Revision-Date: 2014-04-21 21:12+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -83,12 +83,12 @@ msgid "submitted at %s on %s h" msgstr "eingereicht am %s um %s Uhr" #: questtypes/submit/html/submission.tpl:6 -#: questtypes/submit/html/submission.tpl:8 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:43 #: views/html/quests/submissions.tpl:33 msgid "solved" msgstr "gelöst" -#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:45 +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:46 #: views/html/quests/submissions.tpl:24 msgid "unsolved" msgstr "ungelöst" @@ -372,7 +372,7 @@ msgstr "" msgid "Wrong text" msgstr "" -#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:53 +#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:55 msgid "Task" msgstr "Aufgabe" @@ -392,23 +392,32 @@ msgstr "Filter anwenden" msgid "Reset filters" msgstr "Filter zurücksetzen" -#: views/html/quests/quest.tpl:58 +#: views/html/quests/quest.tpl:44 +#, php-format +msgid "Quest completed. You have earned %d XPs." +msgstr "Quest abgeschlossen. Du hast %d XPs erhalten." + +#: views/html/quests/quest.tpl:60 msgid "Task already successfully solved" msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" -#: views/html/quests/quest.tpl:61 +#: views/html/quests/quest.tpl:63 msgid "Show answer" msgstr "Lösung anzeigen" -#: views/html/quests/quest.tpl:62 +#: views/html/quests/quest.tpl:64 msgid "Skip task" msgstr "Aufgabe überspringen" -#: views/html/quests/quest.tpl:67 +#: views/html/quests/quest.tpl:69 msgid "continue" msgstr "fortfahren" -#: views/html/quests/quest.tpl:79 views/html/quests/quest.tpl:92 +#: views/html/quests/quest.tpl:76 +msgid "Continuation" +msgstr "Fortsetzung" + +#: views/html/quests/quest.tpl:82 views/html/quests/quest.tpl:95 msgid "Quest" msgstr "Quest" diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 4d3ce2b9..285aa706 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -39,7 +39,7 @@ - + 0) : ?>

              diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index f851166a..5256b4f7 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -10,9 +10,6 @@ 0) : ?>

              - - -
              @@ -40,10 +37,15 @@
              - +
              + +

              +

              +

              +
              @@ -71,6 +73,7 @@
              +

              0) : ?>
                From 49dbfb643ad325cfd79d48def13eaa11020ab854 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 21 Apr 2014 22:59:57 +0200 Subject: [PATCH 288/340] fixed seminary group parallel alignment --- www/css/desktop.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/css/desktop.css b/www/css/desktop.css index 514e3062..83af4e1e 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -268,8 +268,8 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo /** Media Queries **/ @media only screen and (min-width:480px){ -.questgroups li,.ctopics li,.fll48{width:48%;float:left} -.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.questgroups li,.ctopics li,.fll48{width:48%;display:inline-block;margin-right:2%} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;display:inline-block;margin-right:0;vertical-align:top} .xpinfo{display:inline-block;float:left;padding-right:20px} .xpbar{width:50%} From c67663bbc2d727402cf3cbe3f4439b41a68121a2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 21 Apr 2014 23:01:06 +0200 Subject: [PATCH 289/340] character creation form design --- views/html/characters/register.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl index d62c0d35..2d0879ac 100644 --- a/views/html/characters/register.tpl +++ b/views/html/characters/register.tpl @@ -6,7 +6,7 @@

                -
                +
                  &$settings) : ?> From e816eb8aba603115c390c416386e67daa818da70 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 21 Apr 2014 23:34:36 +0200 Subject: [PATCH 290/340] removed bonus quest double list system --- views/html/questgroups/questgroup.tpl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 285aa706..87fd2b29 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -45,16 +45,14 @@
                • class="solved"> - 0) : ?> -
                    - -
                  • - -
                  • - -
                  -
                • + 0) : ?> + +
                • + +
                • + +
                From 4a0f44dfceb00caf2f7d475cae7f2ff6c689d279 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 21 Apr 2014 23:50:27 +0200 Subject: [PATCH 291/340] quest list icon change --- views/html/questgroups/questgroup.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 87fd2b29..84c7177c 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -44,7 +44,7 @@
                • - class="solved"> + class="solved">
                • 0) : ?> From 647428d417a7a030dadc97483dc6326386389724 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 00:30:41 +0200 Subject: [PATCH 292/340] do not show Quest picture on submissions page --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 +++++++++ agents/bottomlevel/MenuAgent.inc | 37 + .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarybarAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/AchievementsAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 + agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/LibraryAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 + agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 + agents/toplevel/BinaryAgent.inc | 41 + agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 70 ++ apis/WebApi.inc | 250 ++++++ app/Controller.inc | 106 +++ app/Model.inc | 42 + app/QuesttypeAgent.inc | 267 ++++++ app/QuesttypeController.inc | 349 ++++++++ app/QuesttypeModel.inc | 154 ++++ app/QuesttypeView.inc | 76 ++ app/TextFormatter.inc | 141 +++ app/ToplevelAgent.inc | 36 + app/Utils.inc | 112 +++ app/controllers/IntermediateController.inc | 139 +++ app/controllers/SeminaryRoleController.inc | 277 ++++++ app/exceptions/FileUploadException.inc | 75 ++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 ++ .../QuesttypeAgentNotValidException.inc | 77 ++ .../QuesttypeControllerNotFoundException.inc | 77 ++ .../QuesttypeControllerNotValidException.inc | 77 ++ .../QuesttypeModelNotFoundException.inc | 77 ++ .../QuesttypeModelNotValidException.inc | 77 ++ .../SubmissionNotValidException.inc | 77 ++ app/exceptions/WrongFiletypeException.inc | 75 ++ app/lib/Password.inc | 316 +++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 199 +++++ configs/CoreConfig.inc | 167 ++++ controllers/AchievementsController.inc | 172 ++++ controllers/BinaryController.inc | 37 + controllers/CharactergroupsController.inc | 150 ++++ .../CharactergroupsquestsController.inc | 91 ++ controllers/CharactersController.inc | 280 ++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 + controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 + controllers/LibraryController.inc | 129 +++ controllers/MediaController.inc | 432 ++++++++++ controllers/MenuController.inc | 52 ++ controllers/QuestgroupsController.inc | 223 +++++ .../QuestgroupshierarchypathController.inc | 90 ++ controllers/QuestsController.inc | 814 ++++++++++++++++++ controllers/SeminariesController.inc | 252 ++++++ controllers/SeminarybarController.inc | 86 ++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 ++++ controllers/UserrolesController.inc | 47 + controllers/UsersController.inc | 363 ++++++++ .../components/AchievementComponent.inc | 41 + controllers/components/AuthComponent.inc | 79 ++ .../components/ValidationComponent.inc | 140 +++ core/Agent.inc | 607 +++++++++++++ core/Api.inc | 163 ++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 +++ core/Component.inc | 85 ++ core/Config.inc | 49 ++ core/Controller.inc | 433 ++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++ core/Logger.inc | 132 +++ core/Model.inc | 141 +++ core/Request.inc | 64 ++ core/Response.inc | 158 ++++ core/View.inc | 124 +++ core/WebUtils.inc | 75 ++ drivers/DatabaseDriver.inc | 87 ++ drivers/MysqliDriver.inc | 169 ++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 ++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 ++ exceptions/ClassNotValidException.inc | 77 ++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 ++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 ++ exceptions/ServiceUnavailableException.inc | 77 ++ exceptions/ViewNotFoundException.inc | 77 ++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 8666 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 691 +++++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 549 ++++++++++++ models/AvatarsModel.inc | 92 ++ models/CharactergroupsModel.inc | 197 +++++ models/CharactergroupsquestsModel.inc | 136 +++ models/CharactersModel.inc | 475 ++++++++++ models/CharactertypesModel.inc | 57 ++ models/DatabaseModel.inc | 83 ++ models/MediaModel.inc | 195 +++++ models/QuestgroupsModel.inc | 635 ++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 +++ models/QuestsModel.inc | 460 ++++++++++ models/QuesttextsModel.inc | 220 +++++ models/QuesttopicsModel.inc | 154 ++++ models/QuesttypesModel.inc | 77 ++ models/SeminariesModel.inc | 193 +++++ models/SeminarycharacterfieldsModel.inc | 78 ++ models/UploadsModel.inc | 148 ++++ models/UserrolesModel.inc | 77 ++ models/UsersModel.inc | 370 ++++++++ models/UserseminaryrolesModel.inc | 78 ++ .../bossfight/BossfightQuesttypeAgent.inc | 24 + .../BossfightQuesttypeController.inc | 244 ++++++ .../bossfight/BossfightQuesttypeModel.inc | 183 ++++ questtypes/bossfight/html/quest.tpl | 51 ++ questtypes/bossfight/html/submission.tpl | 38 + .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 + .../ChoiceinputQuesttypeController.inc | 157 ++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 ++++ questtypes/choiceinput/html/quest.tpl | 15 + questtypes/choiceinput/html/submission.tpl | 12 + .../crossword/CrosswordQuesttypeAgent.inc | 24 + .../CrosswordQuesttypeController.inc | 351 ++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 ++ questtypes/crossword/html/quest.tpl | 49 ++ questtypes/crossword/html/submission.tpl | 48 ++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 203 +++++ .../dragndrop/DragndropQuesttypeModel.inc | 180 ++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 ++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 262 ++++++ .../MultiplechoiceQuesttypeModel.inc | 159 ++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 ++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 ++++ .../textinput/TextinputQuesttypeModel.inc | 117 +++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 +++++++++ responses/WebResponse.inc | 250 ++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/achievement.tpl | 1 + views/binary/media/avatar.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/achievements/index.tpl | 94 ++ views/html/charactergroups/group.tpl | 50 ++ views/html/charactergroups/groupsgroup.tpl | 25 + views/html/charactergroups/index.tpl | 14 + views/html/charactergroupsquests/quest.tpl | 53 ++ views/html/characters/character.tpl | 101 +++ views/html/characters/index.tpl | 17 + views/html/characters/register.tpl | 82 ++ views/html/error/index.tpl | 2 + views/html/html.tpl | 56 ++ views/html/introduction/index.tpl | 36 + views/html/library/index.tpl | 28 + views/html/library/topic.tpl | 30 + views/html/menu/index.tpl | 9 + views/html/questgroups/create.tpl | 16 + views/html/questgroups/questgroup.tpl | 60 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/create.tpl | 41 + views/html/quests/createmedia.tpl | 19 + views/html/quests/index.tpl | 49 ++ views/html/quests/quest.tpl | 113 +++ views/html/quests/submission.tpl | 13 + views/html/quests/submissions.tpl | 37 + views/html/seminaries/create.tpl | 15 + views/html/seminaries/delete.tpl | 13 + views/html/seminaries/edit.tpl | 15 + views/html/seminaries/index.tpl | 31 + views/html/seminaries/seminary.tpl | 40 + views/html/seminarybar/index.tpl | 48 ++ views/html/seminarymenu/index.tpl | 5 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 9 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 90 ++ views/html/users/user.tpl | 35 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 347 ++++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 + www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 +++ 244 files changed, 24121 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/LibraryAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/TextFormatter.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/LibraryController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/AvatarsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/CharactertypesModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttopicsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeAgent.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeController.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeModel.inc create mode 100644 questtypes/bossfight/html/quest.tpl create mode 100644 questtypes/bossfight/html/submission.tpl create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/avatar.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/achievements/index.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/characters/register.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/library/index.tpl create mode 100644 views/html/library/topic.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/create.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/create.tpl create mode 100644 views/html/quests/createmedia.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarybar/index.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..43dff51a --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* 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..49512791 --- /dev/null +++ b/agents/bottomlevel/MenuAgent.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\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) + { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); + } + + } + +?> 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/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\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/LibraryAgent.inc b/agents/intermediate/LibraryAgent.inc new file mode 100644 index 00000000..aedc890a --- /dev/null +++ b/agents/intermediate/LibraryAgent.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 Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryAgent 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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..049ba904 --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,70 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); + } + + + + + 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..461c9fd1 --- /dev/null +++ b/app/Controller.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 text formatter + $this->set('t', new \hhu\z\TextFormatter($this->linker)); + + // Create date and time and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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/TextFormatter.inc b/app/TextFormatter.inc new file mode 100644 index 00000000..e226c7e5 --- /dev/null +++ b/app/TextFormatter.inc @@ -0,0 +1,141 @@ + + * @copyright 2014 Heinrich-Heine-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 to format text with different syntax tags. + * + * @author Oliver Hanraths + */ + class TextFormatter + { + /** + * Linker to create links. + * + * @var Linker + */ + private $linker; + /** + * Media-Model to retrieve media data + * + * @static + * @var model + */ + private static $Media = null; + + + + + /** + * Create a new text formatter. + * + * @param Linker $linker Linker to create links with + */ + public function __construct(\nre\core\Linker $linker) + { + $this->linker = $linker; + } + + + + + /** + * Format a string. + * + * @param string $string String to format + * @return string Formatted string + */ + public function t($string) + { + // Remove chars + $string = htmlspecialchars($string, ENT_NOQUOTES); + + // Create tables + $string = str_replace('[table]', '

                  ', $string); + $string = str_replace('[/table]', '

                  ', $string); + $string = str_replace('[tr]', '', $string); + $string = str_replace('[/tr]', '', $string); + $string = str_replace('[th]', '', $string); + $string = str_replace('[/th]', '', $string); + $string = str_replace('[td]', '', $string); + $string = str_replace('[/td]', '', $string); + + // Create links + $string = preg_replace('!(^|\s)"([^"]+)":(https?://[^\s]+)(\s|$)!i', '$1$2$4', $string); + $string = preg_replace('!(^|\s)(https?://[^\s]+)(\s|$)!i', '$1$2$4', $string); + + // Handle Seminarymedia + $seminarymedia = array(); + preg_match_all('/\[seminarymedia:(\d+)\]/iu', $string, $matches); //, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); + $seminarymediaIds = array_unique($matches[1]); + foreach($seminarymediaIds as &$seminarymediaId) + { + $replacement = null; + if(!is_null(\hhu\z\controllers\SeminaryRoleController::$seminary) && $this->loadMediaModel()) + { + try { + $medium = self::$Media->getSeminaryMediaById($seminarymediaId); + $replacement = sprintf( + '%s', + $this->linker->link(array('media','seminary', \hhu\z\controllers\SeminaryRoleController::$seminary['url'],$medium['url'])), + $medium['description'] + ); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + } + + $seminarymedia[$seminarymediaId] = $replacement; + } + foreach($seminarymedia as $seminarymediaId => $replacement) { + $string = str_replace("[seminarymedia:$seminarymediaId]", $replacement, $string); + } + + + // Return processed string + return nl2br($string); + } + + + + + /** + * Load the Media-Model if it is not loaded + * + * @return boolean Whether the Media-Model has been loaded or not + */ + private function loadMediaModel() + { + // Do not load Model if it has already been loaded + if(!is_null(self::$Media)) { + return true; + } + + try { + // Load class + Model::load('media'); + + // Construct Model + self::$Media = Model::factory('media'); + } + catch(\Exception $e) { + } + + + // Return whether Media-Model has been loaded or not + return !is_null(self::$Media); + } + + } + +?> 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..6f0475df --- /dev/null +++ b/app/Utils.inc @@ -0,0 +1,112 @@ + + * @copyright 2014 Heinrich-Heine-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 + */ + public 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); + } + + + /** + * Send an e‑mail. + * + * @param string $from Sender of mail + * @param mixed $to One (string) or many (array) receivers + * @param string $subject Subject of mail + * @param string $message Message of mail + * @param boolean $html Whether mail should be formatted as HTML or not + * @return Whether mail has been send or not + */ + public static function sendMail($from, $to, $subject, $message, $html=false) + { + // Set receivers + $to = is_array($to) ? implode(',', $to) : $to; + + // Set header + $headers = array(); + $headers[] = 'Content-type: text/'.($html ? 'html' : 'plain').'; charset=UTF-8'; + if(!is_null($from)) { + $headers[] = "From: $from"; + } + $header = implode("\r\n", $headers)."\r\n"; + + + // Send mail + return mail($to, $subject, $message, $header); + } + + } + +?> diff --git a/app/controllers/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..19647d7e --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + self::$user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById(self::$user['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + } + + + /** + * 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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..69e9a38b --- /dev/null +++ b/app/controllers/SeminaryRoleController.inc @@ -0,0 +1,277 @@ + + * @copyright 2014 Heinrich-Heine-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\controllers\IntermediateController + { + /** + * Required components + * + * @var array + */ + public $components = array('achievement', 'auth'); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements'); + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = 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); + + // Get Seminary and Character data + try { + self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); + self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Check achievements + $this->checkAchievements($request, $response); + + // Set Seminary and Character data + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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) + { + // Do not check index page + if(is_null($request->getParam(3))) { + return; + } + + + // 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(!array_key_exists('seminaryroles', self::$user) || count(array_intersect(self::$user['seminaryroles'], $permissions)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Check if Character is present + if(is_null(self::$character)) { + return; + } + + // Get unachieved Achievments + $achievements = $this->Achievements->getUnachhievedAchievementsForCharacter(self::$seminary['id'], self::$character['id']); + if(in_array('user', self::$user['seminaryroles'])) { + $achievements = array_merge($achievements, $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary(self::$seminary['id'])); + } + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + self::$character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + self::$character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + self::$character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], self::$character['id']); + } + } + } + + } + +?> diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..f7ed6931 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 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', + 'mailsender' => 'noreply@zyren.inf-d.de' + ); + + + /** + * 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Media sizes + * + * @static + * @var array + */ + public static $media = array( + 'questgroup' => array( + 'width' => 480, + 'height' => 5000 + ), + 'avatar' => array( + 'width' => 500, + 'height' => 500 + ) + ); + + + /** + * Miscellaneous settings + * + * @static + * @var array + */ + public static $misc = array( + 'ranking_range' => 2, + 'achievements_range' => 3 + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ) + ); + + + /** + * 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|register|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|register))', '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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..d99671b6 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,172 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media', 'characters'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get seldom Achievements + $seldomAchievements = $this->Achievements->getSeldomAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range']); + foreach($seldomAchievements as &$achievement) { + $achievement['achieved'] = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + } + + // Get Characters with the most Achievements + $successfulCharacters = $this->Characters->getCharactersWithMostAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range']); + + // Get achieved Achievements + $achievedAchievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get unachieved Achievements + $unachievedAchievements = $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id'], true); + foreach($unachievedAchievements as &$achievement) + { + // Get Character progress + if($achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + } + + // Get ranking + $character['rank'] = $this->Achievements->getCountRank($seminary['id'], count($achievedAchievements)); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('seldomAchievements', $seldomAchievements); + $this->set('successfulCharacters', $successfulCharacters); + $this->set('achievedAchievements', $achievedAchievements); + $this->set('unachievedAchievements', $unachievedAchievements); + } + + } + +?> diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc new file mode 100644 index 00000000..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..d4cf6cee --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,150 @@ + + * @copyright 2014 Heinrich-Heine-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', 'avatars', '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: 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); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); + + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + // Get Character groups Quests + $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc new file mode 100644 index 00000000..fb9d18c8 --- /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->getSeminaryMediaById($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..7046e1a1 --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,280 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class CharactersController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media', 'questtopics'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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']); + + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get ranking + $ranking = array( + 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']), + 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']) + ); + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('characterfields', $characterfields); + $this->set('user', $user); + $this->set('groups', $groups); + $this->set('achievements', $achievements); + $this->set('ranking', $ranking); + $this->set('questtopics', $questtopics); + } + + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Check for already existing Character + try { + $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + throw new \nre\exceptions\AccessDeniedException(); + } + catch(\nre\exceptions\IdNotFoundException $e) { + // The should be the case + } + + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + if($this->Characters->characterNameExists($charactername)) { + $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true); + } + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Send mail + $this->sendRegistrationMail($charactername); + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + + + + + /** + * Send mail for new Character registration. + * + * @param string $charactername Name of newly registered Character + */ + private function sendRegistrationMail($charactername) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new Character registration: %s', $charactername); + $message = sprintf('User “%s” <%s> has registered a new Character “%s” for the Seminary “%s”', self::$user['username'], self::$user['email'], $charactername, self::$seminary['title']); + $moderators = $this->Users->getUsersWithSeminaryRole(self::$seminary['id'], 'moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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..dd4bc86c --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + $this->set('loggedCharacter', SeminaryRoleController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc new file mode 100644 index 00000000..a37618ae --- /dev/null +++ b/controllers/LibraryController.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\controllers; + + + /** + * Controller of the LibraryAgent to list Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('questtopics', 'seminaries', 'quests', 'questgroups'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Questtopics of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + // Get Quest count + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + + // Get Character progress + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopics', $questtopics); + } + + + /** + * Action: topic. + * + * Show a Questtopic and its Quests with Questsubtopics. + * + * @throws IdNotFoundException + * @param string $eminaryUrl URL-Title of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + */ + public function topic($seminaryUrl, $questtopicUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Questtopic + $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl); + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForQuesttopic($questtopic['id']) as $quest) + { + if($this->Quests->hasCharacterEnteredQuest($quest['id'], $character['id'])) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + + // Get Subtopics + $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']); + + $quests[] = $quest; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopic', $questtopic); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..e6bb69d2 --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,432 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user'), + 'seminary' => array('admin', 'moderator', 'user'), + 'avatar' => array('admin', 'moderator', 'user'), + 'achievement' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'achievements', 'media', 'avatars'); + + + + + /** + * 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. + * + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: avatar. + * + * Display an Avatar as full size or portrait. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $charactertypeUrl URL-title of Character type + * @param int $xplevel XP-level + * @param string $action Size to show (avatar or portrait) + */ + public function avatar($seminaryUrl, $charactertypeUrl, $xplevel, $action='avatar') + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Avatar + $avatar = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $charactertypeUrl, $xplevel); + + // Get media + switch($action) + { + case null: + case 'avatar': + $media = $this->Media->getSeminaryMediaById($avatar['avatarpicture_id']); + $file = $this->getMediaFile($media, 'avatar'); + break; + case 'portrait': + $media = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + $file = $this->getMediaFile($media); + break; + default: + throw new \nre\exceptions\ParamsNotValidException($action); + break; + } + + // Get file + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + + + /** + * 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; + } + + + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + \nre\configs\AppConfig::$media['questgroup']['width'], + \nre\configs\AppConfig::$media['questgroup']['height'] + ); + } + break; + case 'avatar': + $file = self::resizeImage( + $media['filename'], + $format, + \nre\configs\AppConfig::$media['avatar']['width'], + \nre\configs\AppConfig::$media['avatar']['height'] + ); + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } + + + /** + * Get supported image types. + * + * @return array List of supported image types + */ + private static function getImageTypes() + { + $im = new \Imagick(); + + + return $im->queryFormats(); + } + + + /** + * Resize an image. + * + * @param string $fileName Absolute pathname of image to resize + * @param string $mimeType Mimetype of target image + * @param int $width Max. width to resize to + * @param int $height Max. height to resize to + * @return mixed Resized image + */ + private static function resizeImage($fileName, $mimeType, $width, $height) + { + // Read image from cache + $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$width.'x'.$height; + if(file_exists($tempFileName)) + { + // Check age of file + if(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'] < $width) { + $width = $geometry['width']; + } + if($geometry['height'] < $height) { + $height = $geometry['width']; + } + + // Process + $im->thumbnailImage($width, $height, true); + $im->contrastImage(1); + $im->setImageFormat($mimeType); + + // Save temporary file + $im->writeImage($tempFileName); + + + // Return resized image + return $im; + } + + } + +?> diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc new file mode 100644 index 00000000..b7557c1f --- /dev/null +++ b/controllers/MenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the 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', IntermediateController::$user); + $this->set('loggedCharacter', SeminaryRoleController::$character); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..1064be74 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,223 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'questgroup' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'questgroup' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator') + ); + + + + + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permission + if(count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + $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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1 && count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && ($currentQuest['solved'] || count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) > 0)); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + + /** + * Action: create. + * + * Create a new Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function create($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Create Questgroup + $validation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // TODO Validation + $title = $this->request->getPostParam('title'); + + // Create new Questgroup + if($validation === true) + { + $questgroupId = $this->Questgroups->createQuestgroup( + $this->Auth->getUserId(), + $seminary['id'], + $title + ); + + // Redirect + $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url']))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..6da2906d --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,814 @@ + + * @copyright 2014 Heinrich-Heine-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( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator', 'user'), + 'submission' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator'), + 'create' => array('admin', 'moderator') + ); + + + + + /** + * Action: index. + * + * List all Quests for a Seminary. + * + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Prepare filters + $filters = array( + 'questgroups' => array(), + 'questtypes' => array() + ); + + // Get selected filters + $selectedFilters = array( + 'questgroup' => "0", + 'questtype' => "" + ); + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('filters'))) { + $selectedFilters = $this->request->getPostParam('filters'); + } + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForSeminary($seminary['id']) as $quest) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + if($selectedFilters['questgroup'] != "0" && $selectedFilters['questgroup'] != $quest['questgroup']['id']) { + continue; + } + + // Get Questtype + $quest['questtype'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + if($selectedFilters['questtype'] != "" && $selectedFilters['questtype'] != $quest['questtype']['classname']) { + continue; + } + + // Add filter values + $filters['questgroups'][$quest['questgroup']['id']] = $quest['questgroup']; + $filters['questtypes'][$quest['questtype']['classname']] = $quest['questtype']; + + // Add open submissions count + $quest['opensubmissionscount'] = count($this->Characters->getCharactersSubmittedQuest($quest['id'])); + + $quests[] = $quest; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('quests', $quests); + $this->set('filters', $filters); + $this->set('selectedFilters', $selectedFilters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Has Epilog? + $hasEpilog = ($this->Questtexts->getQuesttextCountOfQuest($quest['id'], 'Epilog') > 0); + + // Quest status + $questStatus = $this->request->getGetParam('status'); + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Task + $task = null; + if($questtexttypeUrl == 'Prolog') + { + // Questtype + $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + + // Render task + if(!is_null($questtype['classname'])) { + $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); + } + else { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + } + + // Has Character solved quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Next Quest/Questgroup + $nextQuests = null; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog' || (is_null($questtype['classname']) && !$hasEpilog)) + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('hasEpilog', $hasEpilog); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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); + } + + + /** + * Action: create. + * + * Create a new Quest. + * + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function create($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Quest groups + $questgroups = $this->Questgroups->getQuestgroupsForSeminary($seminary['id']); + + // Quest types + $questtypes = $this->Questtypes->getQuesttypes(); + + // Create Quest + $validation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // TODO Validation + $name = $this->request->getPostParam('name'); + $xps = $this->request->getPostParam('xps'); + $prolog = $this->request->getPostParam('prolog'); + $entrytext = $this->request->getPostParam('entrytext'); + $wrongtext = $this->request->getPostParam('wrongtext'); + $task = $this->request->getPostParam('task'); + + // Validate Questgroup + $questgroupIndex = null; + foreach($questgroups as $index => &$questgroup) + { + $questgroup['selected'] = ($questgroup['url'] == $this->request->getPostParam('questgroup')); + if($questgroup['selected']) { + $questgroupIndex = $index; + } + } + if(is_null($questgroupIndex)) { + throw new \nre\exceptions\ParamsNotValidException($questgroup); + } + + // Validate Questtype + $questtypeIndex = null; + foreach($questtypes as $index => &$questtype) + { + $questtype['selected'] = ($questtype['url'] == $this->request->getPostParam('questtype')); + if($questtype['selected']) { + $questtypeIndex = $index; + } + } + if(is_null($questtypeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($questtype); + } + + // Questmedia + $questmedia = null; + if(array_key_exists('questmedia', $_FILES) && !empty($_FILES['questmedia']) && $_FILES['questmedia']['error'] == 0) { + $questmedia = $_FILES['questmedia']; + } + + // Process Prolog + if(!empty($prolog)) { + $prolog = preg_split('/\s*(_|=){5,}\s*/', $prolog, -1, PREG_SPLIT_NO_EMPTY); + } + + // Create new Quest + if($validation === true) + { + $questId = $this->Quests->createQuest( + $this->Auth->getUserId(), + $name, + $questgroups[$questgroupIndex]['id'], + $questtypes[$questtypeIndex]['id'], + $xps, + $entrytext, + $wrongtext, + $task + ); + + // Create Questmedia + if(!is_null($questmedia)) + { + $questsmediaId = $this->Media->createQuestMedia( + $this->Auth->getUserId(), + $seminary['id'], + $questmedia['name'], + $name, + $questmedia['type'], + $questmedia['tmp_name'] + ); + if($questsmediaId > 0) { + $this->Quests->setQuestmedia($questId, $questsmediaId); + } + } + + // Add Prolog-texts + if(!empty($prolog)) { + $this->Questtexts->addQuesttextsToQuest($this->Auth->getUserId(), $questId, 'Prolog', $prolog); + } + + + // Redirect + $this->redirect($this->linker->link(array('quests', 'index', $seminary['url']))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroups', $questgroups); + $this->set('questtypes', $questtypes); + } + + + /** + * Action: createmedia. + * TODO only temporary for easier data import. + * + * Display a form for creating new Seminary media. + * + * @param string $seminaryUrl URL-title of Seminary + */ + public function createmedia($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Create media + $mediaId = null; + $error = null; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + $file = $_FILES['file']; + $error = $file['error']; + if(empty($error)) + { + $mediaId = $this->Media->createQuestMedia( + $this->Auth->getUserId(), + $seminary['id'], + $file['name'], + $this->request->getPostParam('description'), + $file['type'], + $file['tmp_name'] + ); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('mediaid', $mediaId); + } + + + + + /** + * Render and handle the task of a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTask($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('quest', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Get user answers + $answers = $this->request->getPostParam('answers'); + + // Save answers in database + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..06f95652 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,252 @@ + + * @copyright 2014 Heinrich-Heine-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', 'userseminaryroles', '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) + { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1 && count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($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 + $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/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..88c1037e --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,86 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('characters', 'quests', 'questgroups', 'achievements', 'charactergroups', 'avatars', 'media'); + + + + + /** + * Action: index. + */ + public function index() + { + if(is_null(SeminaryRoleController::$seminary)) { + return; + } + + // Get Seminary + $seminary = SeminaryRoleController::$seminary; + + // Get Character + $character = SeminaryRoleController::$character; + if(is_null($character)) { + return; + } + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + + // Get Character group members + $characterGroups = array(); + foreach($this->Charactergroups->getGroupsForCharacter($character['id']) as $group) + { + $groupsgroup = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']); + if($groupsgroup['preferred']) + { + $group['members'] = $this->Characters->getCharactersForGroup($group['id']); + $characterGroups[] = $group; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); + $this->set('characterGroups', $characterGroups); + } + + } + +?> diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..8f2957d7 --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController extends \hhu\z\controllers\SeminaryRoleController + { + + + + + /** + * 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', self::$user); + $this->set('loggedSeminary', self::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..90e50912 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,363 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + + // Level + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + if($this->Users->usernameExists($username)) { + $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true); + } + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + if($this->Users->emailExists($email)) { + $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true); + } + + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Send mail + $this->sendRegistrationMail($username, $email); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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); + } + + + + + /** + * Send mail for new user registration. + * + * @param string $username Name of newly registered user + * @param string $email E‑mail address of newly registered user + */ + private function sendRegistrationMail($username, $email) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new user registration: %s', $username); + $message = sprintf('User “%s” <%s> has registered themself to %s', $username, $email, \nre\configs\AppConfig::$app['name']); + $moderators = $this->Users->getUsersWithRole('moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..950d45ed --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,140 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $settings) + { + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Add a custom determined validation result to a validation + * store. + * + * @param mixed $validation Validation store to add result to + * @param string $param Name of parameter of the custom validation result + * @param string $setting Name of setting of the custom validation result + * @param mixed $result Validation result + * @return mixed The altered validation store + */ + public function addValidationResult($validation, $param, $setting, $result) + { + if(!is_array($validation)) { + $validation = array(); + } + if(!array_key_exists($param, $validation)) { + $validation[$param] = array(); + } + $validation[$param][$setting] = $result; + + + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws 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..e8c4fa0f --- /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) + { + // Delete critical signs + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, '', $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = $param; + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                  \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..e4ce1818495c7fdf88a21227fd34f6caa4c6c141 GIT binary patch literal 8666 zcmai&dyFKdw1*IJ)h4!oSvE5 znZE7mp7dk)cJts8D*+J+NJJ6^U3f^A9U$T%BEui27_>nMMMN={Ksd-qr${LIBLoux zLE`cGR(H?LKGWLYe7dTutAF*Y-|tsd`|8a%e9CY=gS-Vfe1kE63TJQPhwHWT@W^Pf?28f5y+qU zReoL%ufQLG&q3Ma3sC+34ZIV+0&j!=4R43HprqP0p!9kaO5gKP{WReZ!fsXWL$zCh z8qYIO?SG{ze+sJo75J0zS;&-_7opz!GSt+(1b+g)QkB00rRVqH-EfRSkHdpd?ao2< zy9|FCW{^MgnX3Fe)O%ln()&AbAN&s}{qCf5+3jBV?(mR3r zZVsjQ2cX9BQHTl6C!xOgI~AXUn!hhV&EJbq-+vjRig^_tgx`jf@D7ww{aGmep02nI zrQa%~srfLJ9-o97|8GLQ_ob@*RjBd52Br6{C}H7Sp#1nKl%8#fiOt1|Yf$6*WhlS@ z1XMr22ld^rR^@+&nvWY8l=`_BO20|CA5OzVunncxXQ1r*JXHUGSe5?_@@KwOmH!UP zKK}@%_ctnj6TXh}w;@9^{|2Sc^#qsNy%9=}+o8sDC)D>Qp!9k>lz%@2_5BMKoA58L z!;j$CsK1XOmY@C)RR6bO1o_*3sBt_5Reu)pXWq+?g_oe_|Fcl@@jPt6uRz7w_uxTz zJHaNNg31?bQ2z7lP~-j_)VzKkYX1Kiz5`x`8pn4bLpT2m`7<{&C{#BGpvv!t(&GY@ zojOqcuR-bmF{t-H4P~F-hi`^|0yVC`g>QwgR=f^lNYA_BJ@5#WUM(oQBv5w$D3tym zugaf>8ut~b?_7oY{sxr&UxsS`Pf*|g7pQr>o`ULcf||!$p!9hw)O$0KrsfR17lsvo z14_>qq3rkfQ2M?K8H%})q$a<#6(5DtJBOO5b*OQD9?D){gwppfq3r$=)HuFg@fwsq z--PPtHK_0Y7u5S&95fGafSR8-L%ly)@f}e5z6;7u=b-v2pvM1xI0Zijr{SMNLeBg< zlwLR9QO?(!pyuHoD0>}+(rXs(gXf^^^&u#GeYWD4q4fP%co6<4l;7TiGScHQD1Uk! z$}Sh7#O<@=%h<((CeLHX}VD8D}s)o%!8_Y6w^b*ORw62vvkuT}g5sP=yi zWxto;b#MV0z1kEsFS6Cch{ojS8)}|)y&ZW!GJ|{wafo8>Eb;-Qg&arDBf2ginq$pR zeMtvhXOR05*&LPpTmkPheYk@B0wUcLWD(KTEMeXWA448T){v8ku6HAvUtO}p34d4G zPIlI%_`%)${Qn$WMScePS>zm&A@%h%H@}FytE!N1JcGOkIgLDoybjUz2r}pI*mprv z3oldy1j@(!5H_r1f_z5Gkp0VMQy!=KCCcB|qfdkBWLrqzMl8{G^ZkWX=Wbo z25HdZ);^LZMSpvReY|j4K3XpY2H)nhBg&07i-VpU>0U(}M5znfYt~%~H}~nEwvspx z!Z@>G6uC|i*;cLG)_!dmuLM!p-r9ed**r;Xl*ApouNPdJkXh7AwtttVSvN^@+J^DY zZTo4`cWEACxS^?J$YD`Wk|d)B{lmBj{FGG%XZumG7NSp{*dmUS*0RmJ=$W}z>he+J znTIh>PB$KQ@VUAo*j^R8RXRfXAYH4QsjkReYEHPwjs{IyG{>)I+LMGgk9oOZn?Rx6R2(|VE>tro*tD)7Ywr)=d3F>^+qeP-13DtMcn zU5za+(~&W-Q2W~DKN=r5b3vA^CTV+cGZ=RL#O~#}TN~}{vvY)V5)(DYhDjtdbE)%z zSdn4u9%FTzI=BYqJ)&w$o1InjDa80+6zKKkm+c9AAt`J(SYh6SG$u(At542l6J^7W z($UCmR^F2h)h=1$x2*BQtCEyQp7g_3)!awwd=M`i{oul8-gU@5OmumZmeD%zGJ>&A zit|ZCyw4Zq zsMq~E4mNorW+KO>YMBlfjG>8?N#i81w-9-_!kW{zYc4l~j)UB1_f`-?Wtn&AN_@ zeI9vgZ0_u0!!No#ZJc0;)ArEh)ZxbD;l`mUduVEU>d=A7smVziHs;+*s9yGLr-i7o zm=bnTz=|<#$7X}LQv@B?Sad;eq&!X7KYe0m?DVPAGeZTZCML(Ue_$KfSY*AMwz<2M zA7m#H#z)HijLVOnTRho#aM(_tTXJb*CT=BdGTpR&uo>oKPc|NLu_Mh%kGaHYv7efF zVB#=O%3|Yqkhy-4cc<;d#KH1!Y%VI&AZnaU(jE(0-1o)o=%FL*XY}OgzS!P%)K2X` zGVEu8$!$69TS%tUw$yL%l?AeVt2@Vc8k5s@C3LIV$g9JLQ`7c++Hxv&XJgub6ftv^ zv5i~8B5V$rw_`=N?!s0#_Rs9n#?=%{hOx6V=~5DPoDF-H36Fwei3h2jcVXrx>_SMQ z4KWDY=P0vm#IQ?9-+D2OF;v-M5Zlv^TpF`f(CdAK3yUiqIlG91#BAl-q#D){7Wx3% zL|=)DED!nkOzOf;Ooru6KF9XSqKj*0`Gi?5(l$$8%JzZ)?val=Zi(((ylt#hFn}h# z%PYerCPM+pjz}B!AYR3VdCl(>m#ONcMZaHHXj5NRG^)j*t9WUn-4>I&v>VXnNKZa1 zp5P;czExB1+e2atdoV zM_+ZffA{vwMY=xR)eqVaM#JvQrV4f7j3*ChmOsl!AK2H(5tDD_{L2Fuc@(&G#{xu=`cWQN-*g} zm3PH0XModesvF1r5y1IF!Dz)!UPDaiD6py77FVE&{}fx}+~NXk2seJ)&$zEZ)rC)krIYq?VCiZ(Ah97Nb+kP6C| zlvL)}2&`{8gQO%<+EnKd3i8z|x33rJ#?{ucl8&0_?CD#&C^nCjiDpY0_uj_V3Mcc9 zwl&H7aCiQ93FpQI7wFZmjXn_!Vrex@m*saPv@&nxBs-R@n3GBUM(wA=2=r%@JzgCg zPgu5CxyinzarjqKU7jUxDdq+h$BLy+z{zOiDyvP_*MA^*t#0N`ufsUI00ooWt}c9E zzZ*2%?s%IX?GZ1dpR1g7mw2hhVQN3qj-Vb}wT_PFiOTS0U~6tPUqef8)|g`sTXeY_ z2U(^4P1&McxysBjSPll(s%LSJrG0ag?-R_M8gEuple6vd-p1Xj`7Q>on-UtU37fb< zR<14H4B3g}T2;jIKSmNj(A#Y{_J-H zk&3+PDXUhz*!Rh&%fzJ*P$&Z&r4?A^n9KLHs~OMixo*1WGlJEph}@uE9xij-`hP=b B5pw_l literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..c85999cc --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,691 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-21 21:12+0100\n" +"PO-Revision-Date: 2014-04-21 21:12+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/bossfight/html/quest.tpl:11 +#: questtypes/bossfight/html/quest.tpl:24 +#: questtypes/bossfight/html/submission.tpl:21 +#: questtypes/bossfight/html/submission.tpl:33 +msgid "lost" +msgstr "verloren" + +#: questtypes/bossfight/html/quest.tpl:41 +msgid "Choose" +msgstr "Wählen" + +#: questtypes/bossfight/html/quest.tpl:47 +#: questtypes/choiceinput/html/quest.tpl:14 +#: questtypes/crossword/html/quest.tpl:30 +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/crossword/html/quest.tpl:22 +#: questtypes/crossword/html/submission.tpl:22 +msgid "vertical" +msgstr "vertikal" + +#: questtypes/crossword/html/quest.tpl:24 +#: questtypes/crossword/html/submission.tpl:24 +msgid "horizontal" +msgstr "horizontal" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:43 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:46 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/achievements/index.tpl:9 views/html/characters/character.tpl:31 +#: views/html/seminarymenu/index.tpl:4 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/achievements/index.tpl:10 +msgid "Achievement description" +msgstr "" +"Errungenschaften sind Auszeichnungen für deine Erfolge im Verlauf der Reise. " +"Sie dienen als historische Erinnerungen an Meilensteine, besondere Taten und " +"interessante oder lustige Ereignisse, die du erlebst." + +#: views/html/achievements/index.tpl:13 +msgid "Seldom Achievements" +msgstr "Die seltensten Errungenschaften" + +#: views/html/achievements/index.tpl:27 +#, php-format +msgid "Achievement has been achieved only %d times" +msgstr "wurde erst %d mal gefunden" + +#: views/html/achievements/index.tpl:33 +msgid "Most successful collectors" +msgstr "Die erfolgreichsten Sammler" + +#: views/html/achievements/index.tpl:39 +#, php-format +msgid "Character has achieved %d Achievements" +msgstr "hat %d Errungenschaften erhalten" + +#: views/html/achievements/index.tpl:45 +msgid "Personal Achievements" +msgstr "Deine Errungenschaften" + +#: views/html/achievements/index.tpl:47 +#, php-format +msgid "Own progress: %d %%" +msgstr "Persönlicher Fortschritt: %d %%" + +#: views/html/achievements/index.tpl:52 +#: views/html/charactergroups/group.tpl:17 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/achievements/index.tpl:52 +#, php-format +msgid "You achieved %d of %d Achievements so far" +msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" + +#: views/html/achievements/index.tpl:66 views/html/characters/character.tpl:39 +#: views/html/seminarybar/index.tpl:28 +#, php-format +msgid "achieved at: %s" +msgstr "erhalten am: %s" + +#: views/html/achievements/index.tpl:77 +msgid "Secret Achievement" +msgstr "Geheime Errungenschaft" + +#: views/html/achievements/index.tpl:82 +msgid "Continue playing to unlock this secret Achievement" +msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" + +#: views/html/charactergroups/group.tpl:8 +#: views/html/charactergroups/groupsgroup.tpl:8 +#: views/html/charactergroups/index.tpl:9 +#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:3 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:19 +msgid "Members" +msgstr "Mitglieder" + +#: views/html/charactergroups/group.tpl:19 +msgid "Member" +msgstr "Mitglied" + +#: views/html/charactergroups/group.tpl:23 +#: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:15 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:38 +#: views/html/questgroups/questgroup.tpl:43 views/html/quests/create.tpl:7 +#: views/html/quests/index.tpl:7 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:20 +#: views/html/charactergroupsquests/quest.tpl:9 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:16 +msgid "Description" +msgstr "Beschreibung" + +#: views/html/charactergroupsquests/quest.tpl:24 +msgid "Rules" +msgstr "Regeln" + +#: views/html/charactergroupsquests/quest.tpl:31 +msgid "Won Quest" +msgstr "Gewonnene Quest" + +#: views/html/charactergroupsquests/quest.tpl:37 +msgid "Lost Quest" +msgstr "Verlorene Quest" + +#: views/html/characters/character.tpl:16 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:20 +#: views/html/characters/character.tpl:69 +#: views/html/characters/character.tpl:75 +#: views/html/characters/character.tpl:81 views/html/seminarybar/index.tpl:42 +#: views/html/users/user.tpl:29 +msgid "Level" +msgstr "Level" + +#: views/html/characters/character.tpl:63 +msgid "Ranking" +msgstr "Ranking" + +#: views/html/characters/character.tpl:89 +msgid "Topic progress" +msgstr "Thematischer Fortschritt" + +#: views/html/characters/register.tpl:7 +msgid "Create Character" +msgstr "Charakter erstellen" + +#: views/html/characters/register.tpl:20 +#, php-format +msgid "Character name is too short (min. %d chars)" +msgstr "Der Charaktername ist zu kurz (min. %d Zeichen)" + +#: views/html/characters/register.tpl:22 +#, php-format +msgid "Character name is too long (max. %d chars)" +msgstr "Der Charaktername ist zu lang (max. %d Zeichen)" + +#: views/html/characters/register.tpl:24 +msgid "Character name contains illegal characters" +msgstr "Der Charaktername enthält ungültige Zeichen" + +#: views/html/characters/register.tpl:26 +msgid "Character name already exists" +msgstr "Der Charaktername existiert bereits" + +#: views/html/characters/register.tpl:28 +msgid "Character name invalid" +msgstr "Der Charaktername ist ungültig" + +#: views/html/characters/register.tpl:40 +msgid "Character properties" +msgstr "Charaktereigenschaften" + +#: views/html/characters/register.tpl:41 views/html/characters/register.tpl:42 +msgid "Character name" +msgstr "Charaktername" + +#: views/html/characters/register.tpl:43 +msgid "Character type" +msgstr "Charaktertyp" + +#: views/html/characters/register.tpl:54 +#, php-format +msgid "The Seminary field “%s” is invalid" +msgstr "Das Kursfeld „%s“ ist ungültig" + +#: views/html/characters/register.tpl:59 +msgid "Seminary fields" +msgstr "Kursfelder" + +#: views/html/characters/register.tpl:81 views/html/seminaries/create.tpl:14 +#: views/html/users/create.tpl:17 +msgid "create" +msgstr "erstellen" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 +#: views/html/menu/index.tpl:6 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:86 views/html/users/register.tpl:87 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:14 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:14 +msgid "register yourself" +msgstr "registriere dich" + +#: views/html/library/index.tpl:9 views/html/library/topic.tpl:8 +msgid "Questtopics" +msgstr "Themen" + +#: views/html/menu/index.tpl:2 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:3 views/html/seminaries/create.tpl:6 +#: views/html/seminaries/delete.tpl:6 views/html/seminaries/edit.tpl:6 +#: views/html/seminaries/index.tpl:1 +msgid "Seminaries" +msgstr "Kurse" + +#: views/html/menu/index.tpl:8 +msgid "Logout" +msgstr "Logout" + +#: views/html/questgroups/create.tpl:7 +msgid "Questgroups" +msgstr "Questgruppen" + +#: views/html/questgroups/create.tpl:8 views/html/questgroups/create.tpl:15 +#: views/html/quests/create.tpl:8 views/html/quests/create.tpl:40 +msgid "Create" +msgstr "Erstellen" + +#: views/html/questgroups/create.tpl:12 views/html/questgroups/create.tpl:13 +#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 +#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 +msgid "Title" +msgstr "Titel" + +#: views/html/quests/create.tpl:12 views/html/quests/create.tpl:13 +#: views/html/users/user.tpl:11 +msgid "Name" +msgstr "Name" + +#: views/html/quests/create.tpl:14 views/html/quests/index.tpl:15 +msgid "Questgroup" +msgstr "Questgruppe" + +#: views/html/quests/create.tpl:27 +msgid "XPs" +msgstr "" + +#: views/html/quests/create.tpl:34 +msgid "Entry text" +msgstr "" + +#: views/html/quests/create.tpl:36 +msgid "Wrong text" +msgstr "" + +#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:55 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/index.tpl:21 +msgid "Questname" +msgstr "Questname" + +#: views/html/quests/index.tpl:24 +msgid "Questtype" +msgstr "Questtyp" + +#: views/html/quests/index.tpl:47 +msgid "Apply filters" +msgstr "Filter anwenden" + +#: views/html/quests/index.tpl:48 +msgid "Reset filters" +msgstr "Filter zurücksetzen" + +#: views/html/quests/quest.tpl:44 +#, php-format +msgid "Quest completed. You have earned %d XPs." +msgstr "Quest abgeschlossen. Du hast %d XPs erhalten." + +#: views/html/quests/quest.tpl:60 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:63 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:64 +msgid "Skip task" +msgstr "Aufgabe überspringen" + +#: views/html/quests/quest.tpl:69 +msgid "continue" +msgstr "fortfahren" + +#: views/html/quests/quest.tpl:76 +msgid "Continuation" +msgstr "Fortsetzung" + +#: views/html/quests/quest.tpl:82 views/html/quests/quest.tpl:95 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:10 +#, php-format +msgid "Submission of %s" +msgstr "Lösung von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht am %s um %s Uhr" + +#: views/html/seminaries/create.tpl:7 +msgid "New seminary" +msgstr "Neuer Kurs" + +#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:10 +msgid "Delete seminary" +msgstr "Kurs löschen" + +#: views/html/seminaries/delete.tpl:9 +#, php-format +msgid "Should the seminary “%s” really be deleted?" +msgstr "Soll der Kurs „%s“ wirklich gelöscht werden?" + +#: views/html/seminaries/delete.tpl:11 views/html/users/delete.tpl:6 +msgid "delete" +msgstr "löschen" + +#: views/html/seminaries/delete.tpl:12 views/html/users/delete.tpl:7 +msgid "cancel" +msgstr "abbrechen" + +#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:9 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:14 views/html/users/edit.tpl:17 +msgid "save" +msgstr "speichern" + +#: views/html/seminaries/index.tpl:4 +msgid "Create new seminary" +msgstr "Neuen Kurs erstellen" + +#: views/html/seminaries/index.tpl:21 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminaries/index.tpl:24 +msgid "Create a Character" +msgstr "Erstelle einen Charakter" + +#: views/html/seminaries/index.tpl:26 +#, php-format +msgid "Your Character “%s” has not been activated yet" +msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" + +#: views/html/seminaries/seminary.tpl:11 +msgid "Show Quests" +msgstr "Quests anzeigen" + +#: views/html/seminarybar/index.tpl:14 +msgid "Last Quest" +msgstr "Letzter Speicherpunkt" + +#: views/html/seminarybar/index.tpl:46 +#, php-format +msgid "Show %s-Profile" +msgstr "%s-Profil anzeigen" + +#: views/html/seminarymenu/index.tpl:5 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:82 views/html/users/register.tpl:83 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +#: views/html/users/user.tpl:12 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: 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:7 views/html/users/user.tpl:10 +#, php-format +msgid "registered on %s" +msgstr "registriert am %s" + +#: views/html/users/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:14 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:16 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:18 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:20 +msgid "Username already exists" +msgstr "Der Benutzername existiert bereits" + +#: views/html/users/register.tpl:22 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:27 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:29 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:31 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:33 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:38 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:40 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:42 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:44 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:49 views/html/users/register.tpl:53 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:51 +msgid "E‑mail address already exists" +msgstr "E‑Mail-Adresse existiert bereits" + +#: views/html/users/register.tpl:58 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:60 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:62 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:89 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:34 +msgid "Roles" +msgstr "Rollen" + +#, fuzzy +#~ msgid "achieved at %s" +#~ msgstr "erhalten am: %s" + +#~ msgid "Usergroups" +#~ msgstr "Benutzergruppen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..c5a1d248 --- /dev/null +++ b/models/AchievementsModel.inc @@ -0,0 +1,549 @@ + + * @copyright 2014 Heinrich-Heine-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(); + } + + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get seldom Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $count Number of Achievements to retrieve + * @return array List of seldom Achievements + */ + public function getSeldomAchievements($seminaryId, $count) + { + return $this->db->query( + 'SELECT id, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id, count(DISTINCT character_id) AS c '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 '. + 'GROUP BY achievement_id '. + 'ORDER BY count(DISTINCT character_id) ASC '. + 'LIMIT ?', + 'ii', + $seminaryId, + $count + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId, $includeOnlyOnce=false) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once <= ? AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ') '. + 'ORDER BY achievements.pos ASC', + 'iii', + $seminaryId, + $includeOnlyOnce, + $characterId + ); + } + + + /** + * Get the rank for the number of achieved Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $xps Amount of achieved Achievements + * @return int Rank of Achievements count + */ + public function getCountRank($seminaryId, $count) + { + $data = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM ('. + 'SELECT count(DISTINCT achievement_id) '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'WHERE achievements.seminary_id = ? '. + 'GROUP BY character_id '. + 'HAVING count(DISTINCT achievement_id) > ?'. + ') AS ranking', + 'ii', + $seminaryId, + $count + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT achievement_id, character_id, created '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + if(!empty($data)) { + return $data[0]; + } + + + return false; + } + + } + +?> diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..9c3d3701 --- /dev/null +++ b/models/AvatarsModel.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\models; + + + /** + * Model to interact with Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get an Avatar by its Character type and XP-level. + * + * @param int $seminaryId ID of Seminary + * @param string $charactertypeUrl URL-title of Character type + * @param int $xplevel XP-level + * @return array Avatar data + */ + public function getAvatarByTypeAndLevel($seminaryId, $charactertypeUrl, $xplevel) + { + $data = $this->db->query( + 'SELECT avatars.id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'INNER JOIN charactertypes ON charactertypes.id = avatars.charactertype_id '. + 'INNER JOIN xplevels ON xplevels.id = avatars.xplevel_id AND xplevels.seminary_id = charactertypes.seminary_id '. + 'WHERE charactertypes.seminary_id = ? AND charactertypes.url = ? AND xplevels.level = ?', + 'isi', + $seminaryId, + $charactertypeUrl, + $xplevel + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($charactertypeUrl); + } + + + return $data[0]; + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..e042fd10 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,197 @@ + + * @copyright 2014 Heinrich-Heine-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, preferred '. + '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, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $groupsgroupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupUrl); + } + + + return $data[0]; + } + + + /** + * Get a Character groups-group by its ID. + * + * @throws IdNotFoundException + * @param string $groupsgroupId ID of the Character groups-group + * @return array Character groups-group data + */ + public function getGroupsgroupById($groupsgroupId) + { + $data = $this->db->query( + 'SELECT id, name, url, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE id = ?', + 'i', + $groupsgroupId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupId); + } + + + 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, motto '. + '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, motto '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + } + +?> diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc new file mode 100644 index 00000000..4490168d --- /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, xps '. + '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..b3eac8b7 --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,475 @@ + + * @copyright 2014 Heinrich-Heine-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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url, seminaries.id AS seminary_id, seminaries.url 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Get Characters with the most amount of Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $conut Amount of Characters to retrieve + * @return array List of Characters + */ + public function getCharactersWithMostAchievements($seminaryId, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url, count(DISTINCT achievement_id) AS c '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'LEFT JOIN v_characters AS characters ON characters.id = achievements_characters.character_id '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE achievements.seminary_id = ? '. + 'GROUP BY character_id '. + 'ORDER BY count(DISTINCT achievement_id) DESC '. + 'LIMIT ?', + 'ii', + $seminaryId, + $count + ); + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * Get the superior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of superior Characters + */ + public function getSuperiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps > ? '. + 'ORDER BY characters.xps ASC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * Get the inferior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of inferior Characters + */ + public function getInferiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps < ? '. + 'ORDER BY characters.xps DESC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * 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 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 charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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 charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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 charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Check if a Character name already exists. + * + * @param string $name Character name to check + * @return boolean Whether Character name exists or not + */ + public function characterNameExists($name) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM characters '. + 'WHERE name = ? OR url = ?', + 'ss', + $name, + \nre\core\Linker::createLinkParam($name) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + + } + +?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> 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..031df753 --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,195 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its URL. + * + * @throws IdNotFoundException + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $seminaryMediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its ID. + * + * @throws IdNotFoundException + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Create a new Questsmedia by creating a new Seminarymedia and + * adding it to the list of Questsmedia. + * TODO Currently only temporary for easier data import. + */ + public function createQuestMedia($userId, $seminaryId, $filename, $description, $mimetype, $tmpFilename) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + $this->db->query( + 'INSERT INTO seminarymedia '. + '(created_user_id, seminary_id, name, url, description, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?, ?, ?)', + 'iissss', + $userId, + $seminaryId, + $filename, + \nre\core\Linker::createLinkParam($filename), + $description, + $mimetype + ); + $uploadId = $this->db->getInsertId(); + + $this->db->query( + 'INSERT INTO questsmedia '. + '(media_id, created_user_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $uploadId, + $userId + ); + + // Create filename + $filename = ROOT.DS.'seminarymedia'.DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + } + +?> diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc new file mode 100644 index 00000000..26ae9a10 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,635 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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 all Questgroups for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questgroups + */ + public function getQuestgroupsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questgroups '. + 'WHERE seminary_id = ? '. + 'ORDER BY title ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['id'], $characterId); + } + + + // Return summarized XPs + return $xps; + } + + + /** + * Create a new Questgroup. + * + * @param int $userId User-ID that creates the new character + * @param int $seminaryId ID of Seminary + * @param string $title Title for new Questgroup + * @return int ID of new Questgroup + */ + public function createQuestgroup($userId, $seminaryId, $title) + { + $this->db->query( + 'INSERT INTO questgroups '. + '(created_user_id, seminary_id, title, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $seminaryId, + $title, + \nre\core\Linker::createLinkParam($title) + ); + + + return $this->db->getInsertId(); + } + + + + + /** + * 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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..ffffb536 --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,460 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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, quests.entry_text, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all Quests for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array Quests for this Seminary + */ + public function getQuestsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM questgroups '. + 'INNER JOIN quests ON quests.questgroup_id = questgroups.id '. + 'WHERE questgroups.seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get all Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return array Quests for this Questtopic + */ + public function getQuestsForQuesttopic($questtopicId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_questsubtopics '. + 'INNER JOIN quests ON quests.id = quests_questsubtopics.quest_id '. + 'WHERE quests_questsubtopics.questsubtopic_id = ?', + 'i', + $questtopicId + ); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + + /** + * Create a new Quest. + * + * @param int $userId User-ID that creates the new character + * @param string $name Name for new Quest + * @param int $questgroupId ID of Questgroup + * @param int $questtypeId ID of Questtype + * @param int $xps XPs for new Quest + * @param string $entrytext Entrytext for new Quest + * @param string $wrongtext Wrongtext for new Quest + * @param string $task Task for new Quest + * @return int ID of new Quest + */ + public function createQuest($userId, $name, $questgroupId, $questtypeId, $xps, $entrytext, $wrongtext, $task) + { + $this->db->query( + 'INSERT INTO quests '. + '(created_user_id, questgroup_id, questtype_id, title, url, xps, entry_text, wrong_text, task) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?, ?, ?, ?)', + 'iiississs', + $userId, $questgroupId, $questtypeId, + $name, \nre\core\Linker::createLinkParam($name), + $xps, $entrytext, $wrongtext, $task + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the media for a Quest. + * + * @param int $questId ID of Quest to set media for + * @param int $questmediaId ID of Questsmedia to set + */ + public function setQuestmedia($questId, $questsmediaId) + { + $this->db->query( + 'UPDATE quests '. + 'SET questsmedia_id = ? '. + 'WHERE id = ?', + 'ii', + $questsmediaId, + $questId + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..7aca28b9 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,220 @@ + + * @copyright 2014 Heinrich-Heine-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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * Get count of Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return int Amount of Questtexts for a Quest + */ + public function getQuesttextCountOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + $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 = ?', + 'i', + $questId + ); + } + else + { + $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, + $questtexttypeUrl + ); + } + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all registered Questtexttypes. + * + * @return array Registered Questtexttypes + */ + public function getQuesttexttypes() + { + return $this->db->query( + 'SELECT id, type, url '. + 'FROM questtexttypes' + ); + } + + + /** + * Get a Questtexttype by its URL. + * + * @param string $questtexttypeUrl URL-type of Questtexttype + * @return array Questtexttype data + */ + public function getQuesttexttypeByUrl($questtexttypeUrl) + { + $data = $this->db->query( + 'SELECT id, type, url '. + 'FROM questtexttypes '. + 'WHERE url = ?', + 's', + $questtexttypeUrl + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Add a list of Questtexts to a Quest. + * + * @param int $userId ID of user + * @param int $questId ID of Quest to add texts to + * @param string $questtexttypeUrl URL-type of Questtexttype of texts + * @param array $texts List of texts to add. + */ + public function addQuesttextsToQuest($userId, $questId, $questtexttypeUrl, $texts) + { + $questtexttype = $this->getQuesttexttypeByUrl($questtexttypeUrl); + if(is_null($questtexttype)) { + return; + } + foreach($texts as &$text) + { + $pos = $this->db->query( + 'SELECT COALESCE(MAX(pos),0)+1 AS pos '. + 'FROM questtexts '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + $pos = $pos[0]['pos']; + + $this->db->query( + 'INSERT INTO questtexts '. + '(created_user_id, quest_id, questtexttype_id, pos, text) '. + 'VALUES '. + '(?, ?, ?, ?, ?)', + 'iiiis', + $userId, $questId, $questtexttype['id'], $pos, + $text + ); + } + } + + + + + } + +?> diff --git a/models/QuesttopicsModel.inc b/models/QuesttopicsModel.inc new file mode 100644 index 00000000..abd246f7 --- /dev/null +++ b/models/QuesttopicsModel.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\models; + + + /** + * Model to interact with Questtopics-table. + * + * @author Oliver Hanraths + */ + class QuesttopicsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttopicsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get a Questtopic by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + * @return array Questtopic data + */ + public function getQuesttopicByUrl($seminaryId, $questtopicUrl) + { + $data = $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $questtopicUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questtopicUrl); + } + + + return $data[0]; + } + + + /** + * Get all Questtopics for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questtopics + */ + public function getQuesttopicsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get count of Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return int Count of Quests + */ + public function getQuestCountForQuesttopic($questtopicId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_questsubtopics.quest_id) AS c ' . + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'WHERE questsubtopics.questtopic_id = ?', + 'i', + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get count of Quests that are linked to a Questtopic and are + * unlocked by a Character. + * + * @param int $questtopicId ID of Questtopic + * @param int $characterId ID of Character + * @return int Count of Quests + */ + public function getCharacterQuestCountForQuesttopic($questtopicId, $characterId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_characters.quest_id) AS c '. + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'INNER JOIN quests_characters ON quests_characters.quest_id = quests_questsubtopics.quest_id AND quests_characters.character_id = ? AND quests_characters.status = 3 '. + 'WHERE questsubtopics.questtopic_id = ?', + 'ii', + $characterId, + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get all Questsubtopics for a Quest. + * + * @param int $questId ID of Quest + * @return array List of Questsubtopics + */ + public function getQuestsubtopicsForQuest($questId) + { + return $this->db->query( + 'SELECT DISTINCT id, questtopic_id, title, url '. + 'FROM quests_questsubtopics '. + 'INNER JOIN questsubtopics ON questsubtopics.id = quests_questsubtopics.questsubtopic_id '. + 'WHERE quests_questsubtopics.quest_id = ?', + 'i', + $questId + ); + } + + } + +?> diff --git a/models/QuesttypesModel.inc b/models/QuesttypesModel.inc new file mode 100644 index 00000000..58b36648 --- /dev/null +++ b/models/QuesttypesModel.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 Questtypes-table. + * + * @author Oliver Hanraths + */ + class QuesttypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all registered Questtypes. + * + * @return array List of registered Questtypes + */ + public function getQuesttypes() + { + return $this->db->query( + 'SELECT id, title, url, classname '. + 'FROM questtypes '. + 'ORDER BY title ASC' + ); + } + + + /** + * 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..251ade4f --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, seminarymedia_id '. + '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, seminarymedia_id '. + '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, seminarymedia_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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..efad3bde --- /dev/null +++ b/models/SeminarycharacterfieldsModel.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 the Seminarycharacterfields-tables. + * + * @author Oliver Hanraths + */ + class SeminarycharacterfieldsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new SeminarycharacterfieldsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..c773a0b5 --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,370 @@ + + * @copyright 2014 Heinrich-Heine-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 users with the given user role. + * + * @param string $userrole User role + * @return array List of users + */ + public function getUsersWithRole($userrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + 'FROM users '. + 'LEFT JOIN users_userroles ON users_userroles.user_id = users.id '. + 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '. + 'WHERE userroles.name = ? '. + 'ORDER BY username ASC', + 's', + $userrole + ); + } + + + /** + * Get users with the given user Seminary role. + * + * @param int $seminaryId ID of Seminary + * @param string $userseminaryrole User Seminary role + * @return array List of users + */ + public function getUsersWithSeminaryRole($seminaryId, $userseminaryrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + '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_userseminaryroles.seminary_id = ? AND userseminaryroles.name = ? '. + 'ORDER BY username ASC', + 'is', + $seminaryId, $userseminaryrole + ); + } + + + /** + * 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; + } + + + /** + * Check if an username already exists. + * + * @param string $username Username to check + * @return boolean Whether username exists or not + */ + public function usernameExists($username) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE username = ? OR url = ?', + 'ss', + $username, + \nre\core\Linker::createLinkParam($username) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Check if an e‑mail address already exists. + * + * @param string $email E‑mail address to check + * @return boolean Whether e‑mail address exists or not + */ + public function emailExists($email) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE email = ?', + 's', + $email + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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/bossfight/BossfightQuesttypeAgent.inc b/questtypes/bossfight/BossfightQuesttypeAgent.inc new file mode 100644 index 00000000..6d99ec44 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeAgent.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 a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeController.inc b/questtypes/bossfight/BossfightQuesttypeController.inc new file mode 100644 index 00000000..366aabc8 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeController.inc @@ -0,0 +1,244 @@ + + * @copyright 2014 Heinrich-Heine-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 BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeController 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) + { + // Prepare session + $this->prepareSession($quest['id']); + + // Remove previous answers + $this->Bossfight->clearCharacterSubmissions($quest['id'], $character['id']); + + // Save answers + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) { + $this->Bossfight->setCharacterSubmission($stage['id'], $character['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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + + // Prepare session + $this->prepareSession($quest['id']); + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + + return ($lives['boss'] == 0 && $lives['character'] > 0); + } + + + /** + * Action: quest. + * + * Display a stage with a text and the answers for the following + * stages. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Prepare session + $this->prepareSession($quest['id']); + + // Get Stage + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit_stages'))) + { + $stages = $this->request->getPostParam('submit_stages'); + $stageId = array_keys($stages)[0]; + $stage = $this->Bossfight->getStageById($stageId); + } + else + { + $_SESSION['quests'][$quest['id']]['stages'] = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + } + + // Store Stage in session + if(count($_SESSION['quests'][$quest['id']]['stages']) == 0 || $_SESSION['quests'][$quest['id']]['stages'][count($_SESSION['quests'][$quest['id']]['stages'])-1]['id'] != $stage['id']) { + $_SESSION['quests'][$quest['id']]['stages'][] = $stage; + } + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + // Get Child-Stages + $childStages = $this->Bossfight->getChildStages($stage['id']); + + // Get answer of Character + if($this->request->getGetParam('show-answer') == 'true') { + foreach($childStages as &$childStage) { + $childStage['answer'] = $this->Bossfight->getCharacterSubmission($childStage['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stage', $stage); + $this->set('lives', $lives); + $this->set('childStages', $childStages); + } + + + /** + * Action: submission. + * + * Display all stages with the answers the character has + * choosen. + * + * @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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Get stages + $stages = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + while(!is_null($stage)) + { + $stages[] = $stage; + + $childStages = $this->Bossfight->getChildStages($stage['id']); + $stage = null; + foreach($childStages as &$childStage) + { + if($this->Bossfight->getCharacterSubmission($childStage['id'], $character['id'])) + { + $stage = $childStage; + break; + } + } + } + + // Calculate lives + $stages[0]['lives'] = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + for($i=1; $i $stages[$i-1]['lives']['character'] + $stages[$i]['livedrain_character'], + 'boss' => $stages[$i-1]['lives']['boss'] + $stages[$i]['livedrain_boss'], + ); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stages', $stages); + } + + + + + /** + * Prepare the session to store stage information in + * + * @param int $questId ID of Quest + */ + private function prepareSession($questId) + { + if(!array_key_exists('quests', $_SESSION)) { + $_SESSION['quests'] = array(); + } + if(!array_key_exists($questId, $_SESSION['quests'])) { + $_SESSION['quests'][$questId] = array(); + } + if(!array_key_exists('stages', $_SESSION['quests'][$questId])) { + $_SESSION['quests'][$questId]['stages'] = array(); + } + } + + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeModel.inc b/questtypes/bossfight/BossfightQuesttypeModel.inc new file mode 100644 index 00000000..a05927f0 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeModel.inc @@ -0,0 +1,183 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\questtypes; + + + /** + * Model of the BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get a Boss-Fight. + * + * @throws IdNotFoundException + * @param int $questId ID of Quest + * @return array Boss-Fight data + */ + public function getBossFight($questId) + { + $data = $this->db->query( + 'SELECT bossname, boss_seminarymedia_id, lives_character, lives_boss '. + 'FROM questtypes_bossfight '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Stage to begin the Boss-Fight with. + * + * @param int $questId ID of Quest + * @return array Data of first Stage + */ + public function getFirstStage($questId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ? AND parent_stage_id IS NULL', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get a Stage by its ID. + * + * @param int $stageId ID of Stage + * @return array Stage data or null + */ + public function getStageById($stageId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE id = ?', + 'i', + $stageId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get the follow-up Stages for a Stage. + * + * @param int $parentStageId ID of Stage to get follow-up Stages for + * @return array List of follow-up Stages + */ + public function getChildStages($parentStageId) + { + return $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE parent_stage_id = ?', + 'i', + $parentStageId + ); + } + + + /** + * Reset all Character submissions of a Boss-Fight. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + */ + public function clearCharacterSubmissions($questId, $characterId) + { + $this->db->query( + 'DELETE FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id IN ('. + 'SELECT id '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ?'. + ') AND character_id = ?', + 'ii', + $questId, + $characterId + ); + } + + + /** + * Save Character’s submitted answer for one Boss-Fight-Stage. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + */ + public function setCharacterSubmission($stageId, $characterId) + { + $this->db->query( + 'INSERT INTO questtypes_bossfight_stages_characters '. + '(questtypes_bossfight_stage_id, character_id) '. + 'VALUES '. + '(?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_bossfight_stage_id = ?', + 'iii', + $stageId, $characterId, $stageId + ); + } + + + /** + * Get answer of one Boss-Fight-Stage submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return boolean Stage taken + */ + public function getCharacterSubmission($stageId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_bossfight_stage_id '. + 'FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id = ? AND character_id = ? ', + 'ii', + $stageId, $characterId + ); + + + return (!empty($data)); + } + + } + +?> diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl new file mode 100644 index 00000000..e97569c2 --- /dev/null +++ b/questtypes/bossfight/html/quest.tpl @@ -0,0 +1,51 @@ +

                  +
                  +

                  +

                  +

                  + 0) : ?> + + + + + + +

                  +
                  +
                  +

                  +

                  +

                  + 0) : ?> + + + + + + +

                  +
                  +
                  + +

                  + + + +
                    + +
                  • +

                    + + +

                    +

                    +
                  • + + +
                  • + + +
                  • + +
                  + diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl new file mode 100644 index 00000000..3696964e --- /dev/null +++ b/questtypes/bossfight/html/submission.tpl @@ -0,0 +1,38 @@ +
                  +
                  +

                  +
                  +
                  +

                  +
                  +
                  + + +

                  +
                  +
                  +

                  +

                  + 0) : ?> + + + + + + +

                  +
                  +
                  +

                  +

                  + 0) : ?> + + + + + + +

                  +
                  +
                  + diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..489cbc2f --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
                  + &$text) : ?> + 0) : ?> + + + t($text)?> + + +

                  + +
                  diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..40f60505 --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
                  + &$text) : ?> + 0) : ?> + + + t($text)?> + +
                  diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..ba3e5a0c --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,351 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..d202eb8f --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,49 @@ +
                  + + + + + + + + + + +
                  + + 0) : ?> + + +
                  +
                    + +
                  1. + + : + + : + + +
                  2. + +
                  + +
                  + diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..c47e414d --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,48 @@ +
                  + + + + + + + + + + +
                  + + 0) : ?> + + +
                  +
                    + +
                  1. + + : + + : + + +
                  2. + +
                  +
                  + 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..5fbf6328 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,203 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drags + $drags = $this->Dragndrop->getDrags($dndField['quest_id'], true); + + // Match drags with user answers + foreach($drags as &$drag) + { + $founds = array_keys($answers, 'drag'.$drag['id']); + if(count($founds) != 1) { + return false; + } + if(!$this->Dragndrop->dragMatchesDrop($drag['id'], $founds[0])) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..2aadb335 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,180 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 + * @param boolean $onlyUsed Only Drag-items that are used for a Drop-item + * @return array Drag-items + */ + public function getDrags($dragndropId, $onlyUsed=false) + { + return $this->db->query( + 'SELECT id, questmedia_id '. + 'FROM questtypes_dragndrop_drags '. + 'WHERE questtypes_dragndrop_id = ?'. + ($onlyUsed + ? ' AND EXISTS ('. + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_drags '. + 'WHERE questtypes_dragndrop_drag_id = questtypes_dragndrop_drags.id'. + ')' + : null + ), + 'i', + $dragndropId + ); + } + + + /** + * Check if a Drag-item mathes a Drop-item. + * + * @param int $dragId ID of Drag-field + * @param int $dropId ID of Drop-field + * @return boolean Drag-item is valid for Drop-item + */ + public function dragMatchesDrop($dragId, $dropId) + { + $data = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM questtypes_dragndrop_drops_drags '. + 'WHERE questtypes_dragndrop_drop_id = ? AND questtypes_dragndrop_drag_id = ?', + 'ii', + $dropId, $dragId + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Save Character’s submitted answer for one 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..4cb10b60 --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                  + +
                  + + + +
                  + +
                  + +
                  + + + +
                  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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..bca8c33a --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,262 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Match answers with user answers + foreach($answers as &$answer) + { + if(is_null($answer['tick'])) { + continue; + } + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..eba80286 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                  +
                  + : +

                  +
                    + &$answer) : ?> +
                  1. + /> + +
                  2. + +
                  +
                  + + + + + + + +
                  diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ebbbd493 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                    + &$question) : ?> +
                  1. +

                    t($question['question'])?>

                    +
                      + +
                    1. + + × + +
                    2. + +
                    +
                  2. + +
                  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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..7132de5e --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                  + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                  + + +
                  + +

                  :

                  +
                    + +
                  • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                  • + +
                  + +
                  + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                  + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                  + + + + + + + +
                  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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..6616ba4d --- /dev/null +++ b/questtypes/textinput/html/quest.tpl @@ -0,0 +1,11 @@ +
                  +

                  + &$text) : ?> + 0) : ?> + + + t($text)?> + +

                  + +
                  diff --git a/questtypes/textinput/html/submission.tpl b/questtypes/textinput/html/submission.tpl new file mode 100644 index 00000000..b3d606f4 --- /dev/null +++ b/questtypes/textinput/html/submission.tpl @@ -0,0 +1,7 @@ + &$text) : ?> + 0) : ?> + + + +t($text)?> + diff --git a/requests/WebRequest.inc b/requests/WebRequest.inc new file mode 100644 index 00000000..2e0f19b9 --- /dev/null +++ b/requests/WebRequest.inc @@ -0,0 +1,401 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\requests; + + + /** + * Representation of a web-request. + * + * @author coderkun + */ + class WebRequest extends \nre\core\Request + { + /** + * Passed GET-parameters + * + * @var array + */ + private $getParams = array(); + /** + * Passed POST-parameters + * + * @var array + */ + private $postParams = array(); + /** + * Stored routes + * + * @var array + */ + private $routes = array(); + /** + * Stored reverse-routes + * + * @var array + */ + private $reverseRoutes = array(); + + + + + /** + * Construct a new web-request. + */ + public function __construct() + { + // Detect current request + $this->detectRequest(); + + // Load GET-parameters + $this->loadGetParams(); + + // Load POST-parameters + $this->loadPostParams(); + + // Detect AJAX + $this->detectAJAX(); + } + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + if($index == 0) { + return $this->getGetParam('layout', $defaultIndex); + } + else { + return parent::getParam($index-1, $defaultIndex); + } + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameters + */ + public function getParams($offset=0) + { + if($offset == 0) + { + return array_merge( + array( + $this->getGetParam('layout', 'toplevel') + ), + parent::getParams() + ); + } + + + return array_slice($this->params, $offset-1); + } + + + /** + * Get a GET-parameter. + * + * @param string $key Key of GET-parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of GET-parameter + */ + public function getGetParam($key, $defaultIndex=null) + { + // Check key + if(array_key_exists($key, $this->getParams)) + { + // Return value + return $this->getParams[$key]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all GET-parameters. + * + * @return array GET-Parameters + */ + public function getGetParams() + { + return $this->getParams; + } + + + /** + * Get a POST-parameter. + * + * @param string $key Key of POST-parameter + * @param string $defaultValue Default value for this parameter + * @return string Value of POST-parameter + */ + public function getPostParam($key, $defaultValue=null) + { + // Check key + if(array_key_exists($key, $this->postParams)) + { + // Return value + return $this->postParams[$key]; + } + + + // Return default value + return $defaultValue; + } + + + /** + * Get all POST-parameters. + * + * @return array POST-parameters + */ + public function getPostParams() + { + return $this->postParams; + } + + + /** + * Get the method of the current request. + * + * @return string Current request method + */ + public function getRequestMethod() + { + return $_SERVER['REQUEST_METHOD']; + } + + + /** + * Add a URL-route. + * + * @param string $pattern Regex-Pattern that defines the routing + * @param string $replacement Regex-Pattern for replacement + * @param bool $isLast Stop after that rule + */ + public function addRoute($pattern, $replacement, $isLast=false) + { + // Store route + $this->routes[] = $this->newRoute($pattern, $replacement, $isLast); + } + + + /** + * Add a reverse URL-route. + * + * @param string $pattern Regex-Pattern that defines the reverse routing + * @param string $replacement Regex-Pattern for replacement + * @param bool $isLast Stop after that rule + */ + public function addReverseRoute($pattern, $replacement, $isLast=false) + { + // Store reverse route + $this->reverseRoutes[] = $this->newRoute($pattern, $replacement, $isLast); + } + + + /** + * Apply stored reverse-routes to an URL + * + * @param string $url URL to apply reverse-routes to + * @return string Reverse-routed URL + */ + public function applyReverseRoutes($url) + { + return $this->applyRoutes($url, $this->reverseRoutes); + } + + + /** + * Revalidate the current request + */ + public function revalidate() + { + $this->detectRequest(); + } + + + /** + * Get a SERVER-parameter. + * + * @param string $key Key of SERVER-parameter + * @return string Value of SERVER-parameter + */ + public function getServerParam($key) + { + if(array_key_exists($key, $_SERVER)) { + return $_SERVER[$key]; + } + + + return null; + } + + + + + /** + * Detect the current HTTP-request. + */ + private function detectRequest() + { + // URL ermitteln + $url = isset($_GET) && array_key_exists('url', $_GET) ? $_GET['url'] : ''; + $url = trim($url, '/'); + + // Routes anwenden + $url = $this->applyRoutes($url, $this->routes); + + // URL splitten + $params = explode('/', $url); + if(empty($params[0])) { + $params = array(); + } + + + // Parameter speichern + $this->params = $params; + } + + + /** + * Determine parameters passed by GET. + */ + private function loadGetParams() + { + if(isset($_GET)) { + $this->getParams = $_GET; + } + } + + + /** + * Determine parameters passed by POST. + */ + private function loadPostParams() + { + if(isset($_POST)) { + $this->postParams = $_POST; + } + } + + + /** + * Detect an AJAX-request by checking the X-Requested-With + * header and set the layout to 'ajax' in this case. + */ + private function detectAjax() + { + // Get request headers + $headers = apache_request_headers(); + + // Check X-Requested-With header and set layout + if(array_key_exists('X-Requested-With', $headers) && $headers['X-Requested-With'] == 'XMLHttpRequest') { + if(!array_key_exists('layout', $this->getParams)) { + $this->getParams['layout'] = 'ajax'; + } + } + } + + + /** + * Create a new URL-route. + * + * @param string $pattern Regex-Pattern that defines the reverse routing + * @param string $replacement Regex-Pattern for replacement + * @param bool $isLast Stop after that rule + * @return array New URL-route + */ + private function newRoute($pattern, $replacement, $isLast=false) + { + return array( + 'pattern' => $pattern, + 'replacement' => $replacement, + 'isLast' => $isLast + ); + } + + + /** + * Apply given routes to an URL + * + * @param string $url URL to apply routes to + * @param array $routes Routes to apply + * @return string Routed URL + */ + private function applyRoutes($url, $routes) + { + // Traverse given routes + foreach($routes as &$route) + { + // Create and apply Regex + $urlR = preg_replace( + '>'.$route['pattern'].'>i', + $route['replacement'], + $url + ); + + // Split URL + $get = ''; + if(($gpos = strrpos($urlR, '?')) !== false) { + $get = substr($urlR, $gpos+1); + $urlR = substr($urlR, 0, $gpos); + } + + // Has current route changed anything? + if($urlR != $url || !empty($get)) + { + // Extract GET-parameters + if(strlen($get) > 0) + { + $gets = explode('&', $get); + foreach($gets as $get) + { + $get = explode('=', $get); + if(!array_key_exists($get[0], $this->getParams)) { + $this->getParams[$get[0]] = $get[1]; + } + } + } + + + // Stop when route “isLast” + if($route['isLast']) { + $url = $urlR; + break; + } + } + + + // Set new URL + $url = $urlR; + } + + + // Return routed URL + return $url; + } + + } + +?> diff --git a/responses/WebResponse.inc b/responses/WebResponse.inc new file mode 100644 index 00000000..2ca25079 --- /dev/null +++ b/responses/WebResponse.inc @@ -0,0 +1,250 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\responses; + + + /** + * Representation of a web-response. + * + * @author coderkun + */ + class WebResponse extends \nre\core\Response + { + /** + * Applied GET-parameters + * + * @var array + */ + private $getParams = array(); + /** + * Changed header lines + * + * @var array + */ + private $headers = array(); + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + if(array_key_exists('layout', $this->getParams)) { + parent::addParam($value); + } + else { + $this->addGetParam('layout', $value); + } + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->addParam($value1); + + $this->params = array_merge( + $this->params, + array_slice( + func_get_args(), + 1 + ) + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + if($offset == 0) { + unset($this->getParams['layout']); + } + + parent::clearParams(max(0, $offset-1)); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + if($index == 0) { + return $this->getGetParam('layout', $defaultIndex); + } + else { + return parent::getParam($index-1, $defaultIndex); + } + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + if($offset == 0) + { + if(!array_key_exists('layout', $this->getParams)) { + return array(); + } + + return array_merge( + array( + $this->getParams['layout'] + ), + $this->params + ); + } + + + return array_slice($this->params, $offset-1); + } + + + /** + * Add a GET-parameter. + * + * @param string $key Key of GET-parameter + * @param mixed $value Value of GET-parameter + */ + public function addGetParam($key, $value) + { + $this->getParams[$key] = $value; + } + + + /** + * Add multiple GET-parameters. + * + * @param array $params Associative arary with key-value GET-parameters + */ + public function addGetParams($params) + { + $this->getParams = array_merge( + $this->getParams, + $params + ); + } + + + /** + * Get a GET-parameter. + * + * @param int $index Index of GET-parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of GET-parameter + */ + public function getGetParam($key, $defaultIndex=null) + { + // Check key + if(array_key_exists($key, $this->getParams)) + { + // Return value + return $this->getParams[$key]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all GET-parameters. + * + * @return array All GET-parameters + */ + public function getGetParams() + { + return $this->getParams; + } + + + /** + * Add a line to the response header. + * + * @param string $headerLine Header line + * @param bool $replace Replace existing header line + * @param int $http_response_code HTTP-response code + */ + public function addHeader($headerLine, $replace=true, $http_response_code=null) + { + $this->headers[] = $this->newHeader($headerLine, $replace, $http_response_code); + } + + + /** + * Clear all stored headers. + */ + public function clearHeaders() + { + $this->headers = array(); + } + + + /** + * Send stored headers. + */ + public function header() + { + foreach($this->headers as $header) + { + header( + $header['string'], + $header['replace'], + $header['responseCode'] + ); + } + } + + + + + /** + * Create a new header line. + * + * @param string $headerLine Header line + * @param bool $replace Replace existing header line + * @param int $http_response_code HTTP-response code + */ + private function newHeader($headerLine, $replace=true, $http_response_code=null) + { + return array( + 'string' => $headerLine, + 'replace' => $replace, + 'responseCode' => $http_response_code + ); + } + + } + +?> diff --git a/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/avatar.tpl b/views/binary/media/avatar.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/avatar.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/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..33c967e1 --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,94 @@ + +
                  + +
                  + + +

                  +

                  +
                  +
                  +

                  +
                    + +
                  1. + + + + + + + + + +

                    +

                    +
                  2. + +
                  +
                  +
                  +

                  +
                    + +
                  1. + +

                    +

                    +
                  2. + +
                  +
                  +
                  +

                  +
                  +

                  +
                  + +
                  +
                  +

                  . : .

                  +
                    +
                  • + +

                    Freigeschaltetes Achievementerreicht am 17.06.104

                    +

                    Das Bild ist entsprechend farbig, ein eventueller Fortschrittsbalken mit 100% soll entfallen.

                    +
                  • + +
                  • + + + +

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

                    +

                    +
                  • + + +
                  • + + + +

                    + +

                    + +

                    + +

                    + + +
                    +
                    + +
                    +

                    %

                    +
                    + +
                  • + +
                  diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..0f6f88d3 --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,50 @@ + +
                  + +
                  + + +
                  + +

                  +

                  ""

                  +
                  +
                    +
                  • .
                  • +
                  • XP
                  • +
                  • 1) ? _('Members') : _('Member')?>
                  • +
                  + +
                  +

                  +
                    + +
                  • + +

                    + +

                    +

                    XP

                    +
                  • + +
                  +
                  + +
                  +

                  +
                    + +
                  • +

                    + format(new \DateTime($quest['created']))?> + + / XP +

                    +
                  • + +
                  +
                  diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..3d337cf3 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,25 @@ + +
                  + +
                  + + + +

                  + +
                    + +
                  1. XP
                  2. + +
                  + + +

                  +
                    + +
                  • + +
                  diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..d88d25eb --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,14 @@ + +
                  + +
                  + + +

                  +
                    + +
                  • + +
                  diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..6c696737 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,53 @@ + +
                  + +
                  + + + +

                  +Maximale Belohnung: XP + +
                  +

                  +

                  + + + + +

                  + +

                  +

                  + +
                  + + +
                  +

                  +

                  +
                  + + +
                  +

                  +

                  +
                  + + +
                  +

                  +
                    + +
                  • + format(new \DateTime($group['created']))?> + + XP +
                  • + +
                  +
                  diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl new file mode 100644 index 00000000..0ea56007 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,101 @@ + +
                  + +
                  + +

                  +

                  +

                  + +
                  +
                  +
                  +
                  + +
                  +

                  :  %

                  +
                  +
                  +

                  +

                  +
                  +
                  +

                  +

                  XP

                  +
                  +
                  +

                  .

                  +

                  +
                  + +

                  +
                    + +
                  • + + + +

                    +

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

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

                  +
                    + +
                  • + +

                     XPs

                    +
                  • + +
                  +
                  + +
                  +

                  +
                    + &$rankCharacter) : ?> +
                  • + +

                    .

                    +

                    ( XPs)

                    +
                  • + +
                  • + +

                    .

                    +

                    ( XPs)

                    +
                  • + &$rankCharacter) : ?> +
                  • + +

                    .

                    +

                    ( XPs)

                    +
                  • + +
                  +
                  +
                  + +
                  +

                  +
                    + +
                  • +

                    (/)

                    +
                    + +
                    +
                  • + +
                  +
                  + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..91833a0e --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,17 @@ + +
                  + +
                  + +

                  +

                  + +
                    + +
                  • +

                    +

                    +

                    XP

                    +
                  • + +
                  diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..d62c0d35 --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,82 @@ + +
                  + +
                  + +

                  +

                  + +
                  + +
                    + &$settings) : ?> +
                  • +
                      + $value) : ?> +
                    • + +
                    • + +
                    +
                  • + +
                  + +
                  + + +
                  + + +
                  + + +
                    + &$settings) : ?> +
                  • + +
                  + +
                  + + + + + required="required"/> + + + + + +
                  + +
                  + +
                  diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..0f68bc07 --- /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..a62f4e11 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,56 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                  + +
                  +
                  + +
                  + + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..3dc754ae --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                  +

                  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/library/index.tpl b/views/html/library/index.tpl new file mode 100644 index 00000000..df59a4bd --- /dev/null +++ b/views/html/library/index.tpl @@ -0,0 +1,28 @@ + +
                  + +
                  + + +

                  +

                  Hier findest du alle Themen aus der Vorlesung "Wissensrepräsentation" und die passenden Quests zum Nachschlagen und Wiederholen. Dein Fortschritt in "Die Legende von Zyren" beeinflusst den Umfang der Bibliothek, spiele also regelmäßig weiter und schalte so Quest für Quest alle Inhalte frei.

                  +
                  +

                  Gesamtfortschritt: 77%

                  +
                  + +
                  +
                  +
                    + +
                  • + +

                    +

                    Fortschritt: /

                    +
                    + +
                    +
                  • + +
                  diff --git a/views/html/library/topic.tpl b/views/html/library/topic.tpl new file mode 100644 index 00000000..b68d2a67 --- /dev/null +++ b/views/html/library/topic.tpl @@ -0,0 +1,30 @@ + +
                  + +
                  + + +

                  +
                  +

                  Themenfortschritt: /

                  +
                  + +
                  +
                  + +

                  Quests zu diesem Thema:

                  +
                    + +
                  • +

                    +
                      + +
                    • + +
                    +
                  • + +
                  diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl new file mode 100644 index 00000000..dbab43c2 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
                • The Legend of Z
                • + 0) : ?>
                • +
                • + 0) : ?> + +
                • + +
                • + diff --git a/views/html/questgroups/create.tpl b/views/html/questgroups/create.tpl new file mode 100644 index 00000000..233713cc --- /dev/null +++ b/views/html/questgroups/create.tpl @@ -0,0 +1,16 @@ + +
                  + +
                  + +

                  +

                  +

                  + +
                  +
                  + +
                  +
                  + +
                  diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..285aa706 --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,60 @@ + +
                  + +
                  + +

                  + + + + +

                  :

                  + +

                  + + +

                  + + + + + 0) : ?> +

                  +
                    + +
                  • + +
                    +
                    +

                    Fortschritt:

                    +
                    + +
                    +

                    / XP

                    +
                    +
                  • + +
                  + + + + + 0) : ?> +

                  + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + diff --git a/views/html/quests/create.tpl b/views/html/quests/create.tpl new file mode 100644 index 00000000..94bc47b3 --- /dev/null +++ b/views/html/quests/create.tpl @@ -0,0 +1,41 @@ + +
                  + +
                  + +

                  +

                  +

                  + +
                  +
                  + +
                  + +
                  + +
                  + +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  +
                  + +
                  diff --git a/views/html/quests/createmedia.tpl b/views/html/quests/createmedia.tpl new file mode 100644 index 00000000..003ea042 --- /dev/null +++ b/views/html/quests/createmedia.tpl @@ -0,0 +1,19 @@ + +
                  + +
                  + +

                  +

                  Create Questsmedia

                  + + +

                  New mediaId:

                  + + +

                  Error:

                  + +
                  +
                  +
                  + +
                  diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl new file mode 100644 index 00000000..00d7e576 --- /dev/null +++ b/views/html/quests/index.tpl @@ -0,0 +1,49 @@ + +
                  + +
                  + +

                  +

                  + +
                  + + + + + + + + + + + + + + + + + + + + + +
                  + + + + XPs
                  + + + +
                  diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl new file mode 100644 index 00000000..5256b4f7 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,113 @@ + +
                  + +
                  + +

                  + +

                  + + 0) : ?> +
                  +

                  +
                  + + +

                  + + + + + + + +

                  + 0 || !empty($questtext['abort_text'])) : ?> +
                    + +
                  • + + +
                  • + +
                  + + +
                  +
                  + + + +
                  + +

                  +

                  + +

                  +

                  + +
                  + + + +
                  + +

                  +

                  t($quest['task'])?>

                  + + + +

                  : +

                    + +
                  • +
                  • + +
                  + + +

                  + +
                  + + + +
                  +

                  + 0) : ?> +
                    + + +
                  • + : + + + + + +
                  • + +
                  • + + + + + : + + + + + + + + + +
                  • + + +
                  + + : + +
                  + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..10fde93a --- /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..a00ce56d --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,37 @@ + +
                  + +
                  + +

                  + +

                  + +
                  +

                  +
                    + +
                  • + +
                  • + +
                  + +

                  +
                    + +
                  • + +
                  • + +
                  + +

                  +
                    + +
                  • + +
                  • + +
                  +
                  diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..22a9acaa --- /dev/null +++ b/views/html/seminaries/create.tpl @@ -0,0 +1,15 @@ + +
                  + +
                  + +

                  +

                  + +
                  +
                  + +
                  +
                  + +
                  diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl new file mode 100644 index 00000000..8e53fdb5 --- /dev/null +++ b/views/html/seminaries/delete.tpl @@ -0,0 +1,13 @@ + +
                  + +
                  + +

                  +

                  + + +
                  + + +
                  diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl new file mode 100644 index 00000000..27974c1b --- /dev/null +++ b/views/html/seminaries/edit.tpl @@ -0,0 +1,15 @@ + +
                  + +
                  + +

                  +

                  + +
                  +
                  + +
                  +
                  + +
                  diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..40cdcd7c --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,31 @@ +

                  + 0) : ?> + + +
                    + +
                  • + + + +
                    +

                    + 0) : ?> + + + + +

                    +

                    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..b28b5a14 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,40 @@ + +
                  + +
                  + +

                  + 0) : ?> + + +

                  + +

                  + + diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..c71cf164 --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,48 @@ +
                  +

                  + + +
                  + + +
                  +

                  +

                  +
                  + + + +
                  +

                  +
                    +
                  • + + + +

                    +

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

                    +
                  • +
                  +
                  + + +
                  + +

                  +
                    + +
                  • + +

                    +

                    ( XPs)

                    +
                  • + +
                  +

                  + +
                  diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..56b9307b --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,5 @@ +
                • + 0) : ?>
                • +
                • +
                • +
                • 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..2d5484dc --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                  +

                  + +
                  +
                  + +
                  + +
                  + +
                  + +
                  + +
                  +
                  + +
                  diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..510aaed3 --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                  +

                  + +
                  +
                  + +
                  + +
                  + +
                  + +
                  + +
                  +
                  + +
                  diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..9f36049c --- /dev/null +++ b/views/html/users/index.tpl @@ -0,0 +1,9 @@ +

                  + +
                    + +
                  1. format(new \DateTime($user['created'])))?>
                  2. + +
                  diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl new file mode 100644 index 00000000..f141f4cd --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                  + +

                  + +

                  .

                  + +
                  +
                  + +
                  + +
                  +
                  + +
                  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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..29843569 --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,90 @@ +

                  + +

                  + +
                    + &$settings) : ?> +
                  • +
                      + $value) : ?> +
                    • + getMessage(); + break; + } ?> +
                    • + +
                    +
                  • + +
                  + +
                  +
                  + + />
                  + + />
                  + + />
                  + + />
                  + + />
                  +
                  + +
                  diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..b5e23aa7 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,35 @@ +

                  + 0) : ?> + + +

                  +

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

                  + +

                  +
                    + +
                  • + +

                    + +

                    + 0) : ?> + + + + +

                    +

                    +
                  • + +
                  + +

                  + 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..514e3062 --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,347 @@ +@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%;margin-top:25px} +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:1px 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:87.5%} +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:0;margin:0;padding:0} +legend{border:0;padding:0;margin-bottom:15px} +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} +a:hover{color:#62c5cd} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} +aside{display:none} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.breadcrumbs li{display:block;font-size:.875em} +.breadcrumbs .fa{padding-right:5px;font-size:.75em;color:#989693} + +.questgroups li{margin:0 0 25px 0;border-radius:3px;overflow:hidden} +.questgroups img{display:block;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.qgtitle a{background:#5cb6bd;border-radius:3px 3px 0 0;display:block;padding:20px;color:#fff;font-weight:700} +.qgtitle a:hover{background:#62c5cd} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img,.grpqimg{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + +.admin li{margin:0 0 15px 0;display:inline-block} +.admin li a{padding:4px 15px;background:#5cb6bd;border-bottom:2px solid #50a0a6;color:#fff;border-radius:3px} +.admin li a:hover{background:#62c5cd} + +/** Login & Registration **/ + +.logreg{width:auto;display:inline-block;padding:15px 20px;background:#eae8e4;border-radius:3px} +.logreg label{display:block;font-size:.875em} +.logreg input{margin:5px 0 15px} +.logreg .cta{display:block} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last-child{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar,.ltopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup List **/ + +.cglist li{list-style-type:decimal;background:#fff;margin:0 0 6px 25px;padding:5px 15px;border-radius:3px} +.cglist .xp{float:right} +.cgqlist li{list-style-type:square;margin-left:25px;padding:2px 15px} + +/** Charactergroup Profile **/ + +.gbanner img{margin-top:10px;border-radius:3px} +.gbanner h1{margin:10px 0 5px 0} + +.gdata{margin:20px 0 40px 0} +.gdata li{float:left;background:#fff;padding:5px 15px;margin:0 5px 5px 0;border-radius:3px} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + + +/** Charactergroup Quest **/ + +.grpqlist li{background:#fff;margin-bottom:6px;padding:5px 15px 5px 0;font-size:.875em;border-radius:3px} +.grpqlist .date{padding:0 15px;border-right:1px solid #dad8d5} +.grpqlist .group{padding:0 15px} +.grpqlist .xp{float:right} + + +/** Achievements **/ + +.rare ol{margin-bottom:40px} +.rare li{margin-bottom:15px} +.rare img{float:left;width:45px;height:45px;margin:4px 10px 0 0;border-radius:3px} +.rare p{margin:0} + +.achmnts li{background:#fff;margin-bottom:10px;padding:15px;border-radius:3px} +.achmnts img{width:55px;height:55px;float:left;margin-right:15px;border-radius:3px} +.achmnts p{margin:0 0 4px} +.achmnts .unlcked{margin-top:3px;font-size:.875em;font-weight:normal;display:block} +.achmnts .desc{font-size:.875em;padding-top:10px} +.achmnts .prgrss{margin-top:15px} +.achmnts .xpbar{margin:8px 0 0 0;width:80%} +.achmnts .xpnumeric{margin:0} + + +/** Library **/ + +.libindxpr{margin-top:20px} +.libindxpr p{font-weight:bold;margin:0} +.libindxpr .xpbar{float:none;margin:9px 0 20px 0;background:#e4e1dd;width:100%} +.libindxpr .xpbar span{background:#50a4ab} + +.libindx li{text-align:center;background:#fff;border-radius:3px;margin:0 0 15px 0;padding:15px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} +.libindx i{margin-bottom:15px;font-size:1.25em;padding:0} +.libindx .ltopc{overflow:hidden;text-overflow:ellipsis} +.libindx .xpbar{width:100%;margin:0} + +.libtop li{background:#fff;padding:5px 5px 5px 10px;border-radius:3px;margin-bottom:6px} +.libtop p{margin-bottom:2px} +.libtop .addon li{display:inline;font-size:.875em;padding:0} +.libtop .addon li:after{content:", "} +.libtop .addon li:last-child:after{content: ""} + + +/** Quest Types **/ + +.mchoice li{margin-bottom:10px} +.mchoice input[type=checkbox]{width:8%;float:left;margin-top:6px} +.mchoice label{width:92%;float:right} + +.submit{padding:15px;background:#eae8e4;border-radius:3px;margin-bottom:15px} +.submit p{margin:15px 0 0 0;font-weight:bold} +.submit ul{list-style-type:square;margin-left:20px} +.error{padding:2px 5px;border:1px solid #850000;background:#ebd3d3;color:#850000;border-radius:3px} + +.textinput input[type=text]{height:16px} + +.crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} +.crossword td{background:#d7d4cf;padding:1px} +.crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} +.crossword ol{list-style-type:decimal;margin-left:25px} +.crossword li{margin-top:20px} +.crossword .index{position:absolute;font-size:0.625em;margin:-2px 0 0 2px} + +.opponent{width:50%;float:left} +.opponent .portrait{height:250px;overflow:hidden;margin-bottom:15px} +.opponent .hero{background:#fff;max-width:130px} +.opponent .boss{max-width:100%} +.opponent p{text-align:center} +.opponent .fa{font-size:1.25em;color:#c7135b;padding-right:0} +.bossfight .option{background:#fff;margin-bottom:10px;padding:15px 15px 5px;border-radius:3px} + + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;float:left} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;float:right} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gbanner img{float:left;margin:20px 20px 0 0} +.gbanner h1{margin:25px 0 2px} + +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} + +.achmnts .xpbar{width:89%} + +.libindx li{float:left;width:49%} +.libindx li:nth-child(2n){float:right} +.libindx .ltopc{height:80px} + +.opponent .hero{max-width:200px} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{float:right} + +.rare,.hunter{float:left;width:49%} +.hunter{float:right} +.achmnts .desc{padding-top:0} +.achmnts .unlcked{font-size:.75em;float:right;margin:-6px -4px 0 0;color:#878787} + +.libindx li{float:left;width:32%;margin-right:2%} +.libindx li:nth-child(2n){float:left} +.libindx li:nth-child(3n){margin-right:0} + +.bossfight li{float:left;width:44%} +.bossfight li:nth-child(even){float:right} +.bossfight input,.bossfight p{text-align:center} +} + +@media only screen and (min-width:1024px){ +header{position:fixed;left:0;top:0;bottom:0;width:300px;margin:0} +header nav{margin:30px 10% 0} +menu{display:block;opacity:1;position:relative} +menu a{padding:10px 0} +#profile{float:none;margin:0 0 15px 12px} +#toggle,.toggle{display:none} +.wrap{margin:0 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px 0 -40px} +.breadcrumbs li{display:inline;padding-right:5px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} +article{background:#f7f5f2;float:left} +.moodpic{width:880px;height:230px} +.moodpic img{width:100%} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} +aside .cranks li:nth-child(odd){background:#f7f5f2} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Tue, 22 Apr 2014 00:40:21 +0200 Subject: [PATCH 293/340] add library description and calculate total progress of Character --- controllers/LibraryController.inc | 6 ++++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 8666 -> 9152 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 32 ++++++++++++++------ models/SeminariesModel.inc | 6 ++-- views/html/library/index.tpl | 6 ++-- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc index a37618ae..b6c77079 100644 --- a/controllers/LibraryController.inc +++ b/controllers/LibraryController.inc @@ -62,19 +62,25 @@ $character = SeminaryRoleController::$character; // Get Quest topics + $totalQuestcount = 0; + $totalCharacterQuestcount = 0; $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); foreach($questtopics as &$questtopic) { // Get Quest count $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $totalQuestcount += $questtopic['questcount']; // Get Character progress $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + $totalCharacterQuestcount += $questtopic['characterQuestcount']; } // Pass data to view $this->set('seminary', $seminary); + $this->set('totalQuestcount', $totalQuestcount); + $this->set('totalCharacterQuestcount', $totalCharacterQuestcount); $this->set('questtopics', $questtopics); } diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index e4ce1818495c7fdf88a21227fd34f6caa4c6c141..bbbde6ffcb0f1f8bfdc90f658e3b5323f0e693e0 100644 GIT binary patch delta 3405 zcmY+`32YQq9LMq3S}o-$Qi@m*I)E(&D$s)5cezB1pa>!$Y};vfWV_q$?3P1di;93I zimu>+N}^~&RM6B$Lr~C$n4lsECIoM;L=q$sFd~Wg{cRrxU-|-eOF`JZ&r9zyUn?8gub7s$n+0x5XZK6BZ$l%pfeqF}6N}N}vIi*m~@Y zuV6>qY5f$*%^XabFy=TH{4;0xlZ6*i1O9_IVis9wWjdk;D8_soi?`w;%)#ign2BFvcRZqc#y97=P=iaT8K#r28fKyHb5RYuV>j%B?2;LWYFCEZty$Ow z=iB=TD&ggLJ8r}R+=+VcC??eLSuXnEAILv*3$1ith-x?kHG>EiVH}m<>!_vNft|1! zwSr%u5KJJ8u!A@?7uRldQ!aEGzd_C9bn63i3HfJk;u~|Y zC+cjBM{QL(-huU~&v6^-4D7X@N5(SwWTmq;3^nsI)Rxc0c8qW4axt8W3e-$qK`q&K zq!05kG8uE&-k(7wauu~w9hi;=$VW|}7}b6Z>b+U0m3kQU-MgrjU5mZ-{Xee@Y(`~# z4Ey4F)amU?E3Hr;)E10H&15{bVQFXB`uq9y(Mrrm4Y&f8P$OzfHlfFRsDxLcmNbb+`2p0H9YeK0XT69@_zG&J za`IV!byUiomSzN&U>T0WIPxi*_fd(pp!V(vYD-R`R_t$7Vp&e>FJumC#YUr6Y_4@J zD&co=5Pq28LZ|r@Dv>t5S`S?|YGwMOIvRqS;Y`%lEWl!1gG|P}g9Gt^^)f1_e9u}Uc zjX`}S<*0raqqf9DCX_IBTxdyFqh`DgbvmCzHFys-z(<&dYl#U&IkA$szUVVn8BOSC zwvf=^PZIYK;|QG(l_^9q13*uwPJ=M=P6Z+_!}UG5gY|GhZsZX z?93u`mgW+V5&9L>p{*buA|??J5UUAoabNBKwX%*2-7O}%5;|P#36+^d4dD~D#C^o| zWhyrzTXFAo51!y!hj0cFCngdqgNX<+RwL^3Swv(I(+HJSL<6yexK?x+M-Y$LiU+Or z*xz24pi9&d4&f2wiDkrcLZ7lq4)Hiq+0?1smh`M}EV{ypxyxh8w>o^)&8vt8xUX{k z%BWWt^CID5r@&VBy$zxrZ`3O!2~g%yVn})KEL}&LyUMZq%vv!i*Jjs^U%{6mp$8 zLATZoJAt@Q4pC=rBpP!4I0Kw$e7(Rw(fFzp4_7%=p6k>FeBY&U*qI%t2j5v4uXSby zDuaGyFchevUT@CxT>1${LT-4VGuidR&eTXW#w*c;7mGRE|JQMa%j4=$+*dj@f1tKH zz`#mll2;M(BC(*mwAk_MJeNcRAwS|o-5NJk+p@i7n^)s3bG;bvYE=3UgkrA4E2-q1 z>XtoGdzqT%UEyHc)Em`N*BjWhx!bQ9nbTZ9P#ddGmUPceHsoLEF^4^AZCK4+F?`_wuIozk@AGrf8wzv$KZU)?yDFaQ7m delta 2928 zcmY+`drZ}39LMqJz!4FXm2F+#pWk_C_3V6kJ-^4_`Tf4n_j#UE z7gi1}^9@J4PZ(M!kwnaK8xz2?aQ@H|CK?luiI|ICybB-0Xl%eqxDO}eQPln2I0MgO zBwoRL@CN2!I1ia(jL#I((LgEYVk^et>!>e&fD!m9hG9SQG2if~0I%8cc(PCe$*9CU z7=ugkZro_yhU8${9DT+dqQl3$z@G`&jpMKfRgp8O2Y-$!ID`rK2PR?+S?az#RAQy5 zgsV``sm1ZQ!(Q)1-PeX;yx(-v(HEZA0G>d7p&O^-8_24dKGYX4qE=@B<8a7ckDwC0 zgP9n?w5Ov7bzddwxs8~C&FCY{NjuPs`r;*2<|CMmzoHUM;pr+}4o2ZTRK-@I5?^os zz1og%MJ3*ZdT|RX@x7>t9G=YjQ$=%}0ln}wYY%D-&!E<@50$_ml8YHe58lLlOePzR zSD+HCu{NR-+=bj~4x$n{j;g>bQPf{wJZ}dsqbe|p$~>MdQZWg&n~PD2)*)3k4c2zl z%$`H-{$r@;yoq}8WqbWSYDwJ8O3%sh(NTu^I1`Jo0P9eRokW$k7xjX7?DdDp$DFs< zU!f}WEh_PA*6SF;^-W|><`+~#p&T*Y=ZmGIOcGHuNJk!$j5BvPpZ!UZaUfnr%-Fsi+Oknb=>Zt2NOAS zay9Y}(~jCiFQI1sDr#*{qxQhNI3LfUCUP5@y!i|Hm}q7d%ll0(9Stl-B~pzl)ppbi z+EFt&f_gv~szPt!G<**=voA3nhpjHkp+qw=3m2dg+k&c06OPmQKTJn6II02cLe0Dz z^`djA1U^A6!654X?@$T+h+4}~E;JsFTFMwyLg}dQEk|xO<(Px@=u_uqI?A*URl=`P z84n|qF=70?X>+DpOHqlopq8owHM7&Gik(Fz{4uKZ1E`5yvHpNc=z0?M*MmkG(2IXZ zJwX4Z&{9l7_LrH4dO*H)J}Tiws7h6$p3{n|z#jDC5iG)wkdtR_p%M#A4ldc0Wa_WA z$YMYxD@J8jhS^w&D%pNirA}Edq7wcYJ@^}H^JbBa5?O=VQ|nNbX+TY^4YeelI177x zboj0r#MwA%O-l_XvKEzbH5Ow%>i3}+H9mwY@eS0>Z`ff{Nbs;+E zf1&LxLG9udsNG+MdT>3e^v$Rlbf9MbEV4b!i`KVM_kV_}-~hUC1M%Nh$Ay+g)mo3j#_KAxx^k~IkBJEO6cfS5POLRS)P^%`i&RU|erX{8T8YOA9k*m+0ntI+OX%!o5^76`ZNz$A(4Gon4q~3|^E2Z7VA)IO zD66j%(MW{q!f7m!&Z}B!5VIPeA#}XdT8OYf{lu;ie?;ULfsx2~w?8H3{lL|jHkZrm uuZl|_?@cZ67I_N-2jcqO{!LSV3T#Na>h_nX-3rvCdtLtSj41!H49`Dk5chll diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index c85999cc..8ada359f 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-21 21:12+0100\n" -"PO-Revision-Date: 2014-04-21 21:12+0100\n" +"POT-Creation-Date: 2014-04-21 21:42+0100\n" +"PO-Revision-Date: 2014-04-22 00:29+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -147,17 +147,17 @@ msgstr "Platz" msgid "You achieved %d of %d Achievements so far" msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" -#: views/html/achievements/index.tpl:66 views/html/characters/character.tpl:39 +#: views/html/achievements/index.tpl:61 views/html/characters/character.tpl:39 #: views/html/seminarybar/index.tpl:28 #, php-format msgid "achieved at: %s" msgstr "erhalten am: %s" -#: views/html/achievements/index.tpl:77 +#: views/html/achievements/index.tpl:72 msgid "Secret Achievement" msgstr "Geheime Errungenschaft" -#: views/html/achievements/index.tpl:82 +#: views/html/achievements/index.tpl:77 msgid "Continue playing to unlock this secret Achievement" msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" @@ -319,6 +319,20 @@ msgstr "registriere dich" msgid "Questtopics" msgstr "Themen" +#: views/html/library/index.tpl:10 +#, php-format +msgid "Library description, %s, %s" +msgstr "" +"Hier findest du alle Themen aus der Vorlesung „%s“ und die passenden Quests " +"zum Nachschlagen und Wiederholen. Dein Fortschritt in „%s“ beeinflusst den " +"Umfang der Bibliothek, spiele also regelmäßig weiter und schalte so Quest " +"für Quest alle Inhalte frei." + +#: views/html/library/index.tpl:12 +#, php-format +msgid "Total progress: %d %%" +msgstr "Gesamtfortschritt: %d %%" + #: views/html/menu/index.tpl:2 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 @@ -362,15 +376,15 @@ msgstr "Questgruppe" #: views/html/quests/create.tpl:27 msgid "XPs" -msgstr "" +msgstr "XPs" #: views/html/quests/create.tpl:34 msgid "Entry text" -msgstr "" +msgstr "Einstiegstext" #: views/html/quests/create.tpl:36 msgid "Wrong text" -msgstr "" +msgstr "Text für falsche Antwort" #: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:55 msgid "Task" @@ -428,7 +442,7 @@ msgstr "Lösung von %s" #: views/html/quests/submissions.tpl:15 msgid "submitted" -msgstr "eingereicht am %s um %s Uhr" +msgstr "eingereicht" #: views/html/seminaries/create.tpl:7 msgid "New seminary" diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index 251ade4f..554f3d10 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -49,7 +49,7 @@ { // Get seminaries return $this->db->query( - 'SELECT id, created, created_user_id, title, url, description, seminarymedia_id '. + 'SELECT id, created, created_user_id, title, url, course, description, seminarymedia_id '. 'FROM seminaries '. 'ORDER BY created DESC' ); @@ -66,7 +66,7 @@ public function getSeminaryById($seminaryId) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url, description, seminarymedia_id '. + 'SELECT id, created, created_user_id, title, url, course, description, seminarymedia_id '. 'FROM seminaries '. 'WHERE id = ?', 'i', @@ -91,7 +91,7 @@ public function getSeminaryByUrl($seminaryUrl) { $seminary = $this->db->query( - 'SELECT id, created, created_user_id, title, url, description, seminarymedia_id '. + 'SELECT id, created, created_user_id, title, url, course, description, seminarymedia_id '. 'FROM seminaries '. 'WHERE url = ?', 's', diff --git a/views/html/library/index.tpl b/views/html/library/index.tpl index df59a4bd..2dd6cd09 100644 --- a/views/html/library/index.tpl +++ b/views/html/library/index.tpl @@ -7,11 +7,11 @@

                -

                Hier findest du alle Themen aus der Vorlesung "Wissensrepräsentation" und die passenden Quests zum Nachschlagen und Wiederholen. Dein Fortschritt in "Die Legende von Zyren" beeinflusst den Umfang der Bibliothek, spiele also regelmäßig weiter und schalte so Quest für Quest alle Inhalte frei.

                +

                -

                Gesamtfortschritt: 77%

                +

                0) ? $numberFormatter->format(round($totalCharacterQuestcount/$totalQuestcount*100)) : 0) ?>

                - +
                  From 9e80cd414121f2d1727eacc460a755683db78d3f Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 00:40:31 +0200 Subject: [PATCH 294/340] remove Achievement placeholder --- views/html/achievements/index.tpl | 5 ----- 1 file changed, 5 deletions(-) diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 33c967e1..7b6fc469 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -51,11 +51,6 @@

                  . : .

                    -
                  • - -

                    Freigeschaltetes Achievementerreicht am 17.06.104

                    -

                    Das Bild ist entsprechend farbig, ein eventueller Fortschrittsbalken mit 100% soll entfallen.

                    -
                  • From 28a9db07c71c3474cfb6ab7c7c268b584b81ee86 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 00:59:35 +0200 Subject: [PATCH 295/340] add anchors for Achievements and link to these --- views/html/achievements/index.tpl | 6 ++---- views/html/characters/character.tpl | 4 ++-- views/html/seminarybar/index.tpl | 4 ++-- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 7b6fc469..051b1d1e 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -56,8 +56,8 @@ +

                    - format(new \DateTime($achievement['created'])))?>

                    @@ -68,9 +68,7 @@ -

                    - -

                    +

                    diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl index 0ea56007..925a9df0 100644 --- a/views/html/characters/character.tpl +++ b/views/html/characters/character.tpl @@ -33,9 +33,9 @@
                  • - + -

                    +

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

                  • diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl index c71cf164..c2b62e47 100644 --- a/views/html/seminarybar/index.tpl +++ b/views/html/seminarybar/index.tpl @@ -22,9 +22,9 @@
                    • - + -

                      +

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

                    From bf965979280810e3e4295f37a99480b2b199f8fd Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 01:03:42 +0200 Subject: [PATCH 296/340] alphabetical list style for multiple choice quests --- .hgignore | 5 + .htaccess | 50 ++ agents/BottomlevelAgent.inc | 25 + agents/IntermediateAgent.inc | 48 ++ agents/ToplevelAgent.inc | 395 +++++++++ agents/bottomlevel/MenuAgent.inc | 37 + .../QuestgroupshierarchypathAgent.inc | 35 + agents/bottomlevel/SeminarybarAgent.inc | 35 + agents/bottomlevel/SeminarymenuAgent.inc | 35 + agents/bottomlevel/UserrolesAgent.inc | 35 + agents/intermediate/AchievementsAgent.inc | 35 + agents/intermediate/CharactergroupsAgent.inc | 35 + .../CharactergroupsquestsAgent.inc | 35 + agents/intermediate/CharactersAgent.inc | 43 + agents/intermediate/ErrorAgent.inc | 35 + agents/intermediate/IntroductionAgent.inc | 35 + agents/intermediate/LibraryAgent.inc | 35 + agents/intermediate/MediaAgent.inc | 35 + agents/intermediate/QuestgroupsAgent.inc | 36 + agents/intermediate/QuestsAgent.inc | 54 ++ agents/intermediate/SeminariesAgent.inc | 35 + agents/intermediate/UploadsAgent.inc | 35 + agents/intermediate/UsersAgent.inc | 44 + agents/toplevel/BinaryAgent.inc | 41 + agents/toplevel/FaultAgent.inc | 35 + agents/toplevel/HtmlAgent.inc | 70 ++ apis/WebApi.inc | 250 ++++++ app/Controller.inc | 106 +++ app/Model.inc | 42 + app/QuesttypeAgent.inc | 267 ++++++ app/QuesttypeController.inc | 349 ++++++++ app/QuesttypeModel.inc | 154 ++++ app/QuesttypeView.inc | 76 ++ app/TextFormatter.inc | 141 +++ app/ToplevelAgent.inc | 36 + app/Utils.inc | 112 +++ app/controllers/IntermediateController.inc | 139 +++ app/controllers/SeminaryRoleController.inc | 277 ++++++ app/exceptions/FileUploadException.inc | 75 ++ app/exceptions/MaxFilesizeException.inc | 51 ++ .../QuesttypeAgentNotFoundException.inc | 77 ++ .../QuesttypeAgentNotValidException.inc | 77 ++ .../QuesttypeControllerNotFoundException.inc | 77 ++ .../QuesttypeControllerNotValidException.inc | 77 ++ .../QuesttypeModelNotFoundException.inc | 77 ++ .../QuesttypeModelNotValidException.inc | 77 ++ .../SubmissionNotValidException.inc | 77 ++ app/exceptions/WrongFiletypeException.inc | 75 ++ app/lib/Password.inc | 316 +++++++ bootstrap.inc | 33 + configs/AppConfig.inc | 199 +++++ configs/CoreConfig.inc | 167 ++++ controllers/AchievementsController.inc | 172 ++++ controllers/BinaryController.inc | 37 + controllers/CharactergroupsController.inc | 150 ++++ .../CharactergroupsquestsController.inc | 91 ++ controllers/CharactersController.inc | 280 ++++++ controllers/ErrorController.inc | 48 ++ controllers/FaultController.inc | 37 + controllers/HtmlController.inc | 59 ++ controllers/IntroductionController.inc | 37 + controllers/LibraryController.inc | 135 +++ controllers/MediaController.inc | 432 ++++++++++ controllers/MenuController.inc | 52 ++ controllers/QuestgroupsController.inc | 223 +++++ .../QuestgroupshierarchypathController.inc | 90 ++ controllers/QuestsController.inc | 814 ++++++++++++++++++ controllers/SeminariesController.inc | 252 ++++++ controllers/SeminarybarController.inc | 86 ++ controllers/SeminarymenuController.inc | 52 ++ controllers/UploadsController.inc | 174 ++++ controllers/UserrolesController.inc | 47 + controllers/UsersController.inc | 363 ++++++++ .../components/AchievementComponent.inc | 41 + controllers/components/AuthComponent.inc | 79 ++ .../components/ValidationComponent.inc | 140 +++ core/Agent.inc | 607 +++++++++++++ core/Api.inc | 163 ++++ core/Autoloader.inc | 98 +++ core/ClassLoader.inc | 129 +++ core/Component.inc | 85 ++ core/Config.inc | 49 ++ core/Controller.inc | 433 ++++++++++ core/Driver.inc | 96 +++ core/Exception.inc | 65 ++ core/Linker.inc | 311 +++++++ core/Logger.inc | 132 +++ core/Model.inc | 141 +++ core/Request.inc | 64 ++ core/Response.inc | 158 ++++ core/View.inc | 124 +++ core/WebUtils.inc | 75 ++ drivers/DatabaseDriver.inc | 87 ++ drivers/MysqliDriver.inc | 169 ++++ exceptions/AccessDeniedException.inc | 51 ++ exceptions/ActionNotFoundException.inc | 77 ++ exceptions/AgentNotFoundException.inc | 67 ++ exceptions/AgentNotValidException.inc | 67 ++ exceptions/ClassNotFoundException.inc | 77 ++ exceptions/ClassNotValidException.inc | 77 ++ exceptions/ComponentNotFoundException.inc | 67 ++ exceptions/ComponentNotValidException.inc | 67 ++ exceptions/ControllerNotFoundException.inc | 67 ++ exceptions/ControllerNotValidException.inc | 67 ++ exceptions/DatamodelException.inc | 99 +++ exceptions/DriverNotFoundException.inc | 68 ++ exceptions/DriverNotValidException.inc | 68 ++ exceptions/FatalDatamodelException.inc | 96 +++ exceptions/IdNotFoundException.inc | 77 ++ exceptions/LayoutNotFoundException.inc | 68 ++ exceptions/LayoutNotValidException.inc | 67 ++ exceptions/ModelNotFoundException.inc | 67 ++ exceptions/ModelNotValidException.inc | 68 ++ exceptions/ParamsNotValidException.inc | 77 ++ exceptions/ServiceUnavailableException.inc | 77 ++ exceptions/ViewNotFoundException.inc | 77 ++ locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 0 -> 9152 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 705 +++++++++++++++ logs/empty | 0 media/empty | 0 models/AchievementsModel.inc | 549 ++++++++++++ models/AvatarsModel.inc | 92 ++ models/CharactergroupsModel.inc | 197 +++++ models/CharactergroupsquestsModel.inc | 136 +++ models/CharactersModel.inc | 475 ++++++++++ models/CharactertypesModel.inc | 57 ++ models/DatabaseModel.inc | 83 ++ models/MediaModel.inc | 195 +++++ models/QuestgroupsModel.inc | 635 ++++++++++++++ models/QuestgroupshierarchyModel.inc | 128 +++ models/QuestsModel.inc | 460 ++++++++++ models/QuesttextsModel.inc | 220 +++++ models/QuesttopicsModel.inc | 154 ++++ models/QuesttypesModel.inc | 77 ++ models/SeminariesModel.inc | 193 +++++ models/SeminarycharacterfieldsModel.inc | 78 ++ models/UploadsModel.inc | 148 ++++ models/UserrolesModel.inc | 77 ++ models/UsersModel.inc | 370 ++++++++ models/UserseminaryrolesModel.inc | 78 ++ .../bossfight/BossfightQuesttypeAgent.inc | 24 + .../BossfightQuesttypeController.inc | 244 ++++++ .../bossfight/BossfightQuesttypeModel.inc | 183 ++++ questtypes/bossfight/html/quest.tpl | 51 ++ questtypes/bossfight/html/submission.tpl | 38 + .../choiceinput/ChoiceinputQuesttypeAgent.inc | 24 + .../ChoiceinputQuesttypeController.inc | 157 ++++ .../choiceinput/ChoiceinputQuesttypeModel.inc | 153 ++++ questtypes/choiceinput/html/quest.tpl | 15 + questtypes/choiceinput/html/submission.tpl | 12 + .../crossword/CrosswordQuesttypeAgent.inc | 24 + .../CrosswordQuesttypeController.inc | 351 ++++++++ .../crossword/CrosswordQuesttypeModel.inc | 93 ++ questtypes/crossword/html/quest.tpl | 49 ++ questtypes/crossword/html/submission.tpl | 48 ++ .../dragndrop/DragndropQuesttypeAgent.inc | 24 + .../DragndropQuesttypeController.inc | 203 +++++ .../dragndrop/DragndropQuesttypeModel.inc | 180 ++++ questtypes/dragndrop/html/quest.tpl | 17 + questtypes/dragndrop/html/submission.tpl | 15 + questtypes/dummy/DummyQuesttypeAgent.inc | 24 + questtypes/dummy/DummyQuesttypeController.inc | 93 ++ questtypes/dummy/DummyQuesttypeModel.inc | 25 + questtypes/dummy/html/quest.tpl | 4 + questtypes/dummy/html/submission.tpl | 0 .../MultiplechoiceQuesttypeAgent.inc | 24 + .../MultiplechoiceQuesttypeController.inc | 262 ++++++ .../MultiplechoiceQuesttypeModel.inc | 159 ++++ questtypes/multiplechoice/html/quest.tpl | 21 + questtypes/multiplechoice/html/submission.tpl | 16 + questtypes/submit/SubmitQuesttypeAgent.inc | 24 + .../submit/SubmitQuesttypeController.inc | 166 ++++ questtypes/submit/SubmitQuesttypeModel.inc | 106 +++ questtypes/submit/html/quest.tpl | 27 + questtypes/submit/html/submission.tpl | 11 + .../textinput/TextinputQuesttypeAgent.inc | 24 + .../TextinputQuesttypeController.inc | 171 ++++ .../textinput/TextinputQuesttypeModel.inc | 117 +++ questtypes/textinput/html/quest.tpl | 11 + questtypes/textinput/html/submission.tpl | 7 + requests/WebRequest.inc | 401 +++++++++ responses/WebResponse.inc | 250 ++++++ seminarymedia/empty | 0 tmp/empty | 0 uploads/empty | 0 views/binary/binary.tpl | 1 + views/binary/error/index.tpl | 1 + views/binary/media/achievement.tpl | 1 + views/binary/media/avatar.tpl | 1 + views/binary/media/index.tpl | 1 + views/binary/media/seminary.tpl | 1 + views/binary/media/seminaryheader.tpl | 1 + views/binary/uploads/index.tpl | 1 + views/error.tpl | 13 + views/fault/error/index.tpl | 2 + views/fault/fault.tpl | 16 + views/html/achievements/index.tpl | 87 ++ views/html/charactergroups/group.tpl | 50 ++ views/html/charactergroups/groupsgroup.tpl | 25 + views/html/charactergroups/index.tpl | 14 + views/html/charactergroupsquests/quest.tpl | 53 ++ views/html/characters/character.tpl | 101 +++ views/html/characters/index.tpl | 17 + views/html/characters/register.tpl | 82 ++ views/html/error/index.tpl | 2 + views/html/html.tpl | 56 ++ views/html/introduction/index.tpl | 36 + views/html/library/index.tpl | 28 + views/html/library/topic.tpl | 30 + views/html/menu/index.tpl | 9 + views/html/questgroups/create.tpl | 16 + views/html/questgroups/questgroup.tpl | 58 ++ views/html/questgroupshierarchypath/index.tpl | 17 + views/html/quests/create.tpl | 41 + views/html/quests/createmedia.tpl | 19 + views/html/quests/index.tpl | 49 ++ views/html/quests/quest.tpl | 113 +++ views/html/quests/submission.tpl | 13 + views/html/quests/submissions.tpl | 37 + views/html/seminaries/create.tpl | 15 + views/html/seminaries/delete.tpl | 13 + views/html/seminaries/edit.tpl | 15 + views/html/seminaries/index.tpl | 31 + views/html/seminaries/seminary.tpl | 40 + views/html/seminarybar/index.tpl | 48 ++ views/html/seminarymenu/index.tpl | 5 + views/html/userroles/user.tpl | 5 + views/html/users/create.tpl | 18 + views/html/users/delete.tpl | 8 + views/html/users/edit.tpl | 18 + views/html/users/index.tpl | 9 + views/html/users/login.tpl | 15 + views/html/users/logout.tpl | 0 views/html/users/register.tpl | 90 ++ views/html/users/user.tpl | 35 + views/inlineerror.tpl | 1 + www/.htaccess | 8 + www/css/desktop.css | 348 ++++++++ www/error403.html | 14 + www/error404.html | 14 + www/error500.html | 14 + www/index.php | 45 + www/js/dnd.js | 54 ++ www/js/jquery.nicescroll.min.js | 111 +++ 244 files changed, 24133 insertions(+) create mode 100644 .hgignore create mode 100644 .htaccess create mode 100644 agents/BottomlevelAgent.inc create mode 100644 agents/IntermediateAgent.inc create mode 100644 agents/ToplevelAgent.inc create mode 100644 agents/bottomlevel/MenuAgent.inc create mode 100644 agents/bottomlevel/QuestgroupshierarchypathAgent.inc create mode 100644 agents/bottomlevel/SeminarybarAgent.inc create mode 100644 agents/bottomlevel/SeminarymenuAgent.inc create mode 100644 agents/bottomlevel/UserrolesAgent.inc create mode 100644 agents/intermediate/AchievementsAgent.inc create mode 100644 agents/intermediate/CharactergroupsAgent.inc create mode 100644 agents/intermediate/CharactergroupsquestsAgent.inc create mode 100644 agents/intermediate/CharactersAgent.inc create mode 100644 agents/intermediate/ErrorAgent.inc create mode 100644 agents/intermediate/IntroductionAgent.inc create mode 100644 agents/intermediate/LibraryAgent.inc create mode 100644 agents/intermediate/MediaAgent.inc create mode 100644 agents/intermediate/QuestgroupsAgent.inc create mode 100644 agents/intermediate/QuestsAgent.inc create mode 100644 agents/intermediate/SeminariesAgent.inc create mode 100644 agents/intermediate/UploadsAgent.inc create mode 100644 agents/intermediate/UsersAgent.inc create mode 100644 agents/toplevel/BinaryAgent.inc create mode 100644 agents/toplevel/FaultAgent.inc create mode 100644 agents/toplevel/HtmlAgent.inc create mode 100644 apis/WebApi.inc create mode 100644 app/Controller.inc create mode 100644 app/Model.inc create mode 100644 app/QuesttypeAgent.inc create mode 100644 app/QuesttypeController.inc create mode 100644 app/QuesttypeModel.inc create mode 100644 app/QuesttypeView.inc create mode 100644 app/TextFormatter.inc create mode 100644 app/ToplevelAgent.inc create mode 100644 app/Utils.inc create mode 100644 app/controllers/IntermediateController.inc create mode 100644 app/controllers/SeminaryRoleController.inc create mode 100644 app/exceptions/FileUploadException.inc create mode 100644 app/exceptions/MaxFilesizeException.inc create mode 100644 app/exceptions/QuesttypeAgentNotFoundException.inc create mode 100644 app/exceptions/QuesttypeAgentNotValidException.inc create mode 100644 app/exceptions/QuesttypeControllerNotFoundException.inc create mode 100644 app/exceptions/QuesttypeControllerNotValidException.inc create mode 100644 app/exceptions/QuesttypeModelNotFoundException.inc create mode 100644 app/exceptions/QuesttypeModelNotValidException.inc create mode 100644 app/exceptions/SubmissionNotValidException.inc create mode 100644 app/exceptions/WrongFiletypeException.inc create mode 100644 app/lib/Password.inc create mode 100644 bootstrap.inc create mode 100644 configs/AppConfig.inc create mode 100644 configs/CoreConfig.inc create mode 100644 controllers/AchievementsController.inc create mode 100644 controllers/BinaryController.inc create mode 100644 controllers/CharactergroupsController.inc create mode 100644 controllers/CharactergroupsquestsController.inc create mode 100644 controllers/CharactersController.inc create mode 100644 controllers/ErrorController.inc create mode 100644 controllers/FaultController.inc create mode 100644 controllers/HtmlController.inc create mode 100644 controllers/IntroductionController.inc create mode 100644 controllers/LibraryController.inc create mode 100644 controllers/MediaController.inc create mode 100644 controllers/MenuController.inc create mode 100644 controllers/QuestgroupsController.inc create mode 100644 controllers/QuestgroupshierarchypathController.inc create mode 100644 controllers/QuestsController.inc create mode 100644 controllers/SeminariesController.inc create mode 100644 controllers/SeminarybarController.inc create mode 100644 controllers/SeminarymenuController.inc create mode 100644 controllers/UploadsController.inc create mode 100644 controllers/UserrolesController.inc create mode 100644 controllers/UsersController.inc create mode 100644 controllers/components/AchievementComponent.inc create mode 100644 controllers/components/AuthComponent.inc create mode 100644 controllers/components/ValidationComponent.inc create mode 100644 core/Agent.inc create mode 100644 core/Api.inc create mode 100644 core/Autoloader.inc create mode 100644 core/ClassLoader.inc create mode 100644 core/Component.inc create mode 100644 core/Config.inc create mode 100644 core/Controller.inc create mode 100644 core/Driver.inc create mode 100644 core/Exception.inc create mode 100644 core/Linker.inc create mode 100644 core/Logger.inc create mode 100644 core/Model.inc create mode 100644 core/Request.inc create mode 100644 core/Response.inc create mode 100644 core/View.inc create mode 100644 core/WebUtils.inc create mode 100644 drivers/DatabaseDriver.inc create mode 100644 drivers/MysqliDriver.inc create mode 100644 exceptions/AccessDeniedException.inc create mode 100644 exceptions/ActionNotFoundException.inc create mode 100644 exceptions/AgentNotFoundException.inc create mode 100644 exceptions/AgentNotValidException.inc create mode 100644 exceptions/ClassNotFoundException.inc create mode 100644 exceptions/ClassNotValidException.inc create mode 100644 exceptions/ComponentNotFoundException.inc create mode 100644 exceptions/ComponentNotValidException.inc create mode 100644 exceptions/ControllerNotFoundException.inc create mode 100644 exceptions/ControllerNotValidException.inc create mode 100644 exceptions/DatamodelException.inc create mode 100644 exceptions/DriverNotFoundException.inc create mode 100644 exceptions/DriverNotValidException.inc create mode 100644 exceptions/FatalDatamodelException.inc create mode 100644 exceptions/IdNotFoundException.inc create mode 100644 exceptions/LayoutNotFoundException.inc create mode 100644 exceptions/LayoutNotValidException.inc create mode 100644 exceptions/ModelNotFoundException.inc create mode 100644 exceptions/ModelNotValidException.inc create mode 100644 exceptions/ParamsNotValidException.inc create mode 100644 exceptions/ServiceUnavailableException.inc create mode 100644 exceptions/ViewNotFoundException.inc create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.mo create mode 100644 locale/de_DE/LC_MESSAGES/The Legend of Z.po create mode 100644 logs/empty create mode 100644 media/empty create mode 100644 models/AchievementsModel.inc create mode 100644 models/AvatarsModel.inc create mode 100644 models/CharactergroupsModel.inc create mode 100644 models/CharactergroupsquestsModel.inc create mode 100644 models/CharactersModel.inc create mode 100644 models/CharactertypesModel.inc create mode 100644 models/DatabaseModel.inc create mode 100644 models/MediaModel.inc create mode 100644 models/QuestgroupsModel.inc create mode 100644 models/QuestgroupshierarchyModel.inc create mode 100644 models/QuestsModel.inc create mode 100644 models/QuesttextsModel.inc create mode 100644 models/QuesttopicsModel.inc create mode 100644 models/QuesttypesModel.inc create mode 100644 models/SeminariesModel.inc create mode 100644 models/SeminarycharacterfieldsModel.inc create mode 100644 models/UploadsModel.inc create mode 100644 models/UserrolesModel.inc create mode 100644 models/UsersModel.inc create mode 100644 models/UserseminaryrolesModel.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeAgent.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeController.inc create mode 100644 questtypes/bossfight/BossfightQuesttypeModel.inc create mode 100644 questtypes/bossfight/html/quest.tpl create mode 100644 questtypes/bossfight/html/submission.tpl create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeController.inc create mode 100644 questtypes/choiceinput/ChoiceinputQuesttypeModel.inc create mode 100644 questtypes/choiceinput/html/quest.tpl create mode 100644 questtypes/choiceinput/html/submission.tpl create mode 100644 questtypes/crossword/CrosswordQuesttypeAgent.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeController.inc create mode 100644 questtypes/crossword/CrosswordQuesttypeModel.inc create mode 100644 questtypes/crossword/html/quest.tpl create mode 100644 questtypes/crossword/html/submission.tpl create mode 100644 questtypes/dragndrop/DragndropQuesttypeAgent.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeController.inc create mode 100644 questtypes/dragndrop/DragndropQuesttypeModel.inc create mode 100644 questtypes/dragndrop/html/quest.tpl create mode 100644 questtypes/dragndrop/html/submission.tpl create mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc create mode 100644 questtypes/dummy/DummyQuesttypeController.inc create mode 100644 questtypes/dummy/DummyQuesttypeModel.inc create mode 100644 questtypes/dummy/html/quest.tpl create mode 100644 questtypes/dummy/html/submission.tpl create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeAgent.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc create mode 100644 questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc create mode 100644 questtypes/multiplechoice/html/quest.tpl create mode 100644 questtypes/multiplechoice/html/submission.tpl create mode 100644 questtypes/submit/SubmitQuesttypeAgent.inc create mode 100644 questtypes/submit/SubmitQuesttypeController.inc create mode 100644 questtypes/submit/SubmitQuesttypeModel.inc create mode 100644 questtypes/submit/html/quest.tpl create mode 100644 questtypes/submit/html/submission.tpl create mode 100644 questtypes/textinput/TextinputQuesttypeAgent.inc create mode 100644 questtypes/textinput/TextinputQuesttypeController.inc create mode 100644 questtypes/textinput/TextinputQuesttypeModel.inc create mode 100644 questtypes/textinput/html/quest.tpl create mode 100644 questtypes/textinput/html/submission.tpl create mode 100644 requests/WebRequest.inc create mode 100644 responses/WebResponse.inc create mode 100644 seminarymedia/empty create mode 100644 tmp/empty create mode 100644 uploads/empty create mode 100644 views/binary/binary.tpl create mode 100644 views/binary/error/index.tpl create mode 100644 views/binary/media/achievement.tpl create mode 100644 views/binary/media/avatar.tpl create mode 100644 views/binary/media/index.tpl create mode 100644 views/binary/media/seminary.tpl create mode 100644 views/binary/media/seminaryheader.tpl create mode 100644 views/binary/uploads/index.tpl create mode 100644 views/error.tpl create mode 100644 views/fault/error/index.tpl create mode 100644 views/fault/fault.tpl create mode 100644 views/html/achievements/index.tpl create mode 100644 views/html/charactergroups/group.tpl create mode 100644 views/html/charactergroups/groupsgroup.tpl create mode 100644 views/html/charactergroups/index.tpl create mode 100644 views/html/charactergroupsquests/quest.tpl create mode 100644 views/html/characters/character.tpl create mode 100644 views/html/characters/index.tpl create mode 100644 views/html/characters/register.tpl create mode 100644 views/html/error/index.tpl create mode 100644 views/html/html.tpl create mode 100644 views/html/introduction/index.tpl create mode 100644 views/html/library/index.tpl create mode 100644 views/html/library/topic.tpl create mode 100644 views/html/menu/index.tpl create mode 100644 views/html/questgroups/create.tpl create mode 100644 views/html/questgroups/questgroup.tpl create mode 100644 views/html/questgroupshierarchypath/index.tpl create mode 100644 views/html/quests/create.tpl create mode 100644 views/html/quests/createmedia.tpl create mode 100644 views/html/quests/index.tpl create mode 100644 views/html/quests/quest.tpl create mode 100644 views/html/quests/submission.tpl create mode 100644 views/html/quests/submissions.tpl create mode 100644 views/html/seminaries/create.tpl create mode 100644 views/html/seminaries/delete.tpl create mode 100644 views/html/seminaries/edit.tpl create mode 100644 views/html/seminaries/index.tpl create mode 100644 views/html/seminaries/seminary.tpl create mode 100644 views/html/seminarybar/index.tpl create mode 100644 views/html/seminarymenu/index.tpl create mode 100644 views/html/userroles/user.tpl create mode 100644 views/html/users/create.tpl create mode 100644 views/html/users/delete.tpl create mode 100644 views/html/users/edit.tpl create mode 100644 views/html/users/index.tpl create mode 100644 views/html/users/login.tpl create mode 100644 views/html/users/logout.tpl create mode 100644 views/html/users/register.tpl create mode 100644 views/html/users/user.tpl create mode 100644 views/inlineerror.tpl create mode 100644 www/.htaccess create mode 100644 www/css/desktop.css create mode 100644 www/error403.html create mode 100644 www/error404.html create mode 100644 www/error500.html create mode 100644 www/index.php create mode 100644 www/js/dnd.js create mode 100644 www/js/jquery.nicescroll.min.js diff --git a/.hgignore b/.hgignore new file mode 100644 index 00000000..43dff51a --- /dev/null +++ b/.hgignore @@ -0,0 +1,5 @@ +syntax: regex +^media/* +^tmp/* +^uploads/* +^seminarymedia/* 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..49512791 --- /dev/null +++ b/agents/bottomlevel/MenuAgent.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\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) + { + // Add Seminary menu + $this->addSubAgent('Seminarymenu'); + } + + } + +?> 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/SeminarybarAgent.inc b/agents/bottomlevel/SeminarybarAgent.inc new file mode 100644 index 00000000..10315ab5 --- /dev/null +++ b/agents/bottomlevel/SeminarybarAgent.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 sidebar with Seminary related information. + * + * @author Oliver Hanraths + */ + class SeminarybarAgent extends \nre\agents\BottomlevelAgent + { + + + + + /** + * Action: index. + */ + public function index(\nre\core\Request $request, \nre\core\Response $response) + { + } + + } + +?> diff --git a/agents/bottomlevel/SeminarymenuAgent.inc b/agents/bottomlevel/SeminarymenuAgent.inc new file mode 100644 index 00000000..375eab1e --- /dev/null +++ b/agents/bottomlevel/SeminarymenuAgent.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 with Seminary related links. + * + * @author Oliver Hanraths + */ + class SeminarymenuAgent 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/AchievementsAgent.inc b/agents/intermediate/AchievementsAgent.inc new file mode 100644 index 00000000..e6b965f9 --- /dev/null +++ b/agents/intermediate/AchievementsAgent.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 Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsAgent extends \nre\agents\IntermediateAgent + { + + + + + /** + * Action: index. + */ + public function index(\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/LibraryAgent.inc b/agents/intermediate/LibraryAgent.inc new file mode 100644 index 00000000..aedc890a --- /dev/null +++ b/agents/intermediate/LibraryAgent.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 Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryAgent 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..52ecd4e1 --- /dev/null +++ b/agents/intermediate/QuestgroupsAgent.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\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)); + } + + } + +?> diff --git a/agents/intermediate/QuestsAgent.inc b/agents/intermediate/QuestsAgent.inc new file mode 100644 index 00000000..273a47ab --- /dev/null +++ b/agents/intermediate/QuestsAgent.inc @@ -0,0 +1,54 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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); + } + + + /** + * Action: submissions. + */ + public function submissions(\nre\core\Request $request, \nre\core\Response $response) + { + $this->addSubAgent('Questgroupshierarchypath', '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); + } + + } + +?> 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/UploadsAgent.inc b/agents/intermediate/UploadsAgent.inc new file mode 100644 index 00000000..457b6a49 --- /dev/null +++ b/agents/intermediate/UploadsAgent.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 user uploads. + * + * @author Oliver Hanraths + */ + class UploadsAgent 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..049ba904 --- /dev/null +++ b/agents/toplevel/HtmlAgent.inc @@ -0,0 +1,70 @@ + + * @copyright 2014 Heinrich-Heine-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'); + + // Add Seminary sidebar + $this->addSubAgent('Seminarybar'); + } + + + + + 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..461c9fd1 --- /dev/null +++ b/app/Controller.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 text formatter + $this->set('t', new \hhu\z\TextFormatter($this->linker)); + + // Create date and time and number 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 + )); + $this->set('numberFormatter', new \NumberFormatter( + \nre\core\Config::getDefault('locale'), + \NumberFormatter::DEFAULT_STYLE + )); + } + + + /** + * 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..25544ab2 --- /dev/null +++ b/app/QuesttypeController.inc @@ -0,0 +1,349 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public abstract function quest($seminary, $questgroup, $quest, $character, $exception); + + + + /** + * 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/TextFormatter.inc b/app/TextFormatter.inc new file mode 100644 index 00000000..e226c7e5 --- /dev/null +++ b/app/TextFormatter.inc @@ -0,0 +1,141 @@ + + * @copyright 2014 Heinrich-Heine-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 to format text with different syntax tags. + * + * @author Oliver Hanraths + */ + class TextFormatter + { + /** + * Linker to create links. + * + * @var Linker + */ + private $linker; + /** + * Media-Model to retrieve media data + * + * @static + * @var model + */ + private static $Media = null; + + + + + /** + * Create a new text formatter. + * + * @param Linker $linker Linker to create links with + */ + public function __construct(\nre\core\Linker $linker) + { + $this->linker = $linker; + } + + + + + /** + * Format a string. + * + * @param string $string String to format + * @return string Formatted string + */ + public function t($string) + { + // Remove chars + $string = htmlspecialchars($string, ENT_NOQUOTES); + + // Create tables + $string = str_replace('[table]', '

                    ', $string); + $string = str_replace('[/table]', '

                    ', $string); + $string = str_replace('[tr]', '', $string); + $string = str_replace('[/tr]', '', $string); + $string = str_replace('[th]', '', $string); + $string = str_replace('[/th]', '', $string); + $string = str_replace('[td]', '', $string); + $string = str_replace('[/td]', '', $string); + + // Create links + $string = preg_replace('!(^|\s)"([^"]+)":(https?://[^\s]+)(\s|$)!i', '$1$2$4', $string); + $string = preg_replace('!(^|\s)(https?://[^\s]+)(\s|$)!i', '$1$2$4', $string); + + // Handle Seminarymedia + $seminarymedia = array(); + preg_match_all('/\[seminarymedia:(\d+)\]/iu', $string, $matches); //, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); + $seminarymediaIds = array_unique($matches[1]); + foreach($seminarymediaIds as &$seminarymediaId) + { + $replacement = null; + if(!is_null(\hhu\z\controllers\SeminaryRoleController::$seminary) && $this->loadMediaModel()) + { + try { + $medium = self::$Media->getSeminaryMediaById($seminarymediaId); + $replacement = sprintf( + '%s', + $this->linker->link(array('media','seminary', \hhu\z\controllers\SeminaryRoleController::$seminary['url'],$medium['url'])), + $medium['description'] + ); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + } + + $seminarymedia[$seminarymediaId] = $replacement; + } + foreach($seminarymedia as $seminarymediaId => $replacement) { + $string = str_replace("[seminarymedia:$seminarymediaId]", $replacement, $string); + } + + + // Return processed string + return nl2br($string); + } + + + + + /** + * Load the Media-Model if it is not loaded + * + * @return boolean Whether the Media-Model has been loaded or not + */ + private function loadMediaModel() + { + // Do not load Model if it has already been loaded + if(!is_null(self::$Media)) { + return true; + } + + try { + // Load class + Model::load('media'); + + // Construct Model + self::$Media = Model::factory('media'); + } + catch(\Exception $e) { + } + + + // Return whether Media-Model has been loaded or not + return !is_null(self::$Media); + } + + } + +?> 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..6f0475df --- /dev/null +++ b/app/Utils.inc @@ -0,0 +1,112 @@ + + * @copyright 2014 Heinrich-Heine-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 + */ + public 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); + } + + + /** + * Send an e‑mail. + * + * @param string $from Sender of mail + * @param mixed $to One (string) or many (array) receivers + * @param string $subject Subject of mail + * @param string $message Message of mail + * @param boolean $html Whether mail should be formatted as HTML or not + * @return Whether mail has been send or not + */ + public static function sendMail($from, $to, $subject, $message, $html=false) + { + // Set receivers + $to = is_array($to) ? implode(',', $to) : $to; + + // Set header + $headers = array(); + $headers[] = 'Content-type: text/'.($html ? 'html' : 'plain').'; charset=UTF-8'; + if(!is_null($from)) { + $headers[] = "From: $from"; + } + $header = implode("\r\n", $headers)."\r\n"; + + + // Send mail + return mail($to, $subject, $message, $header); + } + + } + +?> diff --git a/app/controllers/IntermediateController.inc b/app/controllers/IntermediateController.inc new file mode 100644 index 00000000..19647d7e --- /dev/null +++ b/app/controllers/IntermediateController.inc @@ -0,0 +1,139 @@ + + * @copyright 2014 Heinrich-Heine-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 an IntermediateAgent. + * + * @author Oliver Hanraths + */ + abstract class IntermediateController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('users', 'userroles', 'seminaries', 'characters'); + /** + * Current user + * + * @var array + */ + public static $user = null; + + + + + /** + * Construct a new IntermediateController. + * + * @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 { + self::$user = $this->Users->getUserById($this->Auth->getUserId()); + self::$user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById(self::$user['id'])); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Set userdata + $this->set('loggedUser', self::$user); + } + + + /** + * 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 + $userRoles = array('guest'); + if(!is_null(self::$user)) { + $userRoles = self::$user['roles']; + } + + + // 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 permissions of Intermediate Controller for current action + $controller = $this->agent->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/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc new file mode 100644 index 00000000..69e9a38b --- /dev/null +++ b/app/controllers/SeminaryRoleController.inc @@ -0,0 +1,277 @@ + + * @copyright 2014 Heinrich-Heine-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\controllers\IntermediateController + { + /** + * Required components + * + * @var array + */ + public $components = array('achievement', 'auth'); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'userseminaryroles', 'characters', 'achievements'); + /** + * Current Seminary + * + * var array + */ + public static $seminary = null; + /** + * Character of current user and Seminary + * + * @var array + */ + public static $character = 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); + + // Get Seminary and Character data + try { + self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); + self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + + // Check permissions + $this->checkPermission($request, $response); + + // Check achievements + $this->checkAchievements($request, $response); + + // Set Seminary and Character data + $this->set('loggedSeminary', self::$seminary); + $this->set('loggedCharacter', self::$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) + { + // Do not check index page + if(is_null($request->getParam(3))) { + return; + } + + + // 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(!array_key_exists('seminaryroles', self::$user) || count(array_intersect(self::$user['seminaryroles'], $permissions)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + + + /** + * Check for newly achieved Achievements. + */ + private function checkAchievements(\nre\core\Request $request, \nre\core\Response $response) + { + // Check if Character is present + if(is_null(self::$character)) { + return; + } + + // Get unachieved Achievments + $achievements = $this->Achievements->getUnachhievedAchievementsForCharacter(self::$seminary['id'], self::$character['id']); + if(in_array('user', self::$user['seminaryroles'])) { + $achievements = array_merge($achievements, $this->Achievements->getUnachievedOnlyOnceAchievementsForSeminary(self::$seminary['id'])); + } + + // Check conditions + foreach($achievements as &$achievement) + { + // Get conditions + $conditions = array(); + $progress = 0; + switch($achievement['condition']) + { + // Date conditions + case 'date': + $conditionsDate = $this->Achievements->getAchievementConditionsDate($achievement['id']); + foreach($conditionsDate as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionDate', + 'params' => array( + $condition['select'] + ) + ); + } + break; + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionCharacter', + 'params' => array( + $condition['field'], + $condition['value'], + self::$character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionQuest', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + self::$character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'checkAchievementConditionAchievement', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + self::$character['id'] + ) + ); + } + break; + } + + // Check conditions + $achieved = ($achievement['all_conditions'] == 1); + foreach($conditions as &$condition) + { + // Calculate result of condition + $result = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + + // The overall result and abort if possible + if($achievement['all_conditions']) + { + if(!$result) { + $achieved = false; + break; + } + } + else + { + if($result) { + $achieved = true; + break; + } + } + + } + + // Set status + if($achieved) { + $this->Achievements->setAchievementAchieved($achievement['id'], self::$character['id']); + } + } + } + + } + +?> diff --git a/app/exceptions/FileUploadException.inc b/app/exceptions/FileUploadException.inc new file mode 100644 index 00000000..3fb62e6f --- /dev/null +++ b/app/exceptions/FileUploadException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File upload went wrong + * + * @author Oliver Hanraths + */ + class FileUploadException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 203; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File upload went wrong'; + + /** + * Nested message + * + * @var string + */ + private $nestedMessage; + + + + + /** + * Construct a new exception. + */ + function __construct($nestedMessage=null, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedMessage + ); + + // Store values + $this->nestedMessage = $nestedMessage; + } + + + + + /** + * Get nested message. + * + * @return Nested message + */ + public function getNestedMessage() + { + return $this->nestedMessage; + } + + } + +?> diff --git a/app/exceptions/MaxFilesizeException.inc b/app/exceptions/MaxFilesizeException.inc new file mode 100644 index 00000000..f16f335e --- /dev/null +++ b/app/exceptions/MaxFilesizeException.inc @@ -0,0 +1,51 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\exceptions; + + + /** + * Exception: File exceeds size maximum. + * + * @author Oliver Hanraths + */ + class MaxFilesizeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 202; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File exceeds size maximum'; + + + + + /** + * Construct a new exception. + */ + function __construct($message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code + ); + } + + } + +?> 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/exceptions/SubmissionNotValidException.inc b/app/exceptions/SubmissionNotValidException.inc new file mode 100644 index 00000000..e2923bdf --- /dev/null +++ b/app/exceptions/SubmissionNotValidException.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: Character submission not valid. + * + * @author Oliver Hanraths + */ + class SubmissionNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 200; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'Character submission not valid'; + + /** + * Nested exception + * + * @var Exception + */ + private $nestedException; + + + + + /** + * Construct a new exception. + * + * @param string $nestedException Nested exception + */ + function __construct($nestedException, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $nestedException + ); + + // Store value + $this->nestedException = $nestedException; + } + + + + + /** + * Get Nested exception. + * + * @return string Nested exception + */ + public function getNestedException() + { + return $this->nestedException; + } + + } + +?> diff --git a/app/exceptions/WrongFiletypeException.inc b/app/exceptions/WrongFiletypeException.inc new file mode 100644 index 00000000..131356b8 --- /dev/null +++ b/app/exceptions/WrongFiletypeException.inc @@ -0,0 +1,75 @@ + + * @copyright 2014 Heinrich-Heine-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: File has wrong filetype. + * + * @author Oliver Hanraths + */ + class WrongFiletypeException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 201; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'File has wrong type “%s”'; + + /** + * Type of file + * + * @var string + */ + private $type; + + + + + /** + * Construct a new exception. + */ + function __construct($type, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $type + ); + + // Store values + $this->type = $type; + } + + + + + /** + * Get type of file. + * + * @return Type of file + */ + public function getType() + { + return $this->type; + } + + } + +?> 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..f7ed6931 --- /dev/null +++ b/configs/AppConfig.inc @@ -0,0 +1,199 @@ + + * @copyright 2014 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', + 'mailsender' => 'noreply@zyren.inf-d.de' + ); + + + /** + * 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', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads' + ); + + + /** + * Media sizes + * + * @static + * @var array + */ + public static $media = array( + 'questgroup' => array( + 'width' => 480, + 'height' => 5000 + ), + 'avatar' => array( + 'width' => 500, + 'height' => 500 + ) + ); + + + /** + * Miscellaneous settings + * + * @static + * @var array + */ + public static $misc = array( + 'ranking_range' => 2, + 'achievements_range' => 3 + ); + + + /** + * Validation settings for user input + * + * @static + * @var array + */ + public static $validation = array( + 'username' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ), + 'email' => array( + 'regex' => '/^\S+@[\w\d.-]{2,}\.[\w]{2,6}$/iU' + ), + 'prename' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'surname' => array( + 'minlength' => 2, + 'maxlength' => 128, + 'regex' => '/^\S*$/' + ), + 'password' => array( + 'minlength' => 5, + 'maxlength' => 64 + ), + 'charactername' => array( + 'minlength' => 5, + 'maxlength' => 32, + 'regex' => '/^\w*$/' + ) + ); + + + /** + * 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|register|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|register))', '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), + array('uploads/(.*)', 'uploads/$1?layout=binary', false), + array('uploads/(?!(index))', 'uploads/index/$1', true) + ); + + + /** + * 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('charactergroups/index/(.*)', 'charactergroups/$1', true), + array('charactergroupsquests/quest/(.*)', 'charactergroupsquests/$1', true), + array('uploads/index/(.*)', 'uploads/$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/AchievementsController.inc b/controllers/AchievementsController.inc new file mode 100644 index 00000000..d99671b6 --- /dev/null +++ b/controllers/AchievementsController.inc @@ -0,0 +1,172 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list Achievements. + * + * @author Oliver Hanraths + */ + class AchievementsController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('achievements', 'seminaries', 'media', 'characters'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Achievements of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get seldom Achievements + $seldomAchievements = $this->Achievements->getSeldomAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range']); + foreach($seldomAchievements as &$achievement) { + $achievement['achieved'] = $this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id']); + } + + // Get Characters with the most Achievements + $successfulCharacters = $this->Characters->getCharactersWithMostAchievements($seminary['id'], \nre\configs\AppConfig::$misc['achievements_range']); + + // Get achieved Achievements + $achievedAchievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get unachieved Achievements + $unachievedAchievements = $this->Achievements->getUnachhievedAchievementsForCharacter($seminary['id'], $character['id'], true); + foreach($unachievedAchievements as &$achievement) + { + // Get Character progress + if($achievement['progress']) + { + $conditions = array(); + switch($achievement['condition']) + { + // Character conditions + case 'character': + $conditionsCharacter = $this->Achievements->getAchievementConditionsCharacter($achievement['id']); + foreach($conditionsCharacter as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionCharacterProgress', + 'params' => array( + $condition['field'], + $condition['value'], + $character['id'] + ) + ); + } + break; + // Quest conditions + case 'quest': + $conditionsQuest = $this->Achievements->getAchievementConditionsQuest($achievement['id']); + foreach($conditionsQuest as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionQuestProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['status'], + $condition['groupby'], + $condition['quest_id'], + $character['id'] + ) + ); + } + break; + // Achievement conditions + case 'achievement': + $conditionsAchievement = $this->Achievements->getAchievementConditionsAchievement($achievement['id']); + foreach($conditionsAchievement as &$condition) + { + $conditions[] = array( + 'func' => 'getAchievementConditionAchievementProgress', + 'params' => array( + $condition['field'], + $condition['count'], + $condition['value'], + $condition['groupby'], + $condition['meta_achievement_id'], + $character['id'] + ) + ); + } + break; + } + + $characterProgresses = array(); + foreach($conditions as &$condition) + { + // Calculate progress of condition + $characterProgresses[] = call_user_func_array( + array( + $this->Achievements, + $condition['func'] + ), + $condition['params'] + ); + } + + $achievement['characterProgress'] = array_sum($characterProgresses) / count($characterProgresses); + } + } + + // Get ranking + $character['rank'] = $this->Achievements->getCountRank($seminary['id'], count($achievedAchievements)); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('seldomAchievements', $seldomAchievements); + $this->set('successfulCharacters', $successfulCharacters); + $this->set('achievedAchievements', $achievedAchievements); + $this->set('unachievedAchievements', $unachievedAchievements); + } + + } + +?> diff --git a/controllers/BinaryController.inc b/controllers/BinaryController.inc new file mode 100644 index 00000000..0d4396ef --- /dev/null +++ b/controllers/BinaryController.inc @@ -0,0 +1,37 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the BinaryAgent to show binary data. + * + * @author Oliver Hanraths + */ + class BinaryController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + * + * Create binary data. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/CharactergroupsController.inc b/controllers/CharactergroupsController.inc new file mode 100644 index 00000000..d4cf6cee --- /dev/null +++ b/controllers/CharactergroupsController.inc @@ -0,0 +1,150 @@ + + * @copyright 2014 Heinrich-Heine-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', 'avatars', '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: 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); + $group['characters'] = $this->Characters->getCharactersForGroup($group['id']); + $group['rank'] = $this->Charactergroups->getXPRank($groupsgroup['id'], $group['xps']); + + // Get Character avatars + foreach($group['characters'] as &$character) + { + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) { + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + // Get Character groups Quests + $quests = $this->Charactergroupsquests->getQuestsForGroup($group['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('groupsgroup', $groupsgroup); + $this->set('group', $group); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/CharactergroupsquestsController.inc b/controllers/CharactergroupsquestsController.inc new file mode 100644 index 00000000..fb9d18c8 --- /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->getSeminaryMediaById($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..7046e1a1 --- /dev/null +++ b/controllers/CharactersController.inc @@ -0,0 +1,280 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class CharactersController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'characters', 'users', 'charactergroups', 'charactertypes', 'seminarycharacterfields', 'avatars', 'media', 'questtopics'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user'), + 'register' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator'), + 'character' => array('admin', 'moderator', 'user') + ); + + + + + /** + * 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); + $seminary['achievable_xps'] = $this->Seminaries->getTotalXPs($seminary['id']); + + // Get Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + $character['quest_xps'] = $this->Characters->getQuestXPsOfCharacter($character['id']); + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // 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']); + + // Get Achievements + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + + // Get ranking + $ranking = array( + 'superior' => $this->Characters->getSuperiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']), + 'inferior' => $this->Characters->getInferiorCharacters($seminary['id'], $character['xps'], \nre\configs\AppConfig::$misc['ranking_range']) + ); + + // Get Quest topics + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('characterfields', $characterfields); + $this->set('user', $user); + $this->set('groups', $groups); + $this->set('achievements', $achievements); + $this->set('ranking', $ranking); + $this->set('questtopics', $questtopics); + } + + + /** + * Acton: register. + * + * Register a new character for a Seminary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function register($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Check for already existing Character + try { + $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + throw new \nre\exceptions\AccessDeniedException(); + } + catch(\nre\exceptions\IdNotFoundException $e) { + // The should be the case + } + + + // Character types + $types = $this->Charactertypes->getCharacterTypesForSeminary($seminary['id']); + + // Character fields + $fields = $this->Seminarycharacterfields->getFieldsForSeminary($seminary['id']); + + // Register Character + $charactername = ''; + $validation = true; + $fieldsValidation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // Validate Character properties + $validation = $this->Validation->validateParams($this->request->getPostParams(), array('charactername')); + $charactername = $this->request->getPostParam('charactername'); + if($this->Characters->characterNameExists($charactername)) { + $validation = $this->Validation->addValidationResult($validation, 'charactername', 'exist', true); + } + + // Validate type + $typeIndex = null; + foreach($types as $index => &$type) + { + $type['selected'] = ($type['url'] == $this->request->getPostParam('type')); + if($type['selected']) { + $typeIndex = $index; + } + } + if(is_null($typeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($characterType); + } + + // Validate fields + $fieldsValues = $this->request->getPostParam('fields'); + foreach($fields as &$field) + { + if(!array_key_exists($field['url'], $fieldsValues)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + if($field['required']) + { + $fieldValidation = $this->Validation->validate($fieldsValues[$field['url']], array('regex'=>$field['regex'])); + if($fieldValidation !== true) + { + if(!is_array($fieldsValidation)) { + $fieldsValidation = array(); + } + $fieldsValidation[$field['url']] = $fieldValidation; + } + } + } + + // Register + if($validation === true && $fieldsValidation === true) + { + $characterId = $this->Characters->createCharacter($this->Auth->getUserId(), $types[$typeIndex]['id'], $charactername); + + // Add Seminary fields + foreach($fields as &$field) { + if(!empty($fieldsValues[$field['url']])) { + $this->Characters->setSeminaryFieldOfCharacter($characterId, $field['id'], $fieldsValues[$field['url']]); + } + } + + // Send mail + $this->sendRegistrationMail($charactername); + + // Redirect + $this->redirect($this->linker->link(array('seminaries'))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('types', $types); + $this->set('fields', $fields); + $this->set('charactername', $charactername); + $this->set('validation', $validation); + $this->set('fieldsValidation', $fieldsValidation); + } + + + + + /** + * Send mail for new Character registration. + * + * @param string $charactername Name of newly registered Character + */ + private function sendRegistrationMail($charactername) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new Character registration: %s', $charactername); + $message = sprintf('User “%s” <%s> has registered a new Character “%s” for the Seminary “%s”', self::$user['username'], self::$user['email'], $charactername, self::$seminary['title']); + $moderators = $this->Users->getUsersWithSeminaryRole(self::$seminary['id'], 'moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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..dd4bc86c --- /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\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 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', IntermediateController::$user); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + $this->set('loggedCharacter', SeminaryRoleController::$character); + } + + } + +?> diff --git a/controllers/IntroductionController.inc b/controllers/IntroductionController.inc new file mode 100644 index 00000000..c1a07f41 --- /dev/null +++ b/controllers/IntroductionController.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 show an introduction page. + * + * @author Oliver Hanraths + */ + class IntroductionController extends \hhu\z\controllers\IntermediateController + { + + + + + /** + * Action: index. + */ + public function index() + { + // Pass data to view + $this->set('userId', $this->Auth->getUserId()); + } + + } + +?> diff --git a/controllers/LibraryController.inc b/controllers/LibraryController.inc new file mode 100644 index 00000000..b6c77079 --- /dev/null +++ b/controllers/LibraryController.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\controllers; + + + /** + * Controller of the LibraryAgent to list Quest topics. + * + * @author Oliver Hanraths + */ + class LibraryController extends \hhu\z\controllers\SeminaryRoleController + { + /** + * Required models + * + * @var array + */ + public $models = array('questtopics', 'seminaries', 'quests', 'questgroups'); + /** + * 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') + ); + + + + + /** + * Action: index. + * + * List Questtopics of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Quest topics + $totalQuestcount = 0; + $totalCharacterQuestcount = 0; + $questtopics = $this->Questtopics->getQuesttopicsForSeminary($seminary['id']); + foreach($questtopics as &$questtopic) + { + // Get Quest count + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $totalQuestcount += $questtopic['questcount']; + + // Get Character progress + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + $totalCharacterQuestcount += $questtopic['characterQuestcount']; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('totalQuestcount', $totalQuestcount); + $this->set('totalCharacterQuestcount', $totalCharacterQuestcount); + $this->set('questtopics', $questtopics); + } + + + /** + * Action: topic. + * + * Show a Questtopic and its Quests with Questsubtopics. + * + * @throws IdNotFoundException + * @param string $eminaryUrl URL-Title of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + */ + public function topic($seminaryUrl, $questtopicUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Questtopic + $questtopic = $this->Questtopics->getQuesttopicByUrl($seminary['id'], $questtopicUrl); + $questtopic['questcount'] = $this->Questtopics->getQuestCountForQuesttopic($questtopic['id']); + $questtopic['characterQuestcount'] = $this->Questtopics->getCharacterQuestCountForQuesttopic($questtopic['id'], $character['id']); + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForQuesttopic($questtopic['id']) as $quest) + { + if($this->Quests->hasCharacterEnteredQuest($quest['id'], $character['id'])) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + + // Get Subtopics + $quest['subtopics'] = $this->Questtopics->getQuestsubtopicsForQuest($quest['id']); + + $quests[] = $quest; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questtopic', $questtopic); + $this->set('quests', $quests); + } + + } + +?> diff --git a/controllers/MediaController.inc b/controllers/MediaController.inc new file mode 100644 index 00000000..e6bb69d2 --- /dev/null +++ b/controllers/MediaController.inc @@ -0,0 +1,432 @@ + + * @copyright 2014 Heinrich-Heine-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', 'guest'), + 'seminaryheader' => array('admin', 'moderator', 'user'), + 'seminary' => array('admin', 'moderator', 'user'), + 'avatar' => array('admin', 'moderator', 'user'), + 'achievement' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'seminary' => array('admin', 'moderator', 'user', 'guest'), + 'achievement' => array('admin', 'moderator', 'user', 'guest') + ); + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'achievements', 'media', 'avatars'); + + + + + /** + * 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. + * + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function index($mediaUrl, $action=null) + { + // Get Media + $media = $this->Media->getMediaByUrl($mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($media)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminaryheader + * + * Display the header of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $action Action for processing the media + */ + public function seminaryheader($seminaryUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get media + $media = $this->Media->getSeminaryMediaById($seminary['seminarymedia_id']); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: seminary. + * + * Display a Seminary medium. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $mediaUrl URL-name of the medium + * @param string $action Action for processing the media + */ + public function seminary($seminaryUrl, $mediaUrl, $action=null) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Media + $media = $this->Media->getSeminaryMediaByUrl($seminary['id'], $mediaUrl); + + // Get file + $file = $this->getMediaFile($media, $action); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: avatar. + * + * Display an Avatar as full size or portrait. + * + * @throws ParamsNotValidException + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $charactertypeUrl URL-title of Character type + * @param int $xplevel XP-level + * @param string $action Size to show (avatar or portrait) + */ + public function avatar($seminaryUrl, $charactertypeUrl, $xplevel, $action='avatar') + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Avatar + $avatar = $this->Avatars->getAvatarByTypeAndLevel($seminary['id'], $charactertypeUrl, $xplevel); + + // Get media + switch($action) + { + case null: + case 'avatar': + $media = $this->Media->getSeminaryMediaById($avatar['avatarpicture_id']); + $file = $this->getMediaFile($media, 'avatar'); + break; + case 'portrait': + $media = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + $file = $this->getMediaFile($media); + break; + default: + throw new \nre\exceptions\ParamsNotValidException($action); + break; + } + + // Get file + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + /** + * Action: achievement + * + * Display the achievement of a Seminary. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of the Seminary + * @param string $achievementUrl URL-title of the Achievement + * @param string $action Action for processing the media + */ + public function achievement($seminaryUrl, $achievementUrl) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Character + $character = SeminaryRoleController::$character; + + // Get Achievement + $achievement = $this->Achievements->getAchievementByUrl($seminary['id'], $achievementUrl); + + // Get media + $index = ''; + if(is_null($character) || !$this->Achievements->hasCharacterAchievedAchievement($achievement['id'], $character['id'])) { + $index = 'unachieved_achievementsmedia_id'; + } + else { + $index = 'achieved_achievementsmedia_id'; + } + if(is_null($achievement[$index])) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + $media = $this->Media->getSeminaryMediaById($achievement[$index]); + + // Get file + $file = $this->getMediaFile($media, null); + if(is_null($file)) { + return; + } + + + // Pass data to view + $this->set('media', $media); + $this->set('file', $file); + } + + + + + /** + * 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; + } + + + /** + * Determine the file for a medium and process it if necessary. + * + * @throws IdNotFoundException + * @throws ParamsNotValidException + * @param array $media Medium to get file for + * @param string $action Action for processing the media + * @return object File for the medium (or null if medium is cached) + */ + private function getMediaFile($media, $action=null) + { + // Get format + $format = explode('/', $media['mimetype']); + $format = $format[1]; + + // Set content-type + $this->response->addHeader("Content-type: ".$media['mimetype'].""); + + // Set filename + $media['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminarymedia'].DS.$media['id']; + if(!file_exists($media['filename'])) { + throw new \nre\exceptions\IdNotFoundException($mediaUrl); + } + + // Cache + if($this->setCacheHeaders($media['filename'])) { + return null; + } + + // 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), self::getImageTypes())) { + $file = file_get_contents($media['filename']); + } + else + { + $file = self::resizeImage( + $media['filename'], + $format, + \nre\configs\AppConfig::$media['questgroup']['width'], + \nre\configs\AppConfig::$media['questgroup']['height'] + ); + } + break; + case 'avatar': + $file = self::resizeImage( + $media['filename'], + $format, + \nre\configs\AppConfig::$media['avatar']['width'], + \nre\configs\AppConfig::$media['avatar']['height'] + ); + break; + default: + throw new ParamsNotValidException($action); + break; + } + + + // Return file + return $file; + } + + + /** + * Get supported image types. + * + * @return array List of supported image types + */ + private static function getImageTypes() + { + $im = new \Imagick(); + + + return $im->queryFormats(); + } + + + /** + * Resize an image. + * + * @param string $fileName Absolute pathname of image to resize + * @param string $mimeType Mimetype of target image + * @param int $width Max. width to resize to + * @param int $height Max. height to resize to + * @return mixed Resized image + */ + private static function resizeImage($fileName, $mimeType, $width, $height) + { + // Read image from cache + $tempFileName = ROOT.DS.\nre\configs\AppConfig::$dirs['temporary'].DS.'media-'.basename($fileName).'-'.$width.'x'.$height; + if(file_exists($tempFileName)) + { + // Check age of file + if(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'] < $width) { + $width = $geometry['width']; + } + if($geometry['height'] < $height) { + $height = $geometry['width']; + } + + // Process + $im->thumbnailImage($width, $height, true); + $im->contrastImage(1); + $im->setImageFormat($mimeType); + + // Save temporary file + $im->writeImage($tempFileName); + + + // Return resized image + return $im; + } + + } + +?> diff --git a/controllers/MenuController.inc b/controllers/MenuController.inc new file mode 100644 index 00000000..b7557c1f --- /dev/null +++ b/controllers/MenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the 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', IntermediateController::$user); + $this->set('loggedCharacter', SeminaryRoleController::$character); + $this->set('loggedSeminary', SeminaryRoleController::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc new file mode 100644 index 00000000..1064be74 --- /dev/null +++ b/controllers/QuestgroupsController.inc @@ -0,0 +1,223 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts', 'media'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'questgroup' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'questgroup' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator') + ); + + + + + /** + * 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 + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permission + if(count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + $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 = null; + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$group) + { + // Check permission of Questgroups + if($i >= 1 && count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get Character XPs + $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + } + } + } + + // Get texts + $questgroupTexts = $this->Questgroups->getQuestgroupTexts($questgroup['id']); + + // Get Character XPs + $questgroup['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + + // Media + $picture = null; + if(!is_null($questgroup['questgroupspicture_id'])) + { + $picture = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + + // Get Quests + $quests = array(); + if(count($childQuestgroupshierarchy) == 0) + { + $currentQuest = null; + do { + // Get next Quest + if(is_null($currentQuest)) { + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + } + else { + $nextQuests = $this->Quests->getNextQuests($currentQuest['id']); + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id'])) { + $currentQuest = $nextQuest; + break; + } + } + } + + // Add additional data + if(!is_null($currentQuest)) + { + // Set status + $currentQuest['solved'] = $this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $character['id']); + + // Attach related Questgroups + $currentQuest['relatedQuestgroups'] = array(); + $relatedQuestgroups = $this->Questgroups->getRelatedQuestsgroupsOfQuest($currentQuest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $firstQuest = $this->Quests->getFirstQuestOfQuestgroup($relatedQuestgroup['id']); + if(!empty($firstQuest) && $this->Quests->hasCharacterEnteredQuest($firstQuest['id'], $character['id'])) { + $currentQuest['relatedQuestgroups'][] = $relatedQuestgroup; + } + } + + // Add Quest to Quests + $quests[] = $currentQuest; + } + } + while(!is_null($currentQuest) && ($currentQuest['solved'] || count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) > 0)); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('childquestgroupshierarchy', $childQuestgroupshierarchy); + $this->set('texts', $questgroupTexts); + $this->set('picture', $picture); + $this->set('quests', $quests); + } + + + /** + * Action: create. + * + * Create a new Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function create($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Create Questgroup + $validation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // TODO Validation + $title = $this->request->getPostParam('title'); + + // Create new Questgroup + if($validation === true) + { + $questgroupId = $this->Questgroups->createQuestgroup( + $this->Auth->getUserId(), + $seminary['id'], + $title + ); + + // Redirect + $this->redirect($this->linker->link(array('seminaries', 'seminary', $seminary['url']))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + } + + } + +?> diff --git a/controllers/QuestgroupshierarchypathController.inc b/controllers/QuestgroupshierarchypathController.inc new file mode 100644 index 00000000..fabf2bbb --- /dev/null +++ b/controllers/QuestgroupshierarchypathController.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\controllers; + + + /** + * Controller of QuestgroupshierarchypathAgent to display the + * Questgroups hierarchy path. + * + * @author Oliver Hanraths + */ + class QuestgroupshierarchypathController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('seminaries', 'questgroups', 'questgroupshierarchy', 'quests', 'questtexts'); + + + + + /** + * Action: index. + * + * Calculate and show the hierarchy path of a Questgroup. + * + * @param string $seminaryUrl URL-Title of a Seminary + * @param string $questgroupUrl URL-Title of a Questgroup + * @param boolean $showGroup Show the current group itself + */ + public function index($seminaryUrl, $questgroupUrl, $showGroup=false) + { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Get parent Questgrouphierarchy + $currentQuestgroup = $questgroup; + $parentQuestgroupshierarchy = array(); + if($showGroup) { + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + if(is_null($questgroup['hierarchy'])) + { + // Get related Questgroup + $questtext = $this->Questtexts->getRelatedQuesttextForQuestgroup($currentQuestgroup['id']); + $quest = $this->Quests->getQuestById($questtext['quest_id']); + $currentQuestgroup = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + $quest['questgroup'] = $currentQuestgroup; + + // Use Hierarchy name for optional Questgroup + if(!empty($parentQuestgroupshierarchy)) { + $parentQuestgroupshierarchy[0]['hierarchy'] = $currentQuestgroup['hierarchy']; + unset($parentQuestgroupshierarchy[0]['hierarchy']['questgroup_pos']); + } + + array_unshift($parentQuestgroupshierarchy, $quest); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + while(!empty($currentQuestgroup['hierarchy']) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) + { + $currentQuestgroup = $this->Questgroups->GetQuestgroupById($currentQuestgroup['hierarchy']['parent_questgroup_id']); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + array_unshift($parentQuestgroupshierarchy, $currentQuestgroup); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('parentquestgroupshierarchy', $parentQuestgroupshierarchy); + } + + } + +?> diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc new file mode 100644 index 00000000..6da2906d --- /dev/null +++ b/controllers/QuestsController.inc @@ -0,0 +1,814 @@ + + * @copyright 2014 Heinrich-Heine-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( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator', 'user'), + 'submission' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator', 'user') + ); + /** + * User seminary permissions + * + * @var array + */ + public $seminaryPermissions = array( + 'index' => array('admin', 'moderator', 'user'), + 'quest' => array('admin', 'moderator', 'user'), + 'submissions' => array('admin', 'moderator'), + 'submission' => array('admin', 'moderator'), + 'create' => array('admin', 'moderator') + ); + + + + + /** + * Action: index. + * + * List all Quests for a Seminary. + * + * @param string $seminaryUrl URL-Title of Seminary + */ + public function index($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Prepare filters + $filters = array( + 'questgroups' => array(), + 'questtypes' => array() + ); + + // Get selected filters + $selectedFilters = array( + 'questgroup' => "0", + 'questtype' => "" + ); + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('filters'))) { + $selectedFilters = $this->request->getPostParam('filters'); + } + + // Get Quests + $quests = array(); + foreach($this->Quests->getQuestsForSeminary($seminary['id']) as $quest) + { + // Get Questgroup + $quest['questgroup'] = $this->Questgroups->getQuestgroupById($quest['questgroup_id']); + if($selectedFilters['questgroup'] != "0" && $selectedFilters['questgroup'] != $quest['questgroup']['id']) { + continue; + } + + // Get Questtype + $quest['questtype'] = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + if($selectedFilters['questtype'] != "" && $selectedFilters['questtype'] != $quest['questtype']['classname']) { + continue; + } + + // Add filter values + $filters['questgroups'][$quest['questgroup']['id']] = $quest['questgroup']; + $filters['questtypes'][$quest['questtype']['classname']] = $quest['questtype']; + + // Add open submissions count + $quest['opensubmissionscount'] = count($this->Characters->getCharactersSubmittedQuest($quest['id'])); + + $quests[] = $quest; + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('quests', $quests); + $this->set('filters', $filters); + $this->set('selectedFilters', $selectedFilters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Get Character + $character = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + $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 + // One previous Quest has to be solved and no other + // following Quests of ones has to be tried + $solved = false; + $tried = false; + foreach($previousQuests as &$previousQuest) + { + // // Check previous Quest + if($this->Quests->hasCharacterSolvedQuest($previousQuest['id'], $character['id'])) + { + $solved = true; + + // Check following Quests + $followingQuests = $this->Quests->getNextQuests($previousQuest['id']); + foreach($followingQuests as $followingQuest) + { + // Check following Quest + if($followingQuest['id'] != $quest['id'] && $this->Quests->hasCharacterTriedQuest($followingQuest['id'], $character['id'])) + { + $tried = true; + break; + } + } + + break; + } + } + if(!$solved || $tried) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + + // Set status “entered” + $this->Quests->setQuestEntered($quest['id'], $character['id']); + + // Get (related) Questtext + $relatedQuesttext = $this->Questtexts->getRelatedQuesttextForQuestgroup($questgroup['id']); + if(!empty($relatedQuesttext)) { + $relatedQuesttext['quest'] = $this->Quests->getQuestById($relatedQuesttext['quest_id']); + if(!empty($relatedQuesttext['quest'])) { + $relatedQuesttext['quest']['questgroup_url'] = $this->Questgroups->getQuestgroupById($relatedQuesttext['quest']['questgroup_id'])['url']; + } + } + + // Get Questtexts + if(is_null($questtexttypeUrl)) { + $questtexttypeUrl = 'Prolog'; + } + $questtexttypes = $this->Questtexts->getQuesttexttypes(); + if(($questtexttypeIndex = array_search($questtexttypeUrl, array_map(function($t) { return $t['url']; }, $questtexttypes))) === false) { + throw new ParamsNotValidException($questtexttypeUrl); + } + $questtexttype = $questtexttypes[$questtexttypeIndex]; + $questtexts = $this->Questtexts->getQuesttextsOfQuest($quest['id'], $questtexttypeUrl); + foreach($questtexts as &$questtext) + { + // Questtext media + if(!is_null($questtext['questsmedia_id'])) { + $questtext['media'] = $this->Media->getSeminaryMediaById($questtext['questsmedia_id']); + } + + // Related Questgroups + $questtext['relatedQuestsgroups'] = $this->Questgroups->getRelatedQuestsgroupsOfQuesttext($questtext['id']); + } + + // Has Epilog? + $hasEpilog = ($this->Questtexts->getQuesttextCountOfQuest($quest['id'], 'Epilog') > 0); + + // Quest status + $questStatus = $this->request->getGetParam('status'); + + // Quest media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Task + $task = null; + if($questtexttypeUrl == 'Prolog') + { + // Questtype + $questtype = $this->Questtypes->getQuesttypeById($quest['questtype_id']); + + // Render task + if(!is_null($questtype['classname'])) { + $task = $this->renderTask($questtype['classname'], $seminary, $questgroup, $quest, $character); + } + else { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + } + + // Has Character solved quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Next Quest/Questgroup + $nextQuests = null; + $charactedHasChoosenNextQuest = false; + $nextQuestgroup = null; + if($questtexttypeUrl == 'Epilog' || (is_null($questtype['classname']) && !$hasEpilog)) + { + // Next Quest + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + // Set entered status of Quest + $nextQuest['entered'] = $this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $character['id']); + if($nextQuest['entered']) { + $charactedHasChoosenNextQuest = true; + } + } + + // Next Questgroup + if(empty($nextQuests)) + { + if(is_null($relatedQuesttext)) + { + $nextQuestgroup = $this->Questgroups->getNextQuestgroup($questgroup['id']); + $nextQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($nextQuestgroup['id']); + } + else + { + // Related (Main-) Quest + $nextQuest = $relatedQuesttext['quest']; + $nextQuest['entered'] = true; + $nextQuests = array($nextQuest); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('questtexttype', $questtexttype); + $this->set('questtexts', $questtexts); + $this->set('hasEpilog', $hasEpilog); + $this->set('quest', $quest); + $this->set('queststatus', $questStatus); + $this->set('relatedquesttext', $relatedQuesttext); + $this->set('nextquests', $nextQuests); + $this->set('charactedHasChoosenNextQuest', $charactedHasChoosenNextQuest); + $this->set('nextquestgroup', $nextQuestgroup); + $this->set('task', $task); + $this->set('media', $questmedia); + $this->set('solved', $solved); + } + + + /** + * List Character submissions for a Quest. + * + * @throws IdNotFoundException + * @param string $seminaryUrl URL-Title of Seminary + * @param string $questgroupUrl URL-Title of Questgroup + * @param string $questUrl URL-Title of Quest + */ + public function submissions($seminaryUrl, $questgroupUrl, $questUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Get Questgroup + $questgroup = $this->Questgroups->getQuestgroupByUrl($seminary['id'], $questgroupUrl); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); + } + + // Get submitted Character submissions + $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); + + // Get unsolved Character submissions + $unsolvedSubmissionCharacters = $this->Characters->getCharactersUnsolvedQuest($quest['id']); + + // Get solved Character submissions + $solvedSubmissionCharacters = $this->Characters->getCharactersSolvedQuest($quest['id']); + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroup', $questgroup); + $this->set('quest', $quest); + $this->set('media', $questmedia); + $this->set('submittedSubmissionCharacters', $submittedSubmissionCharacters); + $this->set('unsolvedSubmissionCharacters', $unsolvedSubmissionCharacters); + $this->set('solvedSubmissionCharacters', $solvedSubmissionCharacters); + } + + + /** + * 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); + $questgroup['picture'] = null; + if(!is_null($questgroup['questgroupspicture_id'])) { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($questgroup['questgroupspicture_id']); + } + + // Get Quest + $quest = $this->Quests->getQuestByUrl($seminary['id'], $questgroup['id'], $questUrl); + + // Character + $character = $this->Characters->getCharacterByUrl($seminary['id'], $characterUrl); + + // Media + $questmedia = null; + if(!is_null($quest['questsmedia_id'])) { + $questmedia = $this->Media->getSeminaryMediaById($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); + } + + + /** + * Action: create. + * + * Create a new Quest. + * + * @param string $seminaryUrl URL-Title of a Seminary + */ + public function create($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Quest groups + $questgroups = $this->Questgroups->getQuestgroupsForSeminary($seminary['id']); + + // Quest types + $questtypes = $this->Questtypes->getQuesttypes(); + + // Create Quest + $validation = true; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('create'))) + { + // TODO Validation + $name = $this->request->getPostParam('name'); + $xps = $this->request->getPostParam('xps'); + $prolog = $this->request->getPostParam('prolog'); + $entrytext = $this->request->getPostParam('entrytext'); + $wrongtext = $this->request->getPostParam('wrongtext'); + $task = $this->request->getPostParam('task'); + + // Validate Questgroup + $questgroupIndex = null; + foreach($questgroups as $index => &$questgroup) + { + $questgroup['selected'] = ($questgroup['url'] == $this->request->getPostParam('questgroup')); + if($questgroup['selected']) { + $questgroupIndex = $index; + } + } + if(is_null($questgroupIndex)) { + throw new \nre\exceptions\ParamsNotValidException($questgroup); + } + + // Validate Questtype + $questtypeIndex = null; + foreach($questtypes as $index => &$questtype) + { + $questtype['selected'] = ($questtype['url'] == $this->request->getPostParam('questtype')); + if($questtype['selected']) { + $questtypeIndex = $index; + } + } + if(is_null($questtypeIndex)) { + throw new \nre\exceptions\ParamsNotValidException($questtype); + } + + // Questmedia + $questmedia = null; + if(array_key_exists('questmedia', $_FILES) && !empty($_FILES['questmedia']) && $_FILES['questmedia']['error'] == 0) { + $questmedia = $_FILES['questmedia']; + } + + // Process Prolog + if(!empty($prolog)) { + $prolog = preg_split('/\s*(_|=){5,}\s*/', $prolog, -1, PREG_SPLIT_NO_EMPTY); + } + + // Create new Quest + if($validation === true) + { + $questId = $this->Quests->createQuest( + $this->Auth->getUserId(), + $name, + $questgroups[$questgroupIndex]['id'], + $questtypes[$questtypeIndex]['id'], + $xps, + $entrytext, + $wrongtext, + $task + ); + + // Create Questmedia + if(!is_null($questmedia)) + { + $questsmediaId = $this->Media->createQuestMedia( + $this->Auth->getUserId(), + $seminary['id'], + $questmedia['name'], + $name, + $questmedia['type'], + $questmedia['tmp_name'] + ); + if($questsmediaId > 0) { + $this->Quests->setQuestmedia($questId, $questsmediaId); + } + } + + // Add Prolog-texts + if(!empty($prolog)) { + $this->Questtexts->addQuesttextsToQuest($this->Auth->getUserId(), $questId, 'Prolog', $prolog); + } + + + // Redirect + $this->redirect($this->linker->link(array('quests', 'index', $seminary['url']))); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('questgroups', $questgroups); + $this->set('questtypes', $questtypes); + } + + + /** + * Action: createmedia. + * TODO only temporary for easier data import. + * + * Display a form for creating new Seminary media. + * + * @param string $seminaryUrl URL-title of Seminary + */ + public function createmedia($seminaryUrl) + { + // Get seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + + // Create media + $mediaId = null; + $error = null; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + $file = $_FILES['file']; + $error = $file['error']; + if(empty($error)) + { + $mediaId = $this->Media->createQuestMedia( + $this->Auth->getUserId(), + $seminary['id'], + $file['name'], + $this->request->getPostParam('description'), + $file['type'], + $file['tmp_name'] + ); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('mediaid', $mediaId); + } + + + + + /** + * Render and handle the task of a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTask($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('quest', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Get user answers + $answers = $this->request->getPostParam('answers'); + + // Save answers in database + try { + if(!$this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id'])) { + $questtypeAgent->saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + } + + // Match answers with correct ones + $status = $questtypeAgent->matchAnswersofCharacter($seminary, $questgroup, $quest, $character, $answers); + if($status === true) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + + // 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'))); + } + else { + // Mark Quest as submitted + $this->Quests->setQuestSubmitted($quest['id'], $character['id']); + + // Redirect + $this->redirect($this->linker->link('Prolog', 5, true)); + } + } + catch(\hhu\z\exceptions\SubmissionNotValidException $e) { + $response->addParam($e); + } + } + + // Render Task + $task = $this->runQuesttypeAgent($questtypeAgent, $request, $response); + } + catch(\nre\exceptions\ViewNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\nre\exceptions\ActionNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeModelNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeControllerNotFoundException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotValidException $e) { + $task = $e->getMessage(); + } + catch(\hhu\z\exceptions\QuesttypeAgentNotFoundException $e) { + $task = $e->getMessage(); + } + + + // Return rendered output + return $task; + } + + + /** + * Render and handle a Character submission for a Quest. + * + * @param string $questtypeClassname Name of the class for the Questtype of a Quest + * @param array $seminary Seminary data + * @param array $questgroup Questgroup data + * @param array $quest Quest data + * @param array $character Character data + * @return string Rendered output + */ + private function renderTaskSubmission($questtypeClassname, $seminary, $questgroup, $quest, $character) + { + $task = null; + try { + // Generate request and response + $request = clone $this->request; + $response = $this->createQuesttypeResponse('submission', $seminary, $questgroup, $quest, $character); + + // Load Questtype Agent + $questtypeAgent = $this->loadQuesttypeAgent($questtypeClassname, $request, $response); + + // Solve Quest + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit'))) + { + // Set status + if($this->request->getPostParam('submit') == _('solved')) + { + // Mark Quest as solved + $this->Quests->setQuestSolved($quest['id'], $character['id']); + } + else + { + // Mark Quest as unsolved + $this->Quests->setQuestUnsolved($quest['id'], $character['id']); + } + + // 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..06f95652 --- /dev/null +++ b/controllers/SeminariesController.inc @@ -0,0 +1,252 @@ + + * @copyright 2014 Heinrich-Heine-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', 'userseminaryroles', '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) + { + $seminary['description'] = \hhu\z\Utils::shortenString($seminary['description'], 100, 120).' …'; + + // Created user + $seminary['creator'] = $this->Users->getUserById($seminary['created_user_id']); + + // Character of currently logged-in user + try { + $seminary['usercharacter'] = $this->Characters->getCharacterForUserAndSeminary($this->Auth->getUserId(), $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + $seminary['userroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(IntermediateController::$user['id'], $seminary['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->getHierarchyOfSeminary($seminary['id']); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $hierarchy['questgroups'] = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + + // Get additional data + foreach($hierarchy['questgroups'] as $i => &$questgroup) + { + // Check permission of Questgroups + if($i >= 1 && count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) + { + if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + { + $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); + break; + } + } + + // Get first Questgroup text + $text = $this->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']); + + // Get Media + $questgroup['picture'] = null; + try { + $questgroup['picture'] = $this->Media->getSeminaryMediaById($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 + $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/SeminarybarController.inc b/controllers/SeminarybarController.inc new file mode 100644 index 00000000..88c1037e --- /dev/null +++ b/controllers/SeminarybarController.inc @@ -0,0 +1,86 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a sidebar with Seminary related + * information. + * + * @author Oliver Hanraths + */ + class SeminarybarController extends \hhu\z\Controller + { + /** + * Required models + * + * @var array + */ + public $models = array('characters', 'quests', 'questgroups', 'achievements', 'charactergroups', 'avatars', 'media'); + + + + + /** + * Action: index. + */ + public function index() + { + if(is_null(SeminaryRoleController::$seminary)) { + return; + } + + // Get Seminary + $seminary = SeminaryRoleController::$seminary; + + // Get Character + $character = SeminaryRoleController::$character; + if(is_null($character)) { + return; + } + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + $character['rank'] = $this->Characters->getXPRank($seminary['id'], $character['xps']); + + // Get “last” Quest + $lastQuest = $this->Quests->getLastQuestForCharacter($character['id']); + if(!is_null($lastQuest)) { + $lastQuest['questgroup'] = $this->Questgroups->getQuestgroupById($lastQuest['questgroup_id']); + } + + // Get last achieved Achievement + $achievements = $this->Achievements->getAchievedAchievementsForCharacter($character['id']); + $lastAchievement = array_shift($achievements); + + // Get Character group members + $characterGroups = array(); + foreach($this->Charactergroups->getGroupsForCharacter($character['id']) as $group) + { + $groupsgroup = $this->Charactergroups->getGroupsgroupById($group['charactergroupsgroup_id']); + if($groupsgroup['preferred']) + { + $group['members'] = $this->Characters->getCharactersForGroup($group['id']); + $characterGroups[] = $group; + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('lastQuest', $lastQuest); + $this->set('lastAchievement', $lastAchievement); + $this->set('characterGroups', $characterGroups); + } + + } + +?> diff --git a/controllers/SeminarymenuController.inc b/controllers/SeminarymenuController.inc new file mode 100644 index 00000000..8f2957d7 --- /dev/null +++ b/controllers/SeminarymenuController.inc @@ -0,0 +1,52 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to display a menu with Seminary related + * links. + * + * @author Oliver Hanraths + */ + class SeminarymenuController extends \hhu\z\controllers\SeminaryRoleController + { + + + + + /** + * 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', self::$user); + $this->set('loggedSeminary', self::$seminary); + } + + + /** + * Action: index. + */ + public function index() + { + } + + } + +?> diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc new file mode 100644 index 00000000..428f980f --- /dev/null +++ b/controllers/UploadsController.inc @@ -0,0 +1,174 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the UploadsAgent to process and show user uploads. + * + * @author Oliver Hanraths + */ + class UploadsController extends \hhu\z\controllers\IntermediateController + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads', 'users', 'userroles'); + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator', 'user', 'userseminaryroles') + ); + + + + + /** + * 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 an upload. + * + * @throws AccessDeniedException + * @throws IdNotFoundException + * @param string $uploadUrl URL-name of the upload + */ + public function index($uploadUrl) + { + // Get Upload + $upload = $this->Uploads->getUploadByUrl($uploadUrl); + + // Check permissions + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array(); + foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { + $user['roles'][] = $role['name']; + } + if(!$upload['public']) + { + // System roles + if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) + { + // Owner of file + if($upload['created_user_id'] != $user['id']) + { + if(!is_null($upload['seminary_id'])) { + throw new \nre\exceptions\AccessDeniedException(); + } + else + { + // Seminary + $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); + + // Seminary roles + $userSeminaryRoles = array(); + foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { + $userSeminaryRoles[] = $role['name']; + } + + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { + throw new \nre\exceptions\AccessDeniedException(); + } + } + } + } + } + + // Set content-type + $this->response->addHeader("Content-type: ".$upload['mimetype'].""); + + // Set filename + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + if(!file_exists($upload['filename'])) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + // Cache + if($this->setCacheHeaders($upload['filename'])) { + return; + } + + // Load file + $file = file_get_contents($upload['filename']); + + + + + // Pass data to view + $this->set('upload', $upload); + $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; + } + + } + +?> 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..90e50912 --- /dev/null +++ b/controllers/UsersController.inc @@ -0,0 +1,363 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers; + + + /** + * Controller of the Agent to list registered users and their data. + * + * @author Oliver Hanraths + */ + class UsersController extends \hhu\z\controllers\IntermediateController + { + /** + * User permissions + * + * @var array + */ + public $permissions = array( + 'index' => array('admin', 'moderator'), + 'user' => array('admin', 'moderator', 'user'), + 'create' => array('admin', 'moderator'), + 'edit' => array('admin', 'moderator'), + 'delete' => array('admin') + ); + /** + * Required models + * + * @var array + */ + public $models = array('users', 'characters', 'avatars', 'media', 'userseminaryroles'); + /** + * Required components + * + * @var array + */ + public $components = array('validation'); + + + + + /** + * 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 + * @throws AccessDeniedException + * @param string $userUrl URL-Username of an user + */ + public function user($userUrl) + { + // Get user + $user = $this->Users->getUserByUrl($userUrl); + + // Check permissions + if(count(array_intersect(array('admin','moderator'), \hhu\z\controllers\IntermediateController::$user['roles'])) == 0 && $user['id'] != IntermediateController::$user['id']) { + throw new \nre\exceptions\AccessDeniedException(); + } + + // Get Characters + $characters = $this->Characters->getCharactersForUser($user['id']); + + // Additional Character information + foreach($characters as &$character) + { + // Seminary roles + $character['user_seminaryroles'] = $this->Userseminaryroles->getUserseminaryrolesForUserById(\hhu\z\controllers\IntermediateController::$user['id'], $character['seminary_id']); + $character['user_seminaryroles'] = array_map(function($a) { return $a['name']; }, $character['user_seminaryroles']); + + // Level + $character['xplevel'] = $this->Characters->getXPLevelOfCharacters($character['id']); + + // Avatar + $avatar = $this->Avatars->getAvatarById($character['avatar_id']); + if(!is_null($avatar['small_avatarpicture_id'])) + { + //$character['seminary'] = + $character['small_avatar'] = $this->Media->getSeminaryMediaById($avatar['small_avatarpicture_id']); + } + } + + + // 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: register. + * + * Register a new user. + */ + public function register() + { + $username = ''; + $prename = ''; + $surname = ''; + $email = ''; + + $fields = array('username', 'prename', 'surname', 'email', 'password'); + $validation = array(); + + // Register a new user + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('register'))) + { + // Get params and validate them + $validation = $this->Validation->validateParams($this->request->getPostParams(), $fields); + $username = $this->request->getPostParam('username'); + if($this->Users->usernameExists($username)) { + $validation = $this->Validation->addValidationResult($validation, 'username', 'exist', true); + } + $prename = $this->request->getPostParam('prename'); + $surname = $this->request->getPostParam('surname'); + $email = $this->request->getPostParam('email'); + if($this->Users->emailExists($email)) { + $validation = $this->Validation->addValidationResult($validation, 'email', 'exist', true); + } + + + // Register + if($validation === true) + { + $userId = $this->Users->createUser( + $username, + $prename, + $surname, + $email, + $this->request->getPostParam('password') + ); + + // Send mail + $this->sendRegistrationMail($username, $email); + + // Login + $this->Auth->setUserId($userId); + $user = $this->Users->getUserById($userId); + + // Redirect to user page + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + // Get validation settings + $validationSettings = array(); + foreach($fields as &$field) { + $validationSettings[$field] = \nre\configs\AppConfig::$validation[$field]; + } + + + // Pass data to view + $this->set('username', $username); + $this->set('prename', $prename); + $this->set('surname', $surname); + $this->set('email', $email); + $this->set('validation', $validation); + $this->set('validationSettings', $validationSettings); + } + + + /** + * Action: 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('prename'), + $this->request->getPostParam('surname'), + $this->request->getPostParam('email'), + $this->request->getPostParam('password') + ); + + // Redirect to user + $user = $this->Users->getUserById($userId); + $this->redirect($this->linker->link(array($user['url']), 1)); + } + } + + + /** + * 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('prename'), + $this->request->getPostParam('surname'), + $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); + } + + + + + /** + * Send mail for new user registration. + * + * @param string $username Name of newly registered user + * @param string $email E‑mail address of newly registered user + */ + private function sendRegistrationMail($username, $email) + { + $sender = \nre\configs\AppConfig::$app['mailsender']; + if(empty($sender)) { + return; + } + + // Send notification mail to system moderators + $subject = sprintf('new user registration: %s', $username); + $message = sprintf('User “%s” <%s> has registered themself to %s', $username, $email, \nre\configs\AppConfig::$app['name']); + $moderators = $this->Users->getUsersWithRole('moderator'); + foreach($moderators as &$moderator) + { + \hhu\z\Utils::sendMail($sender, $moderator['email'], $subject, $message); + } + } + + } + +?> 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/controllers/components/ValidationComponent.inc b/controllers/components/ValidationComponent.inc new file mode 100644 index 00000000..950d45ed --- /dev/null +++ b/controllers/components/ValidationComponent.inc @@ -0,0 +1,140 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\controllers\components; + + + /** + * Component to validate user input. + * + * @author Oliver Hanraths + */ + class ValidationComponent extends \nre\core\Component + { + /** + * Validation settings + * + * @var array + */ + private $config; + + + + + /** + * Construct a new Validation-component. + */ + public function __construct() + { + // Get validation settings from configuration + $this->config = \nre\configs\AppConfig::$validation; + } + + + + + /** + * Validate an user input. + * + * @param mixed $input User input to validate + * @param array $settings Validation setting + * @return mixed True or the settings the validation fails on + */ + public function validate($input, $settings) + { + $validation = array(); + + // Min string length + if(array_key_exists('minlength', $settings) && strlen($input) < $settings['minlength']) { + $validation['minlength'] = $settings['minlength']; + } + // Max string length + if(array_key_exists('maxlength', $settings) && strlen($input) > $settings['maxlength']) { + $validation['maxlength'] = $settings['maxlength']; + } + + // Regex + if(array_key_exists('regex', $settings) && !preg_match($settings['regex'], $input)) { + $validation['regex'] = $settings['regex']; + } + + + // Return true or the failed fields + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Validate user input parameters. + * + * @param array $params User input parameters + * @param array $indices Names of parameters to validate and to validate against + * @return mixed True or the parameters with settings the validation failed on + */ + public function validateParams($params, $indices) + { + $validation = array(); + foreach($indices as $index) + { + if(!array_key_exists($index, $params)) { + throw new \nre\exceptions\ParamsNotValidException($index); + } + + // Check parameter + if(array_key_exists($index, $this->config)) + { + $param = $params[$index]; + $check = $this->validate($param, $this->config[$index]); + if($check !== true) { + $validation[$index] = $check; + } + } + } + + + // Return true or the failed parameters with failed settings + if(empty($validation)) { + return true; + } + return $validation; + } + + + /** + * Add a custom determined validation result to a validation + * store. + * + * @param mixed $validation Validation store to add result to + * @param string $param Name of parameter of the custom validation result + * @param string $setting Name of setting of the custom validation result + * @param mixed $result Validation result + * @return mixed The altered validation store + */ + public function addValidationResult($validation, $param, $setting, $result) + { + if(!is_array($validation)) { + $validation = array(); + } + if(!array_key_exists($param, $validation)) { + $validation[$param] = array(); + } + $validation[$param][$setting] = $result; + + + return $validation; + } + + } + +?> 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..941e7523 --- /dev/null +++ b/core/Controller.inc @@ -0,0 +1,433 @@ + + * @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); + } + // Components of parent classes + $parent = $this; + while($parent = get_parent_class($parent)) + { + $properties = get_class_vars($parent); + if(array_key_exists('components', $properties)) { + $components = array_merge($components, $properties['components']); + } + } + + // Load components + foreach($components as &$component) + { + // Load class + Component::load($component); + + // Construct component + $componentName = ucfirst(strtolower($component)); + $this->$componentName = Component::factory($component); + } + } + + + /** + * Load the Models of this Controller. + * + * @throws 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..e8c4fa0f --- /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) + { + // Delete critical signs + $specials = array('/', '?', '&'); + foreach($specials as &$special) { + $param = str_replace($special, '', $param); + } + + // Process parameter + $param = str_replace( + ' ', + \nre\configs\CoreConfig::$classes['linker']['url']['delimiter'], + substr( + $param, + 0, + \nre\configs\CoreConfig::$classes['linker']['url']['length'] + ) + ); + + // Encode parameter + $linkParams[] = $param; + } + + + // Return link parameters + return $linkParams; + } + + + + + /** + * Create a web link. + * + * @param array $params Parameters to use + * @param int $offset Ignore first parameters + * @param bool $exclusiveParams Use only the given parameters + * @param array $getParams GET-parameter to use + * @param bool $exclusiveGetParams Use only the given GET-parameters + * @param string $anchor Target anchor + * @param bool $absolute Include hostname etc. for an absolute URL + * @return string Created link + */ + public function link($params=null, $offset=0, $exclusiveParams=true, $getParams=null, $exclusiveGetParams=true, $anchor=null, $absolute=false) + { + // Make current request to response + $response = new \nre\responses\WebResponse(); + + + // Check parameters + if(is_null($params)) { + $params = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + + // Set parameters from request + $reqParams = array_slice($this->request->getParams(), 1, $offset); + if(count($reqParams) < $offset && $offset > 0) { + $reqParams[] = $this->request->getParam(1, 'intermediate'); + } + if(count($reqParams) < $offset && $offset > 1) { + $reqParams[] = $this->request->getParam(2, 'action'); + } + $params = array_map('rawurlencode', $params); + $params = array_merge($reqParams, $params); + + // Use Layout + $layout = \nre\configs\AppConfig::$defaults['toplevel']; + if(!empty($getParams) && array_key_exists('layout', $getParams)) { + $layout = $getParams['layout']; + } + array_unshift($params, $layout); + + // Use parameters from request only + if(!$exclusiveParams) + { + $params = array_merge( + $params, + array_slice( + $this->request->getParams(), + count($params) + ) + ); + } + + // Set parameters + call_user_func_array( + array( + $response, + 'addParams' + ), + $params + ); + + + // Check GET-parameters + if(is_null($getParams)) { + $getParams = array(); + } + elseif(!is_array($params)) { + $params = array($params); + } + if(!$exclusiveGetParams) + { + $getParams = array_merge( + $this->request->getGetParams(), + $getParams + ); + } + + // Set GET-parameters + $response->addGetParams($getParams); + + + // Create and return link + return self::createLink($this->request, $response, $anchor, $absolute); + } + + + /** + * Create a link from an URL. + * + * @param string $url URL to create link from + * @param bool $absolute Create absolute URL + * @return string Created link + */ + public function hardlink($url, $absolute=false) + { + return $this->createUrl($url, $this->request, $absolute); + } + + + + + /** + * Create a link. + * + * @param Request $request Current request + * @param Response $response Current response + * @param bool $absolute Create absolute link + * @param string $anchor Anchor on target + * @return string Created link + */ + private static function createLink(Request $request, Response $response, $anchor=null, $absolute=false) + { + // Check response + if(is_null($response)) { + return null; + } + + + // Get parameters + $params = $response->getParams(1); + + // Check Action + if(count($params) == 2 && $params[1] == \nre\configs\CoreConfig::$defaults['action']) { + array_pop($params); + } + + // Set parameters + $link = implode('/', $params); + + // Apply reverse-routes + $link = $request->applyReverseRoutes($link); + + + // Get GET-parameters + $getParams = $response->getGetParams(); + + // Layout überprüfen + if(array_key_exists('layout', $getParams) && $getParams['layout'] == \nre\configs\AppConfig::$defaults['toplevel']) { + unset($getParams['layout']); + } + + // Set GET-parameters + if(array_key_exists('url', $getParams)) { + unset($getParams['url']); + } + if(count($getParams) > 0) { + $link .= '?'.http_build_query($getParams); + } + + // Add anchor + if(!is_null($anchor)) { + $link .= "#$anchor"; + } + + + // Create URL + $url = self::createUrl($link, $request, $absolute); + + + return $url; + } + + + /** + * Adapt a link to the environment. + * + * @param string $url URL + * @param Request $request Current request + * @param bool $absolute Create absolute URL + * @return string Adapted URL + */ + private static function createUrl($url, Request $request, $absolute=false) + { + // Variables + $server = $_SERVER['SERVER_NAME']; + $uri = $_SERVER['REQUEST_URI']; + $prefix = ''; + + + // Determine prefix + $ppos = array(strlen($uri)); + if(($p = strpos($uri, '/'.$request->getParam(1, 'intermediate'))) !== false) { + $ppos[] = $p; + } + $prefix = substr($uri, 0, min($ppos)); + + // Create absolute URL + if($absolute) { + $prefix = "http://$server/".trim($prefix, '/'); + } + + // Put URL together + $url = rtrim($prefix, '/').'/'.ltrim($url, '/'); + + + // Return URL + return $url; + } + + } + +?> diff --git a/core/Logger.inc b/core/Logger.inc new file mode 100644 index 00000000..b5ac7dc9 --- /dev/null +++ b/core/Logger.inc @@ -0,0 +1,132 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class to log messages to different targets. + * + * @author coderkun + */ + class Logger + { + /** + * Log mode: Detect automatic + * + * @var int + */ + const LOGMODE_AUTO = 0; + /** + * Log mode: Print to screen + * + * @var int + */ + const LOGMODE_SCREEN = 1; + /** + * Log mode: Use PHP-logging mechanism + * + * @var int + */ + const LOGMODE_PHP = 2; + + /** + * Do not auto-log to screen + * + * @var boolean + */ + private $autoLogToScreenDisabled = false; + + + + + /** + * Construct a new logger. + */ + public function __construct() + { + } + + + + + /** + * Log a message. + * + * @param string $message Message to log + * @param int $logMode Log mode to use + */ + public function log($message, $logMode=self::LOGMODE_SCREEN) + { + // Choose log mode automatically + if($logMode == self::LOGMODE_AUTO) { + $logMode = $this->getAutoLogMode(); + } + + // Print message to screen + if($logMode & self::LOGMODE_SCREEN) { + $this->logToScreen($message); + } + + // Use PHP-logging mechanism + if($logMode & self::LOGMODE_PHP) { + $this->logToPhp($message); + } + } + + + /** + * Disable logging to screen when the log mode is automatically + * detected. + */ + public function disableAutoLogToScreen() + { + $this->autoLogToScreenDisabled = true; + } + + + + /** + * Print a message to screen. + * + * @param string $message Message to print + */ + private function logToScreen($message) + { + echo "$message
                    \n"; + } + + + /** + * Log a message by using PHP-logging mechanism. + * + * @param string $message Message to log + */ + private function logToPhp($message) + { + error_log($message, 0); + } + + + /** + * Detect log mode automatically by distinguishing between + * production and test environment. + * + * @return int Automatically detected log mode + */ + private function getAutoLogMode() + { + return ($_SERVER['SERVER_ADDR'] == '127.0.0.1' && !$this->autoLogToScreenDisabled) ? self::LOGMODE_SCREEN : self::LOGMODE_PHP; + } + + } + +?> diff --git a/core/Model.inc b/core/Model.inc new file mode 100644 index 00000000..c0475b4a --- /dev/null +++ b/core/Model.inc @@ -0,0 +1,141 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Abstract class for implementing a Model. + * + * @author coderkun + */ + abstract class Model + { + + + + + /** + * Load the class of a Model. + * + * @throws ModelNotFoundException + * @throws ModelNotValidException + * @param string $modelName Name of the Model to load + */ + public static function load($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + try { + // Load class + ClassLoader::load($className); + + // Validate class + ClassLoader::check($className, get_class()); + } + catch(\nre\exceptions\ClassNotValidException $e) { + throw new \nre\exceptions\ModelNotValidException($e->getClassName()); + } + catch(\nre\exceptions\ClassNotFoundException $e) { + throw new \nre\exceptions\ModelNotFoundException($e->getClassName()); + } + } + + + /** + * Instantiate a Model (Factory Pattern). + * + * @param string $modelName Name of the Model to instantiate + */ + public static function factory($modelName) + { + // Determine full classname + $className = self::getClassName($modelName); + + // Construct and return Model + return new $className(); + } + + + /** + * Determine the classname for the given Model name. + * + * @param string $modelName Model name to get classname of + * @return string Classname fore the Model name + */ + private static function getClassName($modelName) + { + $className = ClassLoader::concatClassNames($modelName, ClassLoader::stripNamespace(get_class())); + + + return \nre\configs\AppConfig::$app['namespace']."models\\$className"; + } + + + + + /** + * Construct a new Model. + * + * TODO Catch exception + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + protected function __construct() + { + // Load Models + $this->loadModels(); + } + + + + + /** + * Load the Models of this Model. + * + * @throws DatamodelException + * @throws DriverNotFoundException + * @throws DriverNotValidException + * @throws ModelNotValidException + * @throws ModelNotFoundException + */ + private function loadModels() + { + // Determine Models + $models = array(); + if(property_exists($this, 'models')) { + $models = $this->models; + } + if(!is_array($models)) { + $models = array($models); + } + + + // Load Models + foreach($models as $model) + { + // Load class + Model::load($model); + + // Construct Model + $modelName = ucfirst(strtolower($model)); + $this->$modelName = Model::factory($model); + } + } + + } + +?> diff --git a/core/Request.inc b/core/Request.inc new file mode 100644 index 00000000..5fda187e --- /dev/null +++ b/core/Request.inc @@ -0,0 +1,64 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a request. + * + * @author coderkun + */ + class Request + { + /** + * Passed parameters + * + * @var array + */ + protected $params = array(); + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + } + +?> diff --git a/core/Response.inc b/core/Response.inc new file mode 100644 index 00000000..4781b2ab --- /dev/null +++ b/core/Response.inc @@ -0,0 +1,158 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Base class to represent a response. + * + * @author coderkun + */ + class Response + { + /** + * Applied parameters + * + * @var array + */ + protected $params = array(); + /** + * Generated output + * + * @var string + */ + private $output = ''; + /** + * Abort futher execution + * + * @var bool + */ + private $exit = false; + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + $this->params[] = $value; + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->params = array_merge( + $this->params, + func_get_args() + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + $this->params = array_slice($this->params, 0, $offset); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + // Return parameter + if(count($this->params) > $index) { + return $this->params[$index]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + return array_slice($this->params, $offset); + } + + + /** + * Set output. + * + * @param string $output Generated output + */ + public function setOutput($output) + { + $this->output = $output; + } + + + /** + * Get generated output. + * + * @return string Generated output + */ + public function getOutput() + { + return $this->output; + } + + + /** + * Set exit-state. + * + * @param bool $exit Abort further execution + */ + public function setExit($exit=true) + { + $this->exit = $exit; + } + + + /** + * Get exit-state. + * + * @return bool Abort further execution + */ + public function getExit() + { + return $this->exit; + } + + } + +?> diff --git a/core/View.inc b/core/View.inc new file mode 100644 index 00000000..546ddb6e --- /dev/null +++ b/core/View.inc @@ -0,0 +1,124 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * View. + * + * Class to encapsulate a template file and to provide rendering methods. + * + * @author coderkun + */ + class View + { + /** + * Template filename + * + * @var string + */ + protected $templateFilename; + + + + + /** + * Load and instantiate the View of an Agent. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + public static function loadAndFactory($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + return new View($layoutName, $agentName, $action, $isToplevel); + } + + + + + /** + * Construct a new View. + * + * @throws ViewNotFoundException + * @param string $layoutName Name of Layout in use + * @param string $agentName Name of the Agent + * @param string $action Current Action + * @param bool $isToplevel Agent is a ToplevelAgent + */ + protected function __construct($layoutName, $agentName=null, $action=null, $isToplevel=false) + { + // Create template filename + // LayoutName + $fileName = ROOT.DS. \nre\configs\CoreConfig::getClassDir('views').DS. strtolower($layoutName).DS; + // AgentName and Action + if(strtolower($agentName) != $layoutName || !$isToplevel) { + $fileName .= strtolower($agentName).DS.strtolower($action); + } + else { + $fileName .= strtolower($layoutName); + } + // File extension + $fileName .= \nre\configs\CoreConfig::getFileExt('views'); + + + // Check template file + if(!file_exists($fileName)) { + throw new \nre\exceptions\ViewNotFoundException($fileName); + } + + // Save filename + $this->templateFilename = $fileName; + } + + + + + /** + * Generate output + * + * @param array $data Data to have available in the template file + */ + public function render($data) + { + // Extract data + if(!is_null($data)) { + extract($data, EXTR_SKIP); + } + + // Buffer output + ob_start(); + + // Include template + include($this->templateFilename); + + + // Return buffered output + return ob_get_clean(); + } + + + /** + * Get template filename. + * + * @return string Template filename + */ + public function getTemplateFilename() + { + return $this->templateFilename; + } + + } + +?> diff --git a/core/WebUtils.inc b/core/WebUtils.inc new file mode 100644 index 00000000..5a4bb6f0 --- /dev/null +++ b/core/WebUtils.inc @@ -0,0 +1,75 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\core; + + + /** + * Class that holds several web-specific methods and properties. + * + * @author coderkun + */ + class WebUtils + { + /** + * HTTP-statuscode 403: Forbidden + * + * @var int + */ + const HTTP_FORBIDDEN = 403; + /** + * HTTP-statuscode 404: Not Found + * + * @var int + */ + const HTTP_NOT_FOUND = 404; + /** + * HTTP-statuscode 503: Service Unavailable + * + * @var int + */ + const HTTP_SERVICE_UNAVAILABLE = 503; + + /** + * HTTP-header strings + * + * @var array + */ + public static $httpStrings = array( + 200 => 'OK', + 304 => 'Not Modified', + 403 => 'Forbidden', + 404 => 'Not Found', + 503 => 'Service Unavailable' + ); + + + + + /** + * Get the HTTP-header of a HTTP-statuscode + * + * @param int $httpStatusCode HTTP-statuscode + * @return string HTTP-header of HTTP-statuscode + */ + public static function getHttpHeader($httpStatusCode) + { + if(!array_key_exists($httpStatusCode, self::$httpStrings)) { + $httpStatusCode = 200; + } + + + return sprintf("HTTP/1.1 %d %s\n", $httpStatusCode, self::$httpStrings[$httpStatusCode]); + } + + } + +?> diff --git a/drivers/DatabaseDriver.inc b/drivers/DatabaseDriver.inc new file mode 100644 index 00000000..dfd5c0fd --- /dev/null +++ b/drivers/DatabaseDriver.inc @@ -0,0 +1,87 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Abstract class for implementing a database driver. + * + * @author coderkun + */ + abstract class DatabaseDriver extends \nre\core\Driver + { + /** + * Driver class instance + * + * @static + * @var DatabaseDriver + */ + protected static $driver = null; + /** + * Connection resource + * + * @var resource + */ + protected $connection = null; + + + + + /** + * Singleton-pattern. + * + * @param array $config Database driver configuration + * @return DatabaseDriver Singleton-instance of database driver class + */ + public static function singleton($config) + { + // Singleton + if(self::$driver !== null) { + return self::$driver; + } + + // Construct + $className = get_called_class(); + self::$driver = new $className($config); + + + return self::$driver; + } + + + /** + * Construct a new database driver. + * + * @param array $config Connection and login settings + */ + protected function __construct($config) + { + parent::__construct(); + + // Establish connection + $this->connect($config); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected abstract function connect($config); + + } + +?> diff --git a/drivers/MysqliDriver.inc b/drivers/MysqliDriver.inc new file mode 100644 index 00000000..fe4fa81a --- /dev/null +++ b/drivers/MysqliDriver.inc @@ -0,0 +1,169 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\drivers; + + + /** + * Implementation of a database driver for MySQL-databases. + * + * @author coderkun + */ + class MysqliDriver extends \nre\drivers\DatabaseDriver + { + + + + + /** + * Construct a MySQL-driver. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + function __construct($config) + { + parent::__construct($config); + } + + + + + /** + * Execute a SQL-query. + * + * @throws DatamodelException + * @param string $query Query to run + * @param mixed … Params + * @return array Result + */ + public function query($query) + { + // Prepare statement + if(!($stmt = $this->connection->prepare($query))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + try { + // Prepare data + $data = array(); + + // Bind parameters + $p = array_slice(func_get_args(), 1); + $params = array(); + foreach($p as $key => $value) { + $params[$key] = &$p[$key]; + } + if(count($params) > 0) + { + if(!(call_user_func_array(array($stmt, 'bind_param'), $params))) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + } + + // Execute query + if(!$stmt->execute()) { + throw new \nre\exceptions\DatamodelException($this->connection->error, $this->connection->errno); + } + + // Fetch result + if($result = $stmt->get_result()) { + while($row = $result->fetch_assoc()) { + $data[] = $row; + } + } + + + $stmt->close(); + return $data; + } + catch(Exception $e) { + $stmt->close(); + throw $e; + } + } + + + /** + * Return the last insert id (of the last insert-query). + * + * @return int Last insert id + */ + public function getInsertId() + { + return $this->connection->insert_id; + } + + + /** + * Enable/disable autocommit feature. + * + * @param boolean $autocommit Enable/disable autocommit + */ + public function setAutocommit($autocommit) + { + $this->connection->autocommit($autocommit); + } + + + /** + * Rollback the current transaction. + */ + public function rollback() + { + $this->connection->rollback(); + } + + + /** + * Commit the current transaction. + */ + public function commit() + { + $this->connection->commit(); + } + + + + + /** + * Establish a connect to a MqSQL-database. + * + * @throws DatamodelException + * @param array $config Connection and login settings + */ + protected function connect($config) + { + // Connect + $con = @new \mysqli( + $config['host'], + $config['user'], + $config['password'], + $config['db'] + ); + + // Check connection + if($con->connect_error) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Set character encoding + if(!$con->set_charset('utf8')) { + throw new \nre\exceptions\DatamodelException($con->connect_error, $con->connect_errno); + } + + // Save connection + $this->connection = $con; + } + + } + +?> diff --git a/exceptions/AccessDeniedException.inc b/exceptions/AccessDeniedException.inc new file mode 100644 index 00000000..31bba929 --- /dev/null +++ b/exceptions/AccessDeniedException.inc @@ -0,0 +1,51 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Access denied. + * + * @author coderkun + */ + class AccessDeniedException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'access denied'; + + + + + /** + * Consturct a new exception. + */ + function __construct() + { + parent::__construct( + self::MESSAGE, + self::CODE + ); + } + + } + +?> diff --git a/exceptions/ActionNotFoundException.inc b/exceptions/ActionNotFoundException.inc new file mode 100644 index 00000000..418dd49c --- /dev/null +++ b/exceptions/ActionNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ActionNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 70; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'action not found'; + + /** + * Name of the action that was not found + * + * @var string + */ + private $action; + + + + + /** + * Construct a new exception. + * + * @param string $action Name of the action that was not found + */ + function __construct($action) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $action + ); + + // Store values + $this->action = $action; + } + + + + + /** + * Get the name of the action that was not found. + * + * @return string Name of the action that was not found + */ + public function getAction() + { + return $this->action; + } + + } + +?> diff --git a/exceptions/AgentNotFoundException.inc b/exceptions/AgentNotFoundException.inc new file mode 100644 index 00000000..f0b3a2a7 --- /dev/null +++ b/exceptions/AgentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not found. + * + * @author coderkun + */ + class AgentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 66; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not found'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the Agent that was not found + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Agent that was not found. + * + * @return string Name of the Agent that was not found + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/AgentNotValidException.inc b/exceptions/AgentNotValidException.inc new file mode 100644 index 00000000..1c752051 --- /dev/null +++ b/exceptions/AgentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Agent not valid. + * + * @author coderkun + */ + class AgentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 76; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'agent not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $agentName Name of the invalid Agent + */ + function __construct($agentName) + { + parent::__construct( + $agentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Agent. + * + * @return string Name of the invalid Agent + */ + public function getAgentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ClassNotFoundException.inc b/exceptions/ClassNotFoundException.inc new file mode 100644 index 00000000..070f383f --- /dev/null +++ b/exceptions/ClassNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not found. + * + * @author coderkun + */ + class ClassNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 64; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not found'; + + /** + * Name of the class that was not found + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception + * + * @param string $className Name of the class that was not found + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store values + $this->className = $className; + } + + + + + /** + * Get the name of the class that was not found. + * + * @return string Name of the class that was not found + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ClassNotValidException.inc b/exceptions/ClassNotValidException.inc new file mode 100644 index 00000000..bdd36d5e --- /dev/null +++ b/exceptions/ClassNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Class not valid. + * + * @author coderkun + */ + class ClassNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 74; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'class not valid'; + + /** + * Name of the invalid class + * + * @var string + */ + private $className; + + + + + /** + * Construct a new exception. + * + * @param string $className Name of the invalid class + */ + function __construct($className, $message=self::MESSAGE, $code=self::CODE) + { + parent::__construct( + $message, + $code, + $className + ); + + // Store value + $this->className = $className; + } + + + + + /** + * Get the name of the invalid class. + * + * @return string Name of the invalid class + */ + public function getClassName() + { + return $this->className; + } + + } + +?> diff --git a/exceptions/ComponentNotFoundException.inc b/exceptions/ComponentNotFoundException.inc new file mode 100644 index 00000000..5e75de44 --- /dev/null +++ b/exceptions/ComponentNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not found. + * + * @author coderkun + */ + class ComponentNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not found'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the Component that was not found + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Component that was not found. + * + * @return string Name of the Component that was not found + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ComponentNotValidException.inc b/exceptions/ComponentNotValidException.inc new file mode 100644 index 00000000..a03b0c0d --- /dev/null +++ b/exceptions/ComponentNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Component not valid. + * + * @author coderkun + */ + class ComponentNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'component not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $componentName Name of the invalid Component + */ + function __construct($componentName) + { + parent::__construct( + $componentName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Component. + * + * @return string Name of the invalid Component + */ + public function getComponentName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotFoundException.inc b/exceptions/ControllerNotFoundException.inc new file mode 100644 index 00000000..90859c49 --- /dev/null +++ b/exceptions/ControllerNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not found. + * + * @author coderkun + */ + class ControllerNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 67; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not found'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the Controller that was not found + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Controller that was not found. + * + * @return string Name of the Controller that was not found + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ControllerNotValidException.inc b/exceptions/ControllerNotValidException.inc new file mode 100644 index 00000000..0c945bcb --- /dev/null +++ b/exceptions/ControllerNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Controller not valid. + * + * @author coderkun + */ + class ControllerNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 77; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'controller not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $controllerName Name of the invalid Controller + */ + function __construct($controllerName) + { + parent::__construct( + $controllerName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Controller. + * + * @return string Name of the invalid Controller + */ + public function getControllerName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DatamodelException.inc b/exceptions/DatamodelException.inc new file mode 100644 index 00000000..7785cd21 --- /dev/null +++ b/exceptions/DatamodelException.inc @@ -0,0 +1,99 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel. + * + * This exception is thrown when an error occurred during the execution + * of a datamodel. + * + * @author coderkun + */ + class DatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/DriverNotFoundException.inc b/exceptions/DriverNotFoundException.inc new file mode 100644 index 00000000..9b218f29 --- /dev/null +++ b/exceptions/DriverNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not found. + * + * @author coderkun + */ + class DriverNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 71; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not found'; + + + + + /** + * Construct a new exception. + * + * @param string $driverName Name of the driver that was not found + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the driver that was not found. + * + * @return string Name of the driver that was not found + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/DriverNotValidException.inc b/exceptions/DriverNotValidException.inc new file mode 100644 index 00000000..fa9022e8 --- /dev/null +++ b/exceptions/DriverNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Driver not valid. + * + * @author coderkun + */ + class DriverNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 81; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'driver not valid'; + + + + + /** + * Konstruktor. + * + * @param string $driverName Name of the invalid driver + */ + function __construct($driverName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $driverName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid driver. + * + * @return string Name of the invalid driver + */ + public function getDriverName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/FatalDatamodelException.inc b/exceptions/FatalDatamodelException.inc new file mode 100644 index 00000000..afd80b86 --- /dev/null +++ b/exceptions/FatalDatamodelException.inc @@ -0,0 +1,96 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Datamodel exception that is fatal for the application. + * + * @author coderkun + */ + class FatalDatamodelException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'fatal datamodel error'; + + /** + * Error message of datamodel + * + * @var string + */ + private $datamodelMessage; + /** + * Error code of datamodel + * + * @var int + */ + private $datamodelErrorNumber; + + + + + /** + * Consturct a new exception. + * + * @param string $datamodelMessage Error message of datamodel + * @param int $datamodelErrorNumber Error code of datamodel + */ + function __construct($datamodelMessage, $datamodelErrorNumber) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $datamodelMessage." ($datamodelErrorNumber)" + ); + + // Store values + $this->datamodelMessage = $datamodelMessage; + $this->datamodelErrorNumber = $datamodelErrorNumber; + } + + + + + /** + * Get the error message of datamodel. + * + * @return string Error message of datamodel + */ + public function getDatamodelMessage() + { + return $this->datamodelMessage; + } + + + /** + * Get the error code of datamodel. + * + * @return string Error code of datamodel + */ + public function getDatamodelErrorNumber() + { + return $this->datamodelErrorNumber; + } + + } + +?> diff --git a/exceptions/IdNotFoundException.inc b/exceptions/IdNotFoundException.inc new file mode 100644 index 00000000..fc3315c3 --- /dev/null +++ b/exceptions/IdNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: ID not found. + * + * @author coderkun + */ + class IdNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 85; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'id not found'; + + /** + * ID that was not found + * + * @var mixed + */ + private $id; + + + + + /** + * Consturct a new exception. + * + * @param mixed $id ID that was not found + */ + function __construct($id) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $id + ); + + // Store values + $this->id = $id; + } + + + + + /** + * Get the ID that was not found. + * + * @return mixed ID that was not found + */ + public function getId() + { + return $this->id; + } + + } + +?> diff --git a/exceptions/LayoutNotFoundException.inc b/exceptions/LayoutNotFoundException.inc new file mode 100644 index 00000000..0eb8c89c --- /dev/null +++ b/exceptions/LayoutNotFoundException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not found. + * + * @author coderkun + */ + class LayoutNotFoundException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 65; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not found'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the Layout that was not found + */ + function __construct($layoutName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Layout that was not found. + * + * @return string Name of the Layout that was not found + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/LayoutNotValidException.inc b/exceptions/LayoutNotValidException.inc new file mode 100644 index 00000000..b3af184f --- /dev/null +++ b/exceptions/LayoutNotValidException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Layout not valid. + * + * @author coderkun + */ + class LayoutNotValidException extends \nre\exceptions\AgentNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 75; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'layout not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $layoutName Name of the invalid Layout + */ + function __construct($layoutName) + { + parent::__construct( + $layoutName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Layout. + * + * @return string Name of the invalid Layout + */ + public function getLayoutName() + { + return $this->getAgentName(); + } + + } + +?> diff --git a/exceptions/ModelNotFoundException.inc b/exceptions/ModelNotFoundException.inc new file mode 100644 index 00000000..48e8c0df --- /dev/null +++ b/exceptions/ModelNotFoundException.inc @@ -0,0 +1,67 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotFoundException extends \nre\exceptions\ClassNotFoundException + { + /** + * Error code + * + * @var int + */ + const CODE = 68; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not found'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the Model that was not found + */ + function __construct($modelName) + { + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the Model that was not found + * + * @return string Name of the Model that was not found + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ModelNotValidException.inc b/exceptions/ModelNotValidException.inc new file mode 100644 index 00000000..267e460e --- /dev/null +++ b/exceptions/ModelNotValidException.inc @@ -0,0 +1,68 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Action not found. + * + * @author coderkun + */ + class ModelNotValidException extends \nre\exceptions\ClassNotValidException + { + /** + * Error code + * + * @var int + */ + const CODE = 78; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'model not valid'; + + + + + /** + * Construct a new exception. + * + * @param string $modelName Name of the invalid Model + */ + function __construct($modelName) + { + // Elternkonstruktor aufrufen + parent::__construct( + $modelName, + self::MESSAGE, + self::CODE + ); + } + + + + + /** + * Get the name of the invalid Model + * + * @return string Name of the invalid Model + */ + public function getModelName() + { + return $this->getClassName(); + } + + } + +?> diff --git a/exceptions/ParamsNotValidException.inc b/exceptions/ParamsNotValidException.inc new file mode 100644 index 00000000..be650ce2 --- /dev/null +++ b/exceptions/ParamsNotValidException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception Parameters not valid. + * + * @author coderkun + */ + class ParamsNotValidException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 86; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'parameters not valid'; + + /** + * Invalid parameters. + * + * @var array + */ + private $params; + + + + + /** + * Construct a new exception. + * + * @param mixed $param1 Invalid parameters as argument list + */ + function __construct($param1) + { + parent::__construct( + self::MESSAGE, + self::CODE, + implode(', ', func_get_args()) + ); + + // Store values + $this->params = func_get_args(); + } + + + + + /** + * Get invalid parameters. + * + * @return array Invalid parameters + */ + public function getParams() + { + return $this->params; + } + + } + +?> diff --git a/exceptions/ServiceUnavailableException.inc b/exceptions/ServiceUnavailableException.inc new file mode 100644 index 00000000..bafc297f --- /dev/null +++ b/exceptions/ServiceUnavailableException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: Service is unavailable. + * + * @author coderkun + */ + class ServiceUnavailableException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 84; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'service unavailable'; + + /** + * Throws exception + * + * @var Exception + */ + private $exception; + + + + + /** + * Construct a new exception. + * + * @param Exception $exception Exception that has occurred + */ + function __construct($exception) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $exception->getMessage() + ); + + // Store values + $this->exception = $exception; + } + + + + + /** + * Get the exception that hat occurred + * + * @return Exception Exception that has occurred + */ + public function getException() + { + return $this->exception; + } + + } + +?> diff --git a/exceptions/ViewNotFoundException.inc b/exceptions/ViewNotFoundException.inc new file mode 100644 index 00000000..30366201 --- /dev/null +++ b/exceptions/ViewNotFoundException.inc @@ -0,0 +1,77 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\exceptions; + + + /** + * Exception: View not found. + * + * @author coderkun + */ + class ViewNotFoundException extends \nre\core\Exception + { + /** + * Error code + * + * @var int + */ + const CODE = 69; + /** + * Error message + * + * @var string + */ + const MESSAGE = 'view not found'; + + /** + * Filename of the view that was not found + * + * @var string + */ + private $fileName; + + + + + /** + * Construct a new exception. + * + * @param string $fileName Filename of the view that was not found + */ + function __construct($fileName) + { + parent::__construct( + self::MESSAGE, + self::CODE, + $fileName + ); + + // Save values + $this->fileName = $fileName; + } + + + + + /** + * Get the filename of the view that was not found. + * + * @return string Filename of the view that was not found + */ + public function getFileName() + { + return $this->fileName; + } + + } + +?> diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo new file mode 100644 index 0000000000000000000000000000000000000000..bbbde6ffcb0f1f8bfdc90f658e3b5323f0e693e0 GIT binary patch literal 9152 zcmai&dyE}deaDB8l4OChAbLPyM-}#;2?|07G?`^yMcRj9;A-{;c=W@@x03W}SAFj@8J?~n0 z2JVI*hp&Sd;5PVI@b&N?;LpMzK(&7f?trhO@zwAq_%?V5-U#0Z-wZ$D%D)eHQ~q1{ zM)+f>_pYY%5%@Ov8n_ek$lD7i;q9(IhWh>zlwKc&KLAA9=IJIfIkl(f^UIKQ0+birQesJ`uQ4EKTp9c;NQTv z!tc2H7opm}3^l&XP*(3<1yz0{)O&A*zW}d?Op$j7)O&}arfL?x3EuC@F_gY%;XB~R z;VAqvRJ-p&_4@+69{wlf&wDd3sk{T~y}P0GkKs<3L+SSgC_8=;UIQ;c+3&BR^nS)Y zf7aDM52g2iKz;YWp!EI;)Hp7u6V1alP~Y3`I0`kNyP@Xu4yf9)s%dFJWOvsBu06Uk(2m?tF}DfET1 z_YBnc7F_uuNK>!w%4eba`w-N4e#7xM;boM62VxrUGf?_{(LMhflwRL}8s}3`-}^3< zp8o{p|38HK{wt1G;$+`^6@9{|ssDR|UHFS??n%AeH^!it*_bww@ql))hcq825crTQ`AAqvwZ$s(*7{oN*8kC>@i{lm7mHJOW z&D$+dQsP=5S8lpa^Uz4W)Mq3m)4R6jRCjqe21ygUf^z(*iM@%{*o!EZVK z7)qa=G?v~IQ1fvRDt|l(55R|^^2ZZU?Vg6R`}d*j@q#PA3^m?Yp!$6iO6&P`P~}}v zel+QL3zVI1hl-CGsD2-YnvVc#JRK-|d=P59AA$1APeZl;5>)?x244jqMc#?bAgjpW zrTEpgA5m`If#}=6jvPnsK;#>`rV-gsv8_wrh>+V5K2==r=2t$nf+R%^`O{H2jmYL2 z&oSgOL}So+x)*Nm9YG#M-iy2kSwwWnZ*&o2-u=kE$gd!}c9-zZLD_v5BEPL5HzAK8 z4P+E~H8O?h(j3c=KBj`ZT8_UA-{tNzh|8|K<|)Z1bsa9@?Sr^daqZ;SM{Y)N18)Jj z9g*M6BJ!hC$cGW-KFx0xc>paoW zgV#|Cp{uxO;K8qPFaNt6$&rJIu5lzrB)Ucq{ULS8<=? z8F-_+pMVzWAO;DLJCSANETS0JwGH`Iq^5$dgSA#*mu%ZcnW@{fmIR$Fh@*A&Ek89? zYa`>Ajq4_k!WA=GH(AiO>0nz5VHhvlx@iQVHQ7o>&pREOQ<2#u^$xfE#IJE{?n>gk zv$4Y5o7*%Std|14Z}Zs!r3afue%lUoucGn8#QOCWW6uWb`z)T;;wbZjC^bPC+NK|x zT35N@eoYW9`C(8W?mtLP7RM%xqo&!}_Ro&VEb1lQb%~~FD^4=n2GQniJ4xKJNfuzZ zzNuu$eo;^2IHd;tgDCflDRTvDI-$Q3pidT?JPPC5qRCq5nc7-nvq9r|hcQk@Hw8@Z zbKMGmV^w6A=?LZhWTo3ob$MzN?}!a;X8X@w**)t!*K|F|`mKsm(Plczk`$oiO(6Q)V>vrk^|a>9!w)#;?~Cn{KQap8Vk#dUJYIzG~0btgU0F zVAUFCGHB;*uXrN&TTV2?ng;XSxyMJ-=gvLh9Yfu?p4STNANSL&^jz;a0Tp`3gK8op zISxkG9@Lk3$Kz%YmA|IJP;6a=IL|<@yBXWAV%!p`H=`$MUaK+WMvlwI_--wW39Sr*-upYP9+j+hWR;3ent5i%w%B?sLN#d=3$o8Du zkPR{P8ixl^MlrA&k`Ng1PGCy7P z=JRShNK+Y8mPE%qDeUODrJHoybls#^-oj0W+HCG73sOx#y^PY2KWvo7;S!O%fF0MH zxNjk%sH@dNkYS1i`9d!uDP{ynH$aDq{dhfz*$Z=YZ+Z&3^{rRGRXFt;byezDY+Tty1qa^H1*O6i14CM{(BOkYvFU zD_GsE*v#v7>}SOSQS+l3iCCs!k+dGmpH{mPbyH;vld_eOo0UH9u`~y(N<^U-l4doQIT#Q7@56ip;9d2JgppIh*M^Ad}3c^d|zd9 z!c0y~?VH>^J~2K{!^)gp3e?MRyGb)XJ~eq;*-mR&Wg#ItL!X6Z%8VTMqh{_mZDqmw z?Sb+X+2P?M(<3v-W~TcJPK=F@Xg9*9va-OEIaRFbd)b!+(f)FGWU~V&7mikL?YGnC z8aAm+N42<4LYy+UR)cKhzRF!TvSdT)F&kTL9VW(Z9^1#0vRFCfr?%r~ttm4$wzvEn znGN&A4=YFGq|JI4b&6toU~)gZBRx5=Gcs>KU?z6$@Aos$4>&4dBj|$YziA#g#88cb}&y@ZBT1P#WT}b zyO?0fAhKpUX~bdEnxJi%@X*g2JV?x(4N^O1<^%F^fI--dhpAy(hg|~t){8-ep~?>Z z$jn$$Zp4a0uX_j=)?hlaW&sC@*cjHyNGvz(2YhH^eI?A(Ea2nQi4B?&iI_L}96K-A(Psq<M^ysE}B(0rh7n!_DOsU{y_1v+XmN8*4B5Mza74jn3H*ojvT z7H0}uoH7P0HuE7OL}!b2&4wI)U>CXeWHJ9mH511Y_XV9R9xs|iI9!Hh ziumyu1}UN?x94<`ttx!xJB3pwojh9PyilAu4h7XPh}qCBdhf!`+2|2qe31BMH%IKZ zbka9|o)$^vR7_B(blU&6@`571!^xnCPPbn}=e9ZF*IG$Wd5LDmczq}RQ{ebkjLVgygbAWEn6e!DH#I#Hg$a)u@MEZm6k7Xg6e^n} zgSP5#@n*{-S>_#Kqth)EE`j1@KHB(biwrZ|3r!a{PcE82rT+EF)x$}8+0@+|7WOT3 zWVhW=5HRM!CtGN}vE?O{E~edclV5G}AFLKTH(VEg)FBRcW5r}ZE zTG}x3?ke-|&;%6T#*PywD;#aZ^vKO@kqa9*H!e6&uSHzxNk0IWgJe;DN1iX!X+})3 zl1H5Dx^Hy-wEtFdvfJX--r<5T)W63oOblT z9bW4;D@?C{Ww1F?Flou{7JjPV^%`z;ymgPZ_PpVymvB8d4zKHHN}JuWx$VVZo^XaQ z16y;W`RZGGy~Z38Il0K)_%{GPy*^u%3woJl@tf3dXY8#q+9~h$b4hnTdxjjW*lZ!= zGwYlDlsI44c>M@8ZyO$OZTHQZU&8j?ri^|$X4~6K*bA)l<$};)5y5a|{fvKcmB-KS zR8<2`Hu$6qbv6lP4y>q(;Rs_6i#*T7X@To)!TM8I?uet9;3^jtf01LUwwYcx=@^}w zMpMtPRufxnZZu&HpUq4}qwfC4-DPD0B8b|0ski+ZIG%DqEEXzvL{;FIhtwkdu!PrI z0~ML;DRp~>sOXbVml;nVpipK8lvYH#F%R6AeQ3uxnwpcX#3QPUT#|WclLd<$SN|Wi CFSRxR literal 0 HcmV?d00001 diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po new file mode 100644 index 00000000..8ada359f --- /dev/null +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -0,0 +1,705 @@ +msgid "" +msgstr "" +"Project-Id-Version: The Legend of Z\n" +"POT-Creation-Date: 2014-04-21 21:42+0100\n" +"PO-Revision-Date: 2014-04-22 00:29+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" +"X-Poedit-SearchPath-1: questtypes\n" + +#: questtypes/bossfight/html/quest.tpl:11 +#: questtypes/bossfight/html/quest.tpl:24 +#: questtypes/bossfight/html/submission.tpl:21 +#: questtypes/bossfight/html/submission.tpl:33 +msgid "lost" +msgstr "verloren" + +#: questtypes/bossfight/html/quest.tpl:41 +msgid "Choose" +msgstr "Wählen" + +#: questtypes/bossfight/html/quest.tpl:47 +#: questtypes/choiceinput/html/quest.tpl:14 +#: questtypes/crossword/html/quest.tpl:30 +#: questtypes/dragndrop/html/quest.tpl:16 +#: questtypes/multiplechoice/html/quest.tpl:19 +#: questtypes/submit/html/quest.tpl:23 questtypes/textinput/html/quest.tpl:10 +msgid "solve" +msgstr "lösen" + +#: questtypes/crossword/html/quest.tpl:22 +#: questtypes/crossword/html/submission.tpl:22 +msgid "vertical" +msgstr "vertikal" + +#: questtypes/crossword/html/quest.tpl:24 +#: questtypes/crossword/html/submission.tpl:24 +msgid "horizontal" +msgstr "horizontal" + +#: questtypes/multiplechoice/html/quest.tpl:3 +#, php-format +msgid "Question %d of %d" +msgstr "Frage %d von %d" + +#: questtypes/multiplechoice/html/quest.tpl:17 +msgid "solve Question" +msgstr "Frage lösen" + +#: questtypes/submit/html/quest.tpl:4 +#, php-format +msgid "File has wrong type “%s”" +msgstr "Der Dateityp „%s“ ist nicht erlaubt" + +#: questtypes/submit/html/quest.tpl:6 +msgid "File exceeds size maximum" +msgstr "Die Datei ist zu groß" + +#: questtypes/submit/html/quest.tpl:8 +#, php-format +msgid "Error during file upload: %s" +msgstr "Fehler beim Dateiupload: %s" + +#: questtypes/submit/html/quest.tpl:17 +msgid "Allowed file types" +msgstr "Erlaubte Dateiformate" + +#: questtypes/submit/html/quest.tpl:20 +msgid "max." +msgstr "max." + +#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#, php-format +msgid "submitted at %s on %s h" +msgstr "eingereicht am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:6 +#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:43 +#: views/html/quests/submissions.tpl:33 +msgid "solved" +msgstr "gelöst" + +#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:46 +#: views/html/quests/submissions.tpl:24 +msgid "unsolved" +msgstr "ungelöst" + +#: views/binary/error/index.tpl:1 views/html/error/index.tpl:1 +msgid "Error" +msgstr "Fehler" + +#: views/html/achievements/index.tpl:9 views/html/characters/character.tpl:31 +#: views/html/seminarymenu/index.tpl:4 +msgid "Achievements" +msgstr "Errungenschaften" + +#: views/html/achievements/index.tpl:10 +msgid "Achievement description" +msgstr "" +"Errungenschaften sind Auszeichnungen für deine Erfolge im Verlauf der Reise. " +"Sie dienen als historische Erinnerungen an Meilensteine, besondere Taten und " +"interessante oder lustige Ereignisse, die du erlebst." + +#: views/html/achievements/index.tpl:13 +msgid "Seldom Achievements" +msgstr "Die seltensten Errungenschaften" + +#: views/html/achievements/index.tpl:27 +#, php-format +msgid "Achievement has been achieved only %d times" +msgstr "wurde erst %d mal gefunden" + +#: views/html/achievements/index.tpl:33 +msgid "Most successful collectors" +msgstr "Die erfolgreichsten Sammler" + +#: views/html/achievements/index.tpl:39 +#, php-format +msgid "Character has achieved %d Achievements" +msgstr "hat %d Errungenschaften erhalten" + +#: views/html/achievements/index.tpl:45 +msgid "Personal Achievements" +msgstr "Deine Errungenschaften" + +#: views/html/achievements/index.tpl:47 +#, php-format +msgid "Own progress: %d %%" +msgstr "Persönlicher Fortschritt: %d %%" + +#: views/html/achievements/index.tpl:52 +#: views/html/charactergroups/group.tpl:17 +#: views/html/characters/character.tpl:28 views/html/seminarybar/index.tpl:7 +msgid "Rank" +msgstr "Platz" + +#: views/html/achievements/index.tpl:52 +#, php-format +msgid "You achieved %d of %d Achievements so far" +msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" + +#: views/html/achievements/index.tpl:61 views/html/characters/character.tpl:39 +#: views/html/seminarybar/index.tpl:28 +#, php-format +msgid "achieved at: %s" +msgstr "erhalten am: %s" + +#: views/html/achievements/index.tpl:72 +msgid "Secret Achievement" +msgstr "Geheime Errungenschaft" + +#: views/html/achievements/index.tpl:77 +msgid "Continue playing to unlock this secret Achievement" +msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" + +#: views/html/charactergroups/group.tpl:8 +#: views/html/charactergroups/groupsgroup.tpl:8 +#: views/html/charactergroups/index.tpl:9 +#: views/html/characters/character.tpl:51 views/html/seminarymenu/index.tpl:3 +msgid "Character Groups" +msgstr "Charaktergruppen" + +#: views/html/charactergroups/group.tpl:19 +msgid "Members" +msgstr "Mitglieder" + +#: views/html/charactergroups/group.tpl:19 +msgid "Member" +msgstr "Mitglied" + +#: views/html/charactergroups/group.tpl:23 +#: views/html/characters/character.tpl:7 views/html/characters/index.tpl:7 +#: views/html/seminarymenu/index.tpl:2 views/html/users/user.tpl:15 +msgid "Characters" +msgstr "Charaktere" + +#: views/html/charactergroups/group.tpl:38 +#: views/html/questgroups/questgroup.tpl:43 views/html/quests/create.tpl:7 +#: views/html/quests/index.tpl:7 +msgid "Quests" +msgstr "Quests" + +#: views/html/charactergroups/groupsgroup.tpl:20 +#: views/html/charactergroupsquests/quest.tpl:9 +msgid "Character Groups Quests" +msgstr "Charactergruppen-Quests" + +#: views/html/charactergroupsquests/quest.tpl:16 +msgid "Description" +msgstr "Beschreibung" + +#: views/html/charactergroupsquests/quest.tpl:24 +msgid "Rules" +msgstr "Regeln" + +#: views/html/charactergroupsquests/quest.tpl:31 +msgid "Won Quest" +msgstr "Gewonnene Quest" + +#: views/html/charactergroupsquests/quest.tpl:37 +msgid "Lost Quest" +msgstr "Verlorene Quest" + +#: views/html/characters/character.tpl:16 +msgid "Total progress" +msgstr "Fortschritt" + +#: views/html/characters/character.tpl:20 +#: views/html/characters/character.tpl:69 +#: views/html/characters/character.tpl:75 +#: views/html/characters/character.tpl:81 views/html/seminarybar/index.tpl:42 +#: views/html/users/user.tpl:29 +msgid "Level" +msgstr "Level" + +#: views/html/characters/character.tpl:63 +msgid "Ranking" +msgstr "Ranking" + +#: views/html/characters/character.tpl:89 +msgid "Topic progress" +msgstr "Thematischer Fortschritt" + +#: views/html/characters/register.tpl:7 +msgid "Create Character" +msgstr "Charakter erstellen" + +#: views/html/characters/register.tpl:20 +#, php-format +msgid "Character name is too short (min. %d chars)" +msgstr "Der Charaktername ist zu kurz (min. %d Zeichen)" + +#: views/html/characters/register.tpl:22 +#, php-format +msgid "Character name is too long (max. %d chars)" +msgstr "Der Charaktername ist zu lang (max. %d Zeichen)" + +#: views/html/characters/register.tpl:24 +msgid "Character name contains illegal characters" +msgstr "Der Charaktername enthält ungültige Zeichen" + +#: views/html/characters/register.tpl:26 +msgid "Character name already exists" +msgstr "Der Charaktername existiert bereits" + +#: views/html/characters/register.tpl:28 +msgid "Character name invalid" +msgstr "Der Charaktername ist ungültig" + +#: views/html/characters/register.tpl:40 +msgid "Character properties" +msgstr "Charaktereigenschaften" + +#: views/html/characters/register.tpl:41 views/html/characters/register.tpl:42 +msgid "Character name" +msgstr "Charaktername" + +#: views/html/characters/register.tpl:43 +msgid "Character type" +msgstr "Charaktertyp" + +#: views/html/characters/register.tpl:54 +#, php-format +msgid "The Seminary field “%s” is invalid" +msgstr "Das Kursfeld „%s“ ist ungültig" + +#: views/html/characters/register.tpl:59 +msgid "Seminary fields" +msgstr "Kursfelder" + +#: views/html/characters/register.tpl:81 views/html/seminaries/create.tpl:14 +#: views/html/users/create.tpl:17 +msgid "create" +msgstr "erstellen" + +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + +#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 +#: views/html/menu/index.tpl:6 views/html/users/login.tpl:3 +#: views/html/users/login.tpl:14 +msgid "Login" +msgstr "Login" + +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 +#: 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:9 views/html/users/login.tpl:10 +#: views/html/users/register.tpl:78 views/html/users/register.tpl:79 +msgid "Username" +msgstr "Benutzername" + +#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 +#: views/html/users/create.tpl:14 views/html/users/create.tpl:15 +#: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 +#: views/html/users/login.tpl:11 views/html/users/login.tpl:12 +#: views/html/users/register.tpl:86 views/html/users/register.tpl:87 +msgid "Password" +msgstr "Passwort" + +#: views/html/introduction/index.tpl:14 +msgid "or" +msgstr "oder" + +#: views/html/introduction/index.tpl:14 +msgid "register yourself" +msgstr "registriere dich" + +#: views/html/library/index.tpl:9 views/html/library/topic.tpl:8 +msgid "Questtopics" +msgstr "Themen" + +#: views/html/library/index.tpl:10 +#, php-format +msgid "Library description, %s, %s" +msgstr "" +"Hier findest du alle Themen aus der Vorlesung „%s“ und die passenden Quests " +"zum Nachschlagen und Wiederholen. Dein Fortschritt in „%s“ beeinflusst den " +"Umfang der Bibliothek, spiele also regelmäßig weiter und schalte so Quest " +"für Quest alle Inhalte frei." + +#: views/html/library/index.tpl:12 +#, php-format +msgid "Total progress: %d %%" +msgstr "Gesamtfortschritt: %d %%" + +#: views/html/menu/index.tpl:2 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/register.tpl:1 views/html/users/user.tpl:1 +msgid "Users" +msgstr "Benutzer" + +#: views/html/menu/index.tpl:3 views/html/seminaries/create.tpl:6 +#: views/html/seminaries/delete.tpl:6 views/html/seminaries/edit.tpl:6 +#: views/html/seminaries/index.tpl:1 +msgid "Seminaries" +msgstr "Kurse" + +#: views/html/menu/index.tpl:8 +msgid "Logout" +msgstr "Logout" + +#: views/html/questgroups/create.tpl:7 +msgid "Questgroups" +msgstr "Questgruppen" + +#: views/html/questgroups/create.tpl:8 views/html/questgroups/create.tpl:15 +#: views/html/quests/create.tpl:8 views/html/quests/create.tpl:40 +msgid "Create" +msgstr "Erstellen" + +#: views/html/questgroups/create.tpl:12 views/html/questgroups/create.tpl:13 +#: views/html/seminaries/create.tpl:11 views/html/seminaries/create.tpl:12 +#: views/html/seminaries/edit.tpl:11 views/html/seminaries/edit.tpl:12 +msgid "Title" +msgstr "Titel" + +#: views/html/quests/create.tpl:12 views/html/quests/create.tpl:13 +#: views/html/users/user.tpl:11 +msgid "Name" +msgstr "Name" + +#: views/html/quests/create.tpl:14 views/html/quests/index.tpl:15 +msgid "Questgroup" +msgstr "Questgruppe" + +#: views/html/quests/create.tpl:27 +msgid "XPs" +msgstr "XPs" + +#: views/html/quests/create.tpl:34 +msgid "Entry text" +msgstr "Einstiegstext" + +#: views/html/quests/create.tpl:36 +msgid "Wrong text" +msgstr "Text für falsche Antwort" + +#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:55 +msgid "Task" +msgstr "Aufgabe" + +#: views/html/quests/index.tpl:21 +msgid "Questname" +msgstr "Questname" + +#: views/html/quests/index.tpl:24 +msgid "Questtype" +msgstr "Questtyp" + +#: views/html/quests/index.tpl:47 +msgid "Apply filters" +msgstr "Filter anwenden" + +#: views/html/quests/index.tpl:48 +msgid "Reset filters" +msgstr "Filter zurücksetzen" + +#: views/html/quests/quest.tpl:44 +#, php-format +msgid "Quest completed. You have earned %d XPs." +msgstr "Quest abgeschlossen. Du hast %d XPs erhalten." + +#: views/html/quests/quest.tpl:60 +msgid "Task already successfully solved" +msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" + +#: views/html/quests/quest.tpl:63 +msgid "Show answer" +msgstr "Lösung anzeigen" + +#: views/html/quests/quest.tpl:64 +msgid "Skip task" +msgstr "Aufgabe überspringen" + +#: views/html/quests/quest.tpl:69 +msgid "continue" +msgstr "fortfahren" + +#: views/html/quests/quest.tpl:76 +msgid "Continuation" +msgstr "Fortsetzung" + +#: views/html/quests/quest.tpl:82 views/html/quests/quest.tpl:95 +msgid "Quest" +msgstr "Quest" + +#: views/html/quests/submission.tpl:10 +#, php-format +msgid "Submission of %s" +msgstr "Lösung von %s" + +#: views/html/quests/submissions.tpl:15 +msgid "submitted" +msgstr "eingereicht" + +#: views/html/seminaries/create.tpl:7 +msgid "New seminary" +msgstr "Neuer Kurs" + +#: views/html/seminaries/delete.tpl:7 views/html/seminaries/seminary.tpl:10 +msgid "Delete seminary" +msgstr "Kurs löschen" + +#: views/html/seminaries/delete.tpl:9 +#, php-format +msgid "Should the seminary “%s” really be deleted?" +msgstr "Soll der Kurs „%s“ wirklich gelöscht werden?" + +#: views/html/seminaries/delete.tpl:11 views/html/users/delete.tpl:6 +msgid "delete" +msgstr "löschen" + +#: views/html/seminaries/delete.tpl:12 views/html/users/delete.tpl:7 +msgid "cancel" +msgstr "abbrechen" + +#: views/html/seminaries/edit.tpl:7 views/html/seminaries/seminary.tpl:9 +msgid "Edit seminary" +msgstr "Kurs bearbeiten" + +#: views/html/seminaries/edit.tpl:14 views/html/users/edit.tpl:17 +msgid "save" +msgstr "speichern" + +#: views/html/seminaries/index.tpl:4 +msgid "Create new seminary" +msgstr "Neuen Kurs erstellen" + +#: views/html/seminaries/index.tpl:21 +#, php-format +msgid "created by %s on %s" +msgstr "erstellt von %s am %s" + +#: views/html/seminaries/index.tpl:24 +msgid "Create a Character" +msgstr "Erstelle einen Charakter" + +#: views/html/seminaries/index.tpl:26 +#, php-format +msgid "Your Character “%s” has not been activated yet" +msgstr "Dein Charakter „%s“ wurde noch nicht aktiviert" + +#: views/html/seminaries/seminary.tpl:11 +msgid "Show Quests" +msgstr "Quests anzeigen" + +#: views/html/seminarybar/index.tpl:14 +msgid "Last Quest" +msgstr "Letzter Speicherpunkt" + +#: views/html/seminarybar/index.tpl:46 +#, php-format +msgid "Show %s-Profile" +msgstr "%s-Profil anzeigen" + +#: views/html/seminarymenu/index.tpl:5 +msgid "Library" +msgstr "Bibliothek" + +#: views/html/users/create.tpl:2 +msgid "New user" +msgstr "Neuer Benutzer" + +#: views/html/users/create.tpl:8 views/html/users/create.tpl:9 +#: views/html/users/edit.tpl:8 views/html/users/edit.tpl:9 +#: views/html/users/register.tpl:80 views/html/users/register.tpl:81 +msgid "Prename" +msgstr "Vorname" + +#: 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/register.tpl:82 views/html/users/register.tpl:83 +msgid "Surname" +msgstr "Nachname" + +#: views/html/users/create.tpl:12 views/html/users/create.tpl:13 +#: views/html/users/edit.tpl:12 views/html/users/edit.tpl:13 +#: views/html/users/register.tpl:84 views/html/users/register.tpl:85 +#: views/html/users/user.tpl:12 +msgid "E‑mail address" +msgstr "E‑Mail-Adresse" + +#: 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:7 views/html/users/user.tpl:10 +#, php-format +msgid "registered on %s" +msgstr "registriert am %s" + +#: views/html/users/login.tpl:5 +msgid "Login failed" +msgstr "Die Anmeldung war nicht korrekt" + +#: views/html/users/register.tpl:3 +msgid "Registration" +msgstr "Registrierung" + +#: views/html/users/register.tpl:14 +#, php-format +msgid "Username is too short (min. %d chars)" +msgstr "Der Benutzername ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:16 +#, php-format +msgid "Username is too long (max. %d chars)" +msgstr "Der Benutzername ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:18 +msgid "Username contains illegal characters" +msgstr "Der Benutzername enthält ungültige Zeichen" + +#: views/html/users/register.tpl:20 +msgid "Username already exists" +msgstr "Der Benutzername existiert bereits" + +#: views/html/users/register.tpl:22 +msgid "Username invalid" +msgstr "Der Benutzername ist ungültig" + +#: views/html/users/register.tpl:27 +#, php-format +msgid "Prename is too short (min. %d chars)" +msgstr "Der Vorname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:29 +#, php-format +msgid "Prename is too long (max. %d chars)" +msgstr "Der Vorname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:31 +#, php-format +msgid "Prename contains illegal characters" +msgstr "Der Vorname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:33 +msgid "Prename invalid" +msgstr "Der Vorname ist ungültig" + +#: views/html/users/register.tpl:38 +#, php-format +msgid "Surname is too short (min. %d chars)" +msgstr "Der Nachname ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:40 +#, php-format +msgid "Surname is too long (max. %d chars)" +msgstr "Der Nachname ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:42 +#, php-format +msgid "Surname contains illegal characters" +msgstr "Der Nachname enthält ungültige Zeichen" + +#: views/html/users/register.tpl:44 +msgid "Surname invalid" +msgstr "Der Nachname ist ungültig" + +#: views/html/users/register.tpl:49 views/html/users/register.tpl:53 +msgid "E‑mail address invalid" +msgstr "Die E‑Mail-Adresse ist ungültig" + +#: views/html/users/register.tpl:51 +msgid "E‑mail address already exists" +msgstr "E‑Mail-Adresse existiert bereits" + +#: views/html/users/register.tpl:58 +#, php-format +msgid "Password is too short (min. %d chars)" +msgstr "Das Passwort ist zu kurz (min. %d Zeichen)" + +#: views/html/users/register.tpl:60 +#, php-format +msgid "Password is too long (max. %d chars)" +msgstr "Das Passwort ist zu lang (max. %d Zeichen)" + +#: views/html/users/register.tpl:62 +msgid "Password invalid" +msgstr "Das Passwort ist ungültig" + +#: views/html/users/register.tpl:89 +msgid "Register" +msgstr "Registrieren" + +#: views/html/users/user.tpl:34 +msgid "Roles" +msgstr "Rollen" + +#, fuzzy +#~ msgid "achieved at %s" +#~ msgstr "erhalten am: %s" + +#~ msgid "Usergroups" +#~ msgstr "Benutzergruppen" + +#~ msgid "E‑Mail" +#~ msgstr "E‑Mail" + +#~ msgid "E‑Mail-Address" +#~ msgstr "E‑Mail-Adresse" + +#~ msgid "E‑mail address not valid" +#~ msgstr "Die E‑Mail-Adresse ist nicht gültig" + +#~ msgid "Username is too long" +#~ msgstr "Der Benutzername ist zu lang" + +#~ msgid "Registration failed: %s" +#~ msgstr "Registrierung fehlgeschlagen: %s" + +#~ msgid "Words" +#~ msgstr "Wörter" + +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + +#, fuzzy +#~ msgid "Character groups" +#~ msgstr "Charaktergruppen" + +#~ msgid "locked" +#~ msgstr "gesperrt" + +#~ msgid "Group Leader" +#~ msgstr "Gruppenleiter" + +#~ msgid "User" +#~ msgstr "Benutzer" + +#~ 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" 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..c5a1d248 --- /dev/null +++ b/models/AchievementsModel.inc @@ -0,0 +1,549 @@ + + * @copyright 2014 Heinrich-Heine-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(); + } + + + + + /** + * Get an Achievement by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $achievementUrl URL-title of Achievement + * @return array Achievement data + */ + public function getAchievementByUrl($seminaryId, $achievementUrl) + { + $data = $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $achievementUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($achievementUrl); + } + + + return $data[0]; + } + + + /** + * Get all not yet achieved Achievements for a Seminary that can + * only be achieved once (only by one Character). + * + * @param int $seminaryId ID of Seminary + * @return array Achievements data + */ + public function getUnachievedOnlyOnceAchievementsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 1 AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE achievements_characters.achievement_id = achievements.id'. + ')', + 'i', + $seminaryId + ); + } + + + /** + * Get seldom Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $count Number of Achievements to retrieve + * @return array List of seldom Achievements + */ + public function getSeldomAchievements($seminaryId, $count) + { + return $this->db->query( + 'SELECT id, title, url, description, progress, hidden, unachieved_achievementsmedia_id, achieved_achievementsmedia_id, count(DISTINCT character_id) AS c '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'WHERE achievements.seminary_id = ? AND only_once = 0 '. + 'GROUP BY achievement_id '. + 'ORDER BY count(DISTINCT character_id) ASC '. + 'LIMIT ?', + 'ii', + $seminaryId, + $count + ); + } + + + /** + * Get all achieved Achievements for a Character. + * + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getAchievedAchievementsForCharacter($characterId) + { + return $this->db->query( + 'SELECT achievements.id, achievements_characters.created, achievements.title, achievements.url, achievements.description, achievements.progress, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'INNER JOIN achievements_characters ON achievements_characters.achievement_id = achievements.id '. + 'WHERE achievements_characters.character_id = ? '. + 'ORDER BY achievements_characters.created DESC', + 'i', + $characterId + ); + } + + + /** + * Get all not yet achieved Achievements for a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $characterId ID of Character + * @return array Achievements data + */ + public function getUnachhievedAchievementsForCharacter($seminaryId, $characterId, $includeOnlyOnce=false) + { + return $this->db->query( + 'SELECT achievements.id, achievementconditions.condition, title, url, description, progress, hidden, only_once, all_conditions, unachieved_achievementsmedia_id, achieved_achievementsmedia_id '. + 'FROM achievements '. + 'LEFT JOIN achievementconditions ON achievementconditions.id = achievements.achievementcondition_id '. + 'WHERE achievements.seminary_id = ? AND only_once <= ? AND NOT EXISTS ('. + 'SELECT character_id '. + 'FROM achievements_characters '. + 'WHERE '. + 'achievements_characters.achievement_id = achievements.id AND '. + 'achievements_characters.character_id = ?'. + ') '. + 'ORDER BY achievements.pos ASC', + 'iii', + $seminaryId, + $includeOnlyOnce, + $characterId + ); + } + + + /** + * Get the rank for the number of achieved Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $xps Amount of achieved Achievements + * @return int Rank of Achievements count + */ + public function getCountRank($seminaryId, $count) + { + $data = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM ('. + 'SELECT count(DISTINCT achievement_id) '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'WHERE achievements.seminary_id = ? '. + 'GROUP BY character_id '. + 'HAVING count(DISTINCT achievement_id) > ?'. + ') AS ranking', + 'ii', + $seminaryId, + $count + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * Get all date conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Date conditions + */ + public function getAchievementConditionsDate($achievementId) + { + return $this->db->query( + 'SELECT `select` '. + 'FROM achievementconditions_date '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a date condition. + * + * @param string $select SELECT-string with date-functions + * @return boolean Result + */ + public function checkAchievementConditionDate($select) + { + $data = $this->db->query( + 'SELECT ('.$select.') AS got ' + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get all Character conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Character conditions + */ + public function getAchievementConditionsCharacter($achievementId) + { + return $this->db->query( + 'SELECT field, value '. + 'FROM achievementconditions_character '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionCharacter($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT ($field >= $value) AS got ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return ($data[0]['got'] == 1); + } + + + return false; + } + + + /** + * Get the progress for a Character condition. + * + * @param string $field Field to check + * @param int $value The value the field has to match + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionCharacterProgress($field, $value, $characterId) + { + $data = $this->db->query( + "SELECT $field AS field ". + 'FROM v_characters '. + 'WHERE user_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['field'] / $value; + } + + + return 0; + } + + + /** + * Get all Quest conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Quest conditions + */ + public function getAchievementConditionsQuest($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, quest_id, status, groupby '. + 'FROM achievementconditions_quest '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionQuest($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Quest condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param int $status Status of Quest or NULL + * @param string $groupby Field to group or NULL + * @param int $questId ID of related Quest or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionQuestProgress($field, $count, $value, $status, $groupby, $questId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM quests_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($questId) ? " AND quest_id = $questId" : ''). + (!is_null($status) ? " AND status = $status" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Get all Metaachievement conditions for an Achievement. + * + * @param int $achievementId ID of Achievement + * @return array Metaachievement conditions + */ + public function getAchievementConditionsAchievement($achievementId) + { + return $this->db->query( + 'SELECT field, `count`, value, meta_achievement_id, groupby '. + 'FROM achievementconditions_achievement '. + 'WHERE achievement_id = ?', + 'i', + $achievementId + ); + } + + + /** + * Check a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return boolean Result + */ + public function checkAchievementConditionAchievement($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT ('.( + $count + ? "count($field) >= $value" + : "$field = $value" + ). ') AS got '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) { + foreach($data as &$datum) { + if($datum['got'] == 1) { + return true; + } + } + } + + + return false; + } + + + /** + * Get the progress for a Metaachievement condition. + * + * @param string $field Field to check + * @param boolean $count Conut field-value + * @param int $value The value the field has to match + * @param string $groupby Field to group or NULL + * @param int $metaAchievementId ID of related Achievement or NULL + * @param int $characterId ID of Character + * @return float Percentage progress + */ + public function getAchievementConditionAchievementProgress($field, $count, $value, $groupby, $metaAchievementId, $characterId) + { + $data = $this->db->query( + 'SELECT '.( + $count + ? "count($field)" + : "$field" + ). ' AS field '. + 'FROM achievements_characters '. + 'WHERE '. + 'character_id = ?'. + (!is_null($metaAchievementId) ? " AND achievement_id = $metaAchievementId" : ''). + (!is_null($groupby) ? " GROUP BY $groupby" : ''), + 'i', + $characterId + ); + if(!empty($data)) + { + $maxField = 0; + foreach($data as &$datum) { + $maxField = max($maxField, intval($datum['field'])); + } + + return $maxField / $value; + } + + + return 0; + } + + + /** + * Set an Achievement as achieved for a Character. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + */ + public function setAchievementAchieved($achievementId, $characterId) + { + $this->db->query( + 'INSERT INTO achievements_characters '. + '(achievement_id, character_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $achievementId, $characterId + ); + } + + + /** + * Check if a Character has achieved an Achievement. + * + * @param int $achievementId ID of Achievement + * @param int $characterId ID of Character + * @return boolean Whether Character has achieved the Achievement or not + */ + public function hasCharacterAchievedAchievement($achievementId, $characterId) + { + $data = $this->db->query( + 'SELECT achievement_id, character_id, created '. + 'FROM achievements_characters '. + 'WHERE achievement_id = ? AND character_id = ?', + 'ii', + $achievementId, $characterId + ); + if(!empty($data)) { + return $data[0]; + } + + + return false; + } + + } + +?> diff --git a/models/AvatarsModel.inc b/models/AvatarsModel.inc new file mode 100644 index 00000000..9c3d3701 --- /dev/null +++ b/models/AvatarsModel.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\models; + + + /** + * Model to interact with Avatars-tables. + * + * @author Oliver Hanraths + */ + class AvatarsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new AvatarsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get an Avatar by its ID + * + * @param int $avatarId ID of Avatar + * @return array Avatar data + */ + public function getAvatarById($avatarId) + { + $data = $this->db->query( + 'SELECT id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'WHERE id = ?', + 'i', + $avatarId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get an Avatar by its Character type and XP-level. + * + * @param int $seminaryId ID of Seminary + * @param string $charactertypeUrl URL-title of Character type + * @param int $xplevel XP-level + * @return array Avatar data + */ + public function getAvatarByTypeAndLevel($seminaryId, $charactertypeUrl, $xplevel) + { + $data = $this->db->query( + 'SELECT avatars.id, charactertype_id, xplevel_id, avatarpicture_id, small_avatarpicture_id '. + 'FROM avatars '. + 'INNER JOIN charactertypes ON charactertypes.id = avatars.charactertype_id '. + 'INNER JOIN xplevels ON xplevels.id = avatars.xplevel_id AND xplevels.seminary_id = charactertypes.seminary_id '. + 'WHERE charactertypes.seminary_id = ? AND charactertypes.url = ? AND xplevels.level = ?', + 'isi', + $seminaryId, + $charactertypeUrl, + $xplevel + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($charactertypeUrl); + } + + + return $data[0]; + } + + } + +?> diff --git a/models/CharactergroupsModel.inc b/models/CharactergroupsModel.inc new file mode 100644 index 00000000..e042fd10 --- /dev/null +++ b/models/CharactergroupsModel.inc @@ -0,0 +1,197 @@ + + * @copyright 2014 Heinrich-Heine-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, preferred '. + '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, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $groupsgroupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupUrl); + } + + + return $data[0]; + } + + + /** + * Get a Character groups-group by its ID. + * + * @throws IdNotFoundException + * @param string $groupsgroupId ID of the Character groups-group + * @return array Character groups-group data + */ + public function getGroupsgroupById($groupsgroupId) + { + $data = $this->db->query( + 'SELECT id, name, url, preferred '. + 'FROM charactergroupsgroups '. + 'WHERE id = ?', + 'i', + $groupsgroupId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupsgroupId); + } + + + 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, motto '. + '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, motto '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND url = ?', + 'is', + $groupsgroupId, $groupUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($groupUrl); + } + + + return $data[0]; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($groupsgroupId, $xps) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM v_charactergroups '. + 'WHERE charactergroupsgroup_id = ? AND xps > ?', + 'id', + $groupsgroupId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + } + +?> diff --git a/models/CharactergroupsquestsModel.inc b/models/CharactergroupsquestsModel.inc new file mode 100644 index 00000000..4490168d --- /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, xps '. + '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..b3eac8b7 --- /dev/null +++ b/models/CharactersModel.inc @@ -0,0 +1,475 @@ + + * @copyright 2014 Heinrich-Heine-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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url, seminaries.id AS seminary_id, seminaries.url 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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_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, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE characters.id = ?', + 'i', + $characterId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($characterUrl); + } + + + return $data[0]; + } + + + /** + * Get Characters with the most amount of Achievements. + * + * @param int $seminaryId ID of Seminary + * @param int $conut Amount of Characters to retrieve + * @return array List of Characters + */ + public function getCharactersWithMostAchievements($seminaryId, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.user_id, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url, count(DISTINCT achievement_id) AS c '. + 'FROM achievements_characters '. + 'LEFT JOIN achievements ON achievements.id = achievements_characters.achievement_id '. + 'LEFT JOIN v_characters AS characters ON characters.id = achievements_characters.character_id '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE achievements.seminary_id = ? '. + 'GROUP BY character_id '. + 'ORDER BY count(DISTINCT achievement_id) DESC '. + 'LIMIT ?', + 'ii', + $seminaryId, + $count + ); + } + + + /** + * Calculate only XPs for a Character achieved through Quests. + * + * @param int $characterId ID of Character + * @return int Quest-XPs for Character + */ + public function getQuestXPsOfCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quest_xps '. + 'FROM v_charactersxps '. + 'WHERE character_id = ?', + 'i', + $characterId + ); + if(!empty($data)) { + return $data[0]['quest_xps']; + } + + + return 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; + } + + + /** + * Get the rank of a XP-value of a Character. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value to get rank for + * @return int Rank of XP-value + */ + public function getXPRank($seminaryId, $xps) + { + $data = $this->db->query( + 'SELECT count(v_characters.id) AS c '. + 'FROM charactertypes '. + 'INNER JOIN v_characters ON v_characters.charactertype_id = charactertypes.id '. + 'WHERE seminary_id = ? AND v_characters.xps > ?', + 'id', + $seminaryId, $xps + ); + if(!empty($data)) { + return $data[0]['c'] + 1; + } + + + return 1; + } + + + /** + * Get the superior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of superior Characters + */ + public function getSuperiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps > ? '. + 'ORDER BY characters.xps ASC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * Get the inferior $count Characters in the ranking. + * + * @param int $seminaryId ID of Seminary + * @param int $xps XP-value of Character + * @param int $count Count of Characters to determine + * @return array List of inferior Characters + */ + public function getInferiorCharacters($seminaryId, $xps, $count) + { + return $this->db->query( + 'SELECT characters.id, characters.created, characters.charactertype_id, characters.name, characters.url, characters.xps, characters.xplevel, characters.avatar_id, charactertypes.name AS charactertype_name, charactertypes.url AS charactertype_url '. + 'FROM v_characters AS characters '. + 'INNER JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE charactertypes.seminary_id = ? AND characters.xps < ? '. + 'ORDER BY characters.xps DESC '. + 'LIMIT ?', + 'idd', + $seminaryId, $xps, $count + ); + } + + + /** + * 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 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 charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'ii', + $questId, + QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that did not solv 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 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 charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiii', + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Get Characters that sent a submission for a Quest. + * + * @param int $questId ID of Quest to get Characters for + * @return array Characters data + */ + public function getCharactersSubmittedQuest($questId) + { + return $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 charactertype_url '. + 'FROM v_characters AS characters '. + 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. + 'WHERE EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ') AND NOT EXISTS ('. + 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. + ')', + 'iiiiii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED, + $questId, QuestsModel::QUEST_STATUS_UNSOLVED, + $questId, QuestsModel::QUEST_STATUS_SOLVED + ); + } + + + /** + * Check if a Character name already exists. + * + * @param string $name Character name to check + * @return boolean Whether Character name exists or not + */ + public function characterNameExists($name) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM characters '. + 'WHERE name = ? OR url = ?', + 'ss', + $name, + \nre\core\Linker::createLinkParam($name) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Create a new Character. + * + * @param int $userId User-ID that creates the new character + * @param int $charactertypeId ID of type of new Character + * @param string $characterName Name for the new Character + * @return int ID of Character + */ + public function createCharacter($userId, $charactertypeId, $characterName) + { + $this->db->query( + 'INSERT INTO characters '. + '(user_id, charactertype_id, name, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $charactertypeId, + $characterName, + \nre\core\Linker::createLinkParam($characterName) + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the value of a Seminary field for a Character. + * + * @param int $characterId ID of Character + * @param int $seminarycharacterfieldId ID of seminarycharacterfield to set value of + * @param string $value Value to set + */ + public function setSeminaryFieldOfCharacter($characterId, $seminarycharacterfieldId, $value) + { + $this->db->query( + 'INSERT INTO characters_seminarycharacterfields '. + '(character_id, seminarycharacterfield_id, value) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'value = ?', + 'iiss', + $characterId, + $seminarycharacterfieldId, + $value, + $value + ); + } + + } + +?> diff --git a/models/CharactertypesModel.inc b/models/CharactertypesModel.inc new file mode 100644 index 00000000..07c8d5e9 --- /dev/null +++ b/models/CharactertypesModel.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\models; + + + /** + * Model to interact with Charactertypes-table. + * + * @author Oliver Hanraths + */ + class CharactertypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new CharactertypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character types of a Seminary. + * + * @param int $seminaryId ID of Seminary to get types of + * @return array Character types + */ + public function getCharacterTypesForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, name, url '. + 'FROM charactertypes '. + 'WHERE seminary_id = ? '. + 'ORDER BY name ASC', + 'i', + $seminaryId + ); + } + + } + +?> 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..031df753 --- /dev/null +++ b/models/MediaModel.inc @@ -0,0 +1,195 @@ + + * @copyright 2014 Heinrich-Heine-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 string $mediaURL URL-name of the Medium + * @return array Medium data + */ + public function getMediaByUrl($mediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM media '. + 'WHERE 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 $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 id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its URL. + * + * @throws IdNotFoundException + * @param int $seminaryId ID of the seminary + * @param string $seminaryMediaUrl URL-name of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaByUrl($seminaryId, $seminaryMediaUrl) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE url = ?', + 's', + $seminaryMediaUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($seminaryMediaUrl); + } + + + return $data[0]; + } + + + /** + * Get a Seminary medium by its ID. + * + * @throws IdNotFoundException + * @param int $seminaryMediaId ID of the Seminary medium + * @return array Seminary medium data + */ + public function getSeminaryMediaById($mediaId) + { + $data = $this->db->query( + 'SELECT id, name, url, description, mimetype '. + 'FROM seminarymedia '. + 'WHERE id = ?', + 'i', + $mediaId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($mediaId); + } + + + return $data[0]; + } + + + /** + * Create a new Questsmedia by creating a new Seminarymedia and + * adding it to the list of Questsmedia. + * TODO Currently only temporary for easier data import. + */ + public function createQuestMedia($userId, $seminaryId, $filename, $description, $mimetype, $tmpFilename) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + $this->db->query( + 'INSERT INTO seminarymedia '. + '(created_user_id, seminary_id, name, url, description, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?, ?, ?)', + 'iissss', + $userId, + $seminaryId, + $filename, + \nre\core\Linker::createLinkParam($filename), + $description, + $mimetype + ); + $uploadId = $this->db->getInsertId(); + + $this->db->query( + 'INSERT INTO questsmedia '. + '(media_id, created_user_id) '. + 'VALUES '. + '(?, ?)', + 'ii', + $uploadId, + $userId + ); + + // Create filename + $filename = ROOT.DS.'seminarymedia'.DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + } + +?> diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc new file mode 100644 index 00000000..26ae9a10 --- /dev/null +++ b/models/QuestgroupsModel.inc @@ -0,0 +1,635 @@ + + * @copyright 2014 Heinrich-Heine-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', 'questtexts'); + + + + + /** + * 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 questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id IS NULL '. + 'ORDER BY questgroups_questgroupshierarchy.pos ASC', + 'i', + $hierarchyId + ); + } + else + { + $questgroups = $this->db->query( + 'SELECT questgroups.id, questgroups_questgroupshierarchy.questgroupshierarchy_id, questgroups_questgroupshierarchy.pos, questgroups.title, questgroups.url, questgroups.questgroupspicture_id '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE questgroups_questgroupshierarchy.questgroupshierarchy_id = ? AND questgroups_questgroupshierarchy.parent_questgroup_id = ? '. + 'ORDER BY questgroups_questgroupshierarchy.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 all Questgroups for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questgroups + */ + public function getQuestgroupsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questgroups '. + 'WHERE seminary_id = ? '. + 'ORDER BY title ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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, 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 id, title, url, questgroupspicture_id '. + 'FROM questgroups '. + 'WHERE seminary_id = ? AND 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $nextQuestgroup = $this->_getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($nextQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $nextQuestgroup = $this->getNextQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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); + $currentQuestgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($currentQuestgroup['id']); + if(empty($currentQuestgroup['hierarchy'])) { + return null; + } + + $previousQuestgroup = $this->_getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id'], $currentQuestgroup['hierarchy']['questgroup_pos']); + if(is_null($previousQuestgroup) && !is_null($currentQuestgroup['hierarchy']['parent_questgroup_id'])) { + $previousQuestgroup = $this->getPreviousQuestgroup($currentQuestgroup['hierarchy']['parent_questgroup_id']); + } + + + 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) + { + // Get data of Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Chack all Quests + $currentQuest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($currentQuest)) + { + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + + // Get next Quests + while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + { + // Get choosed Quest + $currentQuest = null; + foreach($nextQuests as &$nextQuest) { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) { + $currentQuest = $nextQuest; + } + } + + // Check Quest + if(is_null($currentQuest)) { + return false; + } + + // Check status + if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { + return false; + } + } + } + + // Check all child Questgroups + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroup['hierarchy'])) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroup['hierarchy']['id']); + foreach($childQuestgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); + foreach($questgroups as &$group) { + if(!$this->hasCharacterSolvedQuestgroup($group['id'], $characterId)) { + return false; + } + } + } + } + + + return true; + } + + + /** + * Get all related Questgroups of a Questtext. + * + * @param int $questtextId ID of the questtext + * @return array Sidequests for the questtext + */ + public function getRelatedQuestsgroupsOfQuesttext($questtextId) + { + return $this->db->query( + 'SELECT questgroups.id, questgroups_questtexts.questtext_id, questgroups.title, questgroups.url, questgroups_questtexts.entry_text '. + 'FROM questgroups_questtexts '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questtexts.questgroup_id '. + 'WHERE questgroups_questtexts.questtext_id = ?', + 'i', + $questtextId + ); + } + + + /** + * Get all related Questgroups of a Quest. + * + * @param int $questId ID of the quest + * @return array Sidequests for the quest + */ + public function getRelatedQuestsgroupsOfQuest($questId) + { + $questgroups = array(); + $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); + foreach($questtexts as &$questtext) { + $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); + } + + + return $questgroups; + } + + + /** + * Summarize XPs of all Quests for a Questgroup and its + * sub-Questgroups. + * + * @param int $questgroupId ID of Questgroup + * @return int Sum of XPs + */ + public function getAchievableXPsForQuestgroup($questgroupId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievableXPsForQuest($quest); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(!empty($questgroupHierarchy)) + { + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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 the given Quest, its following Quests and + * its related Questgroups. + * + * @param array $quest Quest to summarize XPs for + * @return int Sum of XPs + */ + public function getAchievableXPsForQuest($quest) + { + // XPs for the given Quest + $xps = $quest['xps']; + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + } + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + $nextXPs = array(0); + foreach($nextQuests as &$nextQuest) + { + $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + } + $xps += max($nextXPs); + + + 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 int Sum of XPs + */ + public function getAchievedXPsForQuestgroup($questgroupId, $characterId) + { + // Sum of XPs + $xps = 0; + + // Current Questgroup + $questgroup = $this->getQuestgroupById($questgroupId); + + // Quests of current Questgroup + $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); + if(!is_null($quest)) { + $xps += $this->getAchievedXPsForQuest($quest, $characterId); + } + + // XPs of child Questgroups + $questgroupHierarchy = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); + if(empty($questgroupHierarchy)) { + return $xps; + } + $childQuestgroupshierarchy = $this->Questgroupshierarchy->getChildQuestgroupshierarchy($questgroupHierarchy['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; + } + + + /** + * Summarize XPs of the given Quest, its following Quests and + * its related Questgroups solved by a Character. + * + * @param int $quest Quest to summarize XPs for + * @param int $characterId ID of Character + * @return int Sum of XPs + */ + public function getAchievedXPsForQuest($quest, $characterId) + { + $xps = 0; + + // XPs for the given Quest + if($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) + { + $xps += $quest['xps']; + + // Next Quests + $nextQuests = $this->Quests->getNextQuests($quest['id']); + foreach($nextQuests as &$nextQuest) + { + if($this->Quests->hasCharacterEnteredQuest($nextQuest['id'], $characterId)) + { + $xps += $this->getAchievedXPsForQuest($nextQuest, $characterId); + break; + } + } + } + + // Related Questgroups + $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) { + $xps += $this->getAchievedXPsForQuestgroup($relatedQuestgroup['id'], $characterId); + } + + + // Return summarized XPs + return $xps; + } + + + /** + * Create a new Questgroup. + * + * @param int $userId User-ID that creates the new character + * @param int $seminaryId ID of Seminary + * @param string $title Title for new Questgroup + * @return int ID of new Questgroup + */ + public function createQuestgroup($userId, $seminaryId, $title) + { + $this->db->query( + 'INSERT INTO questgroups '. + '(created_user_id, seminary_id, title, url) '. + 'VALUES '. + '(?, ?, ?, ?)', + 'iiss', + $userId, + $seminaryId, + $title, + \nre\core\Linker::createLinkParam($title) + ); + + + return $this->db->getInsertId(); + } + + + + + /** + * 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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? + 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + 'WHERE parent_questgroup_id = ? AND pos = ? - 1', + 'ii', + $parentQuestgroupId, $questgroupPos + ); + } + else + { + $data = $this->db->query( + 'SELECT * '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroups ON questgroups.id = questgroups_questgroupshierarchy.questgroup_id '. + '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..c1dae116 --- /dev/null +++ b/models/QuestgroupshierarchyModel.inc @@ -0,0 +1,128 @@ + + * @copyright 2014 Heinrich-Heine-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 getHierarchyOfSeminary($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 Questgroup-Hierarchy for a Questgroup. + * + * @param int $questgroupId ID of Questgroup + * @return array Hierarchy for Questgroup + */ + public function getHierarchyForQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT questgroups_questgroupshierarchy.parent_questgroup_id, questgroups_questgroupshierarchy.pos AS questgroup_pos, questgroupshierarchy.id, questgroupshierarchy.seminary_id, questgroupshierarchy.parent_questgroupshierarchy_id, questgroupshierarchy.pos, questgroupshierarchy.title_singular, questgroupshierarchy.title_plural, questgroupshierarchy.url '. + 'FROM questgroups_questgroupshierarchy '. + 'INNER JOIN questgroupshierarchy ON questgroupshierarchy.id = questgroups_questgroupshierarchy.questgroupshierarchy_id '. + 'WHERE questgroups_questgroupshierarchy.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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..ffffb536 --- /dev/null +++ b/models/QuestsModel.inc @@ -0,0 +1,460 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Quest-status: Entered + * + * @var int; + */ + const QUEST_STATUS_ENTERED = 0; + /** + * Quest-status: submitted + * + * @var int; + */ + const QUEST_STATUS_SUBMITTED = 1; + /** + * Quest-status: Unsolved + * + * @var int; + */ + const QUEST_STATUS_UNSOLVED = 2; + /** + * Quest-status: Solved + * + * @var int; + */ + const QUEST_STATUS_SOLVED = 3; + + + + + /** + * Construct a new QuestsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE questgroups.seminary_id = ? AND questgroups.id = ? AND quests.url = ?', + 'iis', + $seminaryId, $questgroupId, $questUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questUrl); + } + + + 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.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests '. + 'LEFT JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Quest of a Qusetgroup. + * + * @param int $questId ID of Questgroup + * @return array Data of first Quest + */ + public function getFirstQuestOfQuestgroup($questgroupId) + { + $data = $this->db->query( + 'SELECT id, questtype_id, title, url, xps, task '. + 'FROM quests '. + 'LEFT JOIN quests_previousquests ON quests_previousquests.quest_id = quests.id '. + 'WHERE questgroup_id = ? AND quests_previousquests.previous_quest_id IS NULL', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * 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.questtype_id, quests.title, quests.url, quests.xps, quests.entry_text, quests.task, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.previous_quest_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, quests.entry_text, questgroups.title AS questgroup_title, questgroups.url AS questgroup_url '. + 'FROM quests_previousquests '. + 'INNER JOIN quests ON quests.id = quests_previousquests.previous_quest_id '. + 'INNER JOIN questgroups ON questgroups.id = quests.questgroup_id '. + 'WHERE quests_previousquests.quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Mark a Quest as entered for a Character. + * + * @param int $questId ID of Quest to mark as entered + * @param int $characterId ID of Character that entered the Quest + */ + public function setQuestEntered($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_ENTERED, false); + } + + + /** + * Mark a Quest as submitted 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 setQuestSubmitted($questId, $characterId) + { + $this->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SUBMITTED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_UNSOLVED); + } + + + /** + * 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->setQuestStatus($questId, $characterId, self::QUEST_STATUS_SOLVED, false); + } + + + /** + * Determine if the given Character has entered a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has entered the Quest or not + */ + public function hasCharacterEnteredQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?,?)', + 'iiiii', + $questId, + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Determine if the given Character has tried to solve a Quest. + * + * @param int $questId ID of Quest to check + * @param int $characterId ID of Character to check + * @result boolean Whether Character has tried to solved the Quest or not + */ + public function hasCharacterTriedQuest($questId, $characterId) + { + $count = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status IN (?,?)', + 'iiii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED, self::QUEST_STATUS_UNSOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * 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 = ?', + 'iii', + $questId, + $characterId, + self::QUEST_STATUS_SOLVED + ); + + + return (!empty($count) && intval($count[0]['c']) > 0); + } + + + /** + * Get the last Quests for a Character. + * + * @param int $characterId ID of Character + * @retrun array Quest data + */ + public function getLastQuestForCharacter($characterId) + { + $data = $this->db->query( + 'SELECT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_characters '. + 'LEFT JOIN quests ON quests.id = quests_characters.quest_id '. + 'WHERE quests_characters.character_id = ? AND quests_characters.status IN (?, ?, ?) '. + 'ORDER BY quests_characters.created desc '. + 'LIMIT 1', + 'iiii', + $characterId, + self::QUEST_STATUS_ENTERED, self::QUEST_STATUS_SUBMITTED, self::QUEST_STATUS_SOLVED + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all Quests for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array Quests for this Seminary + */ + public function getQuestsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM questgroups '. + 'INNER JOIN quests ON quests.questgroup_id = questgroups.id '. + 'WHERE questgroups.seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get all Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return array Quests for this Questtopic + */ + public function getQuestsForQuesttopic($questtopicId) + { + return $this->db->query( + 'SELECT DISTINCT quests.id, quests.questgroup_id, quests.questtype_id, quests.title, quests.url, quests.xps, quests.task, quests.wrong_text, quests.questsmedia_id '. + 'FROM quests_questsubtopics '. + 'INNER JOIN quests ON quests.id = quests_questsubtopics.quest_id '. + 'WHERE quests_questsubtopics.questsubtopic_id = ?', + 'i', + $questtopicId + ); + } + + + + + /** + * Mark a Quest for a Character. + * + * @param int $questId ID of Quest to mark + * @param int $characterId ID of Character to mark the Quest for + * @param int $status Quest status to mark + * @param boolean $repeated Insert although status is already set for this Quest and Character + */ + private function setQuestStatus($questId, $characterId, $status, $repeated=true) + { + // Check if status is already set + if(!$repeated) + { + $count = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? AND status = ?', + 'iii', + $questId, + $characterId, + $status + ); + if(!empty($count) && intval($count[0]['c']) > 0) { + return; + } + } + + // Set status + $this->db->query( + 'INSERT INTO quests_characters '. + '(quest_id, character_id, status) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, + $characterId, + $status + ); + } + + + /** + * Create a new Quest. + * + * @param int $userId User-ID that creates the new character + * @param string $name Name for new Quest + * @param int $questgroupId ID of Questgroup + * @param int $questtypeId ID of Questtype + * @param int $xps XPs for new Quest + * @param string $entrytext Entrytext for new Quest + * @param string $wrongtext Wrongtext for new Quest + * @param string $task Task for new Quest + * @return int ID of new Quest + */ + public function createQuest($userId, $name, $questgroupId, $questtypeId, $xps, $entrytext, $wrongtext, $task) + { + $this->db->query( + 'INSERT INTO quests '. + '(created_user_id, questgroup_id, questtype_id, title, url, xps, entry_text, wrong_text, task) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?, ?, ?, ?)', + 'iiississs', + $userId, $questgroupId, $questtypeId, + $name, \nre\core\Linker::createLinkParam($name), + $xps, $entrytext, $wrongtext, $task + ); + + + return $this->db->getInsertId(); + } + + + /** + * Set the media for a Quest. + * + * @param int $questId ID of Quest to set media for + * @param int $questmediaId ID of Questsmedia to set + */ + public function setQuestmedia($questId, $questsmediaId) + { + $this->db->query( + 'UPDATE quests '. + 'SET questsmedia_id = ? '. + 'WHERE id = ?', + 'ii', + $questsmediaId, + $questId + ); + } + + } + +?> diff --git a/models/QuesttextsModel.inc b/models/QuesttextsModel.inc new file mode 100644 index 00000000..7aca28b9 --- /dev/null +++ b/models/QuesttextsModel.inc @@ -0,0 +1,220 @@ + + * @copyright 2014 Heinrich-Heine-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 all Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return array All Questtexts for a Quest + */ + public function getQuesttextsOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + return $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 = ?', + 'i', + $questId + ); + } + else + { + return $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 = ?', + 'is', + $questId, + $questtexttypeUrl + ); + } + } + + + /** + * Get count of Questtexts for a Quest. + * + * @param int $questId ID of the Quest + * @param string $questtexttypeUrl URL of the Questtexttype + * @return int Amount of Questtexts for a Quest + */ + public function getQuesttextCountOfQuest($questId, $questtexttypeUrl=null) + { + if(is_null($questtexttypeUrl)) + { + $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 = ?', + 'i', + $questId + ); + } + else + { + $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, + $questtexttypeUrl + ); + } + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 getRelatedQuesttextForQuestgroup($questgroupId) + { + $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 questgroups_questtexts '. + 'LEFT JOIN questtexts ON questtexts.id = questgroups_questtexts.questtext_id '. + 'LEFT JOIN questtexttypes ON questtexttypes.id = questtexts.questtexttype_id '. + 'WHERE questgroups_questtexts.questgroup_id = ?', + 'i', + $questgroupId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all registered Questtexttypes. + * + * @return array Registered Questtexttypes + */ + public function getQuesttexttypes() + { + return $this->db->query( + 'SELECT id, type, url '. + 'FROM questtexttypes' + ); + } + + + /** + * Get a Questtexttype by its URL. + * + * @param string $questtexttypeUrl URL-type of Questtexttype + * @return array Questtexttype data + */ + public function getQuesttexttypeByUrl($questtexttypeUrl) + { + $data = $this->db->query( + 'SELECT id, type, url '. + 'FROM questtexttypes '. + 'WHERE url = ?', + 's', + $questtexttypeUrl + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Add a list of Questtexts to a Quest. + * + * @param int $userId ID of user + * @param int $questId ID of Quest to add texts to + * @param string $questtexttypeUrl URL-type of Questtexttype of texts + * @param array $texts List of texts to add. + */ + public function addQuesttextsToQuest($userId, $questId, $questtexttypeUrl, $texts) + { + $questtexttype = $this->getQuesttexttypeByUrl($questtexttypeUrl); + if(is_null($questtexttype)) { + return; + } + foreach($texts as &$text) + { + $pos = $this->db->query( + 'SELECT COALESCE(MAX(pos),0)+1 AS pos '. + 'FROM questtexts '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + $pos = $pos[0]['pos']; + + $this->db->query( + 'INSERT INTO questtexts '. + '(created_user_id, quest_id, questtexttype_id, pos, text) '. + 'VALUES '. + '(?, ?, ?, ?, ?)', + 'iiiis', + $userId, $questId, $questtexttype['id'], $pos, + $text + ); + } + } + + + + + } + +?> diff --git a/models/QuesttopicsModel.inc b/models/QuesttopicsModel.inc new file mode 100644 index 00000000..abd246f7 --- /dev/null +++ b/models/QuesttopicsModel.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\models; + + + /** + * Model to interact with Questtopics-table. + * + * @author Oliver Hanraths + */ + class QuesttopicsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttopicsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get a Questtopic by its URL. + * + * @param int $seminaryId ID of Seminary + * @param string $questtopicUrl URL-Title of Questtopic + * @return array Questtopic data + */ + public function getQuesttopicByUrl($seminaryId, $questtopicUrl) + { + $data = $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, $questtopicUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questtopicUrl); + } + + + return $data[0]; + } + + + /** + * Get all Questtopics for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return array List of Questtopics + */ + public function getQuesttopicsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT id, title, url '. + 'FROM questtopics '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + + /** + * Get count of Quests that are linked to a Questtopic. + * + * @param int $questtopicId ID of Questtopic + * @return int Count of Quests + */ + public function getQuestCountForQuesttopic($questtopicId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_questsubtopics.quest_id) AS c ' . + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'WHERE questsubtopics.questtopic_id = ?', + 'i', + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get count of Quests that are linked to a Questtopic and are + * unlocked by a Character. + * + * @param int $questtopicId ID of Questtopic + * @param int $characterId ID of Character + * @return int Count of Quests + */ + public function getCharacterQuestCountForQuesttopic($questtopicId, $characterId) + { + $data = $this->db->query( + 'SELECT count(DISTINCT quests_characters.quest_id) AS c '. + 'FROM questsubtopics '. + 'LEFT JOIN quests_questsubtopics ON quests_questsubtopics.questsubtopic_id = questsubtopics.id '. + 'INNER JOIN quests_characters ON quests_characters.quest_id = quests_questsubtopics.quest_id AND quests_characters.character_id = ? AND quests_characters.status = 3 '. + 'WHERE questsubtopics.questtopic_id = ?', + 'ii', + $characterId, + $questtopicId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * Get all Questsubtopics for a Quest. + * + * @param int $questId ID of Quest + * @return array List of Questsubtopics + */ + public function getQuestsubtopicsForQuest($questId) + { + return $this->db->query( + 'SELECT DISTINCT id, questtopic_id, title, url '. + 'FROM quests_questsubtopics '. + 'INNER JOIN questsubtopics ON questsubtopics.id = quests_questsubtopics.questsubtopic_id '. + 'WHERE quests_questsubtopics.quest_id = ?', + 'i', + $questId + ); + } + + } + +?> diff --git a/models/QuesttypesModel.inc b/models/QuesttypesModel.inc new file mode 100644 index 00000000..58b36648 --- /dev/null +++ b/models/QuesttypesModel.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 Questtypes-table. + * + * @author Oliver Hanraths + */ + class QuesttypesModel extends \hhu\z\Model + { + + + + + /** + * Construct a new QuesttypesModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all registered Questtypes. + * + * @return array List of registered Questtypes + */ + public function getQuesttypes() + { + return $this->db->query( + 'SELECT id, title, url, classname '. + 'FROM questtypes '. + 'ORDER BY title ASC' + ); + } + + + /** + * 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..554f3d10 --- /dev/null +++ b/models/SeminariesModel.inc @@ -0,0 +1,193 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('questgroupshierarchy', 'questgroups'); + + + + + /** + * 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, course, description, seminarymedia_id '. + '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, course, description, seminarymedia_id '. + '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, course, description, seminarymedia_id '. + 'FROM seminaries '. + 'WHERE url = ?', + 's', + $seminaryUrl + ); + if(empty($seminary)) { + throw new \nre\exceptions\IdNotFoundException($seminaryUrl); + } + + + return $seminary[0]; + } + + + /* + * Calculate sum of XPs for a Seminary. + * + * @param int $seminaryId ID of Seminary + * @return int Total sum of XPs + */ + public function getTotalXPs($seminaryId) + { + $xps = 0; + + // Questgroups + $questgroupshierarchy = $this->Questgroupshierarchy->getHierarchyOfSeminary($seminaryId); + foreach($questgroupshierarchy as &$hierarchy) + { + // Get Questgroups + $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); + foreach($questgroups as &$questgroup) { + $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + } + } + + + return $xps; + } + + + /** + * 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..efad3bde --- /dev/null +++ b/models/SeminarycharacterfieldsModel.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 the Seminarycharacterfields-tables. + * + * @author Oliver Hanraths + */ + class SeminarycharacterfieldsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new SeminarycharacterfieldsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Get all Character fields of a Seminary. + * + * @param int $seminaryId ID of Seminary to get fields of + * @param array Seminary Character fields + */ + public function getFieldsForSeminary($seminaryId) + { + return $this->db->query( + 'SELECT seminarycharacterfields.id, seminarycharacterfields.title, seminarycharacterfields.url, seminarycharacterfields.regex, seminarycharacterfields.required, seminarycharacterfieldtypes.id AS type_id, seminarycharacterfieldtypes.title AS type_title, seminarycharacterfieldtypes.url AS type_url '. + 'FROM seminarycharacterfields '. + 'LEFT JOIN seminarycharacterfieldtypes ON seminarycharacterfieldtypes.id = seminarycharacterfields.seminarycharacterfieldtype_id '. + 'WHERE seminarycharacterfields.seminary_id = ? '. + 'ORDER BY pos ASC', + 'i', + $seminaryId + ); + } + + + /** + * 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/UploadsModel.inc b/models/UploadsModel.inc new file mode 100644 index 00000000..078b1ed4 --- /dev/null +++ b/models/UploadsModel.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\models; + + + /** + * Model to handle files to upload. + * + * @author Oliver Hanraths + */ + class UploadsModel extends \hhu\z\Model + { + + + + + /** + * Construct a new UploadsModel. + */ + public function __construct() + { + parent::__construct(); + } + + + + + /** + * Upload a file and create a database record. + * + * @param int $userId ID of user that uploads the file + * @param string $filename Name of file to upload + * @param string $tmpFilename Name of temporary uploaded file + * @param string $mimetype Mimetype of file to upload + * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one + * @return mixed ID of database record or false + */ + public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + { + $uploadId = false; + $this->db->setAutocommit(false); + + try { + // Create database record + if(is_null($seminaryId)) + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?)', + 'isss', + $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + else + { + $this->db->query( + 'INSERT INTO uploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ?, ? ,? ,?)', + 'iisss', + $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype + ); + } + $uploadId = $this->db->getInsertId(); + + // Create filename + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + if(!move_uploaded_file($tmpFilename, $filename)) + { + $this->db->rollback(); + $uploadId = false; + } + } + catch(\nre\exceptions\DatamodelException $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + } + + + $this->db->setAutocommit(true); + return $uploadId; + } + + + /** + * Get an upload by its ID. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadById($uploadId) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE id = ?', + 'i', + $uploadId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadId); + } + + + return $data[0]; + } + + + /** + * Get an upload by its URL. + * + * @throws IdNotFoundException + * @param int $uploadId ID of the uploaded file + * @return array Upload data + */ + public function getUploadByUrl($uploadUrl) + { + $data = $this->db->query( + 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. + 'FROM uploads '. + 'WHERE url = ?', + 's', + $uploadUrl + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($uploadUrl); + } + + + return $data[0]; + } + + } + +?> 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..c773a0b5 --- /dev/null +++ b/models/UsersModel.inc @@ -0,0 +1,370 @@ + + * @copyright 2014 Heinrich-Heine-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 users with the given user role. + * + * @param string $userrole User role + * @return array List of users + */ + public function getUsersWithRole($userrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + 'FROM users '. + 'LEFT JOIN users_userroles ON users_userroles.user_id = users.id '. + 'LEFT JOIN userroles ON userroles.id = users_userroles.userrole_id '. + 'WHERE userroles.name = ? '. + 'ORDER BY username ASC', + 's', + $userrole + ); + } + + + /** + * Get users with the given user Seminary role. + * + * @param int $seminaryId ID of Seminary + * @param string $userseminaryrole User Seminary role + * @return array List of users + */ + public function getUsersWithSeminaryRole($seminaryId, $userseminaryrole) + { + return $this->db->query( + 'SELECT users.id, users.created, users.username, users.url, users.surname, users.prename, users.email '. + '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_userseminaryroles.seminary_id = ? AND userseminaryroles.name = ? '. + 'ORDER BY username ASC', + 'is', + $seminaryId, $userseminaryrole + ); + } + + + /** + * 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; + } + + + /** + * Check if an username already exists. + * + * @param string $username Username to check + * @return boolean Whether username exists or not + */ + public function usernameExists($username) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE username = ? OR url = ?', + 'ss', + $username, + \nre\core\Linker::createLinkParam($username) + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Check if an e‑mail address already exists. + * + * @param string $email E‑mail address to check + * @return boolean Whether e‑mail address exists or not + */ + public function emailExists($email) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM users '. + 'WHERE email = ?', + 's', + $email + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $userId = null; + $this->db->setAutocommit(false); + try { + // Create user + $this->db->query( + 'INSERT INTO users '. + '(username, url, surname, prename, email, password) '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?)', + 'ssssss', + $username, + \nre\core\Linker::createLinkParam($username), + $surname, + $prename, + $email, + $this->hash($password) + ); + $userId = $this->db->getInsertId(); + + // Add role “user” + $this->db->query( + 'INSERT INTO users_userroles '. + '(user_id, userrole_id) '. + 'SELECT ?, userroles.id '. + 'FROM userroles '. + 'WHERE userroles.name = ?', + 'is', + $userId, + 'user' + ); + } + catch(Exception $e) { + $this->db->rollback(); + $this->db->setAutocommit(true); + throw $e; + } + $this->db->setAutocommit(true); + + + return $userId; + } + + + /** + * 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, $prename, $surname, $email, $password) + { + $this->db->setAutocommit(false); + try { + // Update user data + $this->db->query( + 'UPDATE users '. + 'SET username = ?, url = ?, prename = ?, surname = ?, email = ? '. + 'WHERE id = ?', + 'sssssi', + $username, + \nre\core\Linker::createLinkParam($username), + $prename, + $surname, + $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/bossfight/BossfightQuesttypeAgent.inc b/questtypes/bossfight/BossfightQuesttypeAgent.inc new file mode 100644 index 00000000..6d99ec44 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeAgent.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 a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeController.inc b/questtypes/bossfight/BossfightQuesttypeController.inc new file mode 100644 index 00000000..366aabc8 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeController.inc @@ -0,0 +1,244 @@ + + * @copyright 2014 Heinrich-Heine-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 BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeController 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) + { + // Prepare session + $this->prepareSession($quest['id']); + + // Remove previous answers + $this->Bossfight->clearCharacterSubmissions($quest['id'], $character['id']); + + // Save answers + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) { + $this->Bossfight->setCharacterSubmission($stage['id'], $character['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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + + // Prepare session + $this->prepareSession($quest['id']); + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + + return ($lives['boss'] == 0 && $lives['character'] > 0); + } + + + /** + * Action: quest. + * + * Display a stage with a text and the answers for the following + * stages. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Prepare session + $this->prepareSession($quest['id']); + + // Get Stage + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit_stages'))) + { + $stages = $this->request->getPostParam('submit_stages'); + $stageId = array_keys($stages)[0]; + $stage = $this->Bossfight->getStageById($stageId); + } + else + { + $_SESSION['quests'][$quest['id']]['stages'] = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + } + + // Store Stage in session + if(count($_SESSION['quests'][$quest['id']]['stages']) == 0 || $_SESSION['quests'][$quest['id']]['stages'][count($_SESSION['quests'][$quest['id']]['stages'])-1]['id'] != $stage['id']) { + $_SESSION['quests'][$quest['id']]['stages'][] = $stage; + } + + // Calculate lives + $lives = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + foreach($_SESSION['quests'][$quest['id']]['stages'] as &$stage) + { + $lives['character'] += $stage['livedrain_character']; + $lives['boss'] += $stage['livedrain_boss']; + } + + // Get Child-Stages + $childStages = $this->Bossfight->getChildStages($stage['id']); + + // Get answer of Character + if($this->request->getGetParam('show-answer') == 'true') { + foreach($childStages as &$childStage) { + $childStage['answer'] = $this->Bossfight->getCharacterSubmission($childStage['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stage', $stage); + $this->set('lives', $lives); + $this->set('childStages', $childStages); + } + + + /** + * Action: submission. + * + * Display all stages with the answers the character has + * choosen. + * + * @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 Boss-Fight + $fight = $this->Bossfight->getBossFight($quest['id']); + if(!is_null($fight['boss_seminarymedia_id'])) { + $fight['bossmedia'] = $this->Media->getSeminaryMediaById($fight['boss_seminarymedia_id']); + } + + // Get stages + $stages = array(); + $stage = $this->Bossfight->getFirstStage($quest['id']); + while(!is_null($stage)) + { + $stages[] = $stage; + + $childStages = $this->Bossfight->getChildStages($stage['id']); + $stage = null; + foreach($childStages as &$childStage) + { + if($this->Bossfight->getCharacterSubmission($childStage['id'], $character['id'])) + { + $stage = $childStage; + break; + } + } + } + + // Calculate lives + $stages[0]['lives'] = array( + 'character' => $fight['lives_character'], + 'boss' => $fight['lives_boss'] + ); + for($i=1; $i $stages[$i-1]['lives']['character'] + $stages[$i]['livedrain_character'], + 'boss' => $stages[$i-1]['lives']['boss'] + $stages[$i]['livedrain_boss'], + ); + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('character', $character); + $this->set('fight', $fight); + $this->set('stages', $stages); + } + + + + + /** + * Prepare the session to store stage information in + * + * @param int $questId ID of Quest + */ + private function prepareSession($questId) + { + if(!array_key_exists('quests', $_SESSION)) { + $_SESSION['quests'] = array(); + } + if(!array_key_exists($questId, $_SESSION['quests'])) { + $_SESSION['quests'][$questId] = array(); + } + if(!array_key_exists('stages', $_SESSION['quests'][$questId])) { + $_SESSION['quests'][$questId]['stages'] = array(); + } + } + + } + +?> diff --git a/questtypes/bossfight/BossfightQuesttypeModel.inc b/questtypes/bossfight/BossfightQuesttypeModel.inc new file mode 100644 index 00000000..a05927f0 --- /dev/null +++ b/questtypes/bossfight/BossfightQuesttypeModel.inc @@ -0,0 +1,183 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\questtypes; + + + /** + * Model of the BossfightQuesttypeAgent for a boss-fight. + * + * @author Oliver Hanraths + */ + class BossfightQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get a Boss-Fight. + * + * @throws IdNotFoundException + * @param int $questId ID of Quest + * @return array Boss-Fight data + */ + public function getBossFight($questId) + { + $data = $this->db->query( + 'SELECT bossname, boss_seminarymedia_id, lives_character, lives_boss '. + 'FROM questtypes_bossfight '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(empty($data)) { + throw new \nre\exceptions\IdNotFoundException($questId); + } + + + return $data[0]; + } + + + /** + * Get the first Stage to begin the Boss-Fight with. + * + * @param int $questId ID of Quest + * @return array Data of first Stage + */ + public function getFirstStage($questId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ? AND parent_stage_id IS NULL', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get a Stage by its ID. + * + * @param int $stageId ID of Stage + * @return array Stage data or null + */ + public function getStageById($stageId) + { + $data = $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE id = ?', + 'i', + $stageId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get the follow-up Stages for a Stage. + * + * @param int $parentStageId ID of Stage to get follow-up Stages for + * @return array List of follow-up Stages + */ + public function getChildStages($parentStageId) + { + return $this->db->query( + 'SELECT id, text, question, livedrain_character, livedrain_boss '. + 'FROM questtypes_bossfight_stages '. + 'WHERE parent_stage_id = ?', + 'i', + $parentStageId + ); + } + + + /** + * Reset all Character submissions of a Boss-Fight. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + */ + public function clearCharacterSubmissions($questId, $characterId) + { + $this->db->query( + 'DELETE FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id IN ('. + 'SELECT id '. + 'FROM questtypes_bossfight_stages '. + 'WHERE questtypes_bossfight_quest_id = ?'. + ') AND character_id = ?', + 'ii', + $questId, + $characterId + ); + } + + + /** + * Save Character’s submitted answer for one Boss-Fight-Stage. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + */ + public function setCharacterSubmission($stageId, $characterId) + { + $this->db->query( + 'INSERT INTO questtypes_bossfight_stages_characters '. + '(questtypes_bossfight_stage_id, character_id) '. + 'VALUES '. + '(?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_bossfight_stage_id = ?', + 'iii', + $stageId, $characterId, $stageId + ); + } + + + /** + * Get answer of one Boss-Fight-Stage submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return boolean Stage taken + */ + public function getCharacterSubmission($stageId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_bossfight_stage_id '. + 'FROM questtypes_bossfight_stages_characters '. + 'WHERE questtypes_bossfight_stage_id = ? AND character_id = ? ', + 'ii', + $stageId, $characterId + ); + + + return (!empty($data)); + } + + } + +?> diff --git a/questtypes/bossfight/html/quest.tpl b/questtypes/bossfight/html/quest.tpl new file mode 100644 index 00000000..e97569c2 --- /dev/null +++ b/questtypes/bossfight/html/quest.tpl @@ -0,0 +1,51 @@ +

                    +
                    +

                    +

                    +

                    + 0) : ?> + + + + + + +

                    +
                    +
                    +

                    +

                    +

                    + 0) : ?> + + + + + + +

                    +
                    +
                    + +

                    + +
                    + +
                      + +
                    • +

                      + + +

                      +

                      +
                    • + + +
                    • + + +
                    • + +
                    +
                    diff --git a/questtypes/bossfight/html/submission.tpl b/questtypes/bossfight/html/submission.tpl new file mode 100644 index 00000000..3696964e --- /dev/null +++ b/questtypes/bossfight/html/submission.tpl @@ -0,0 +1,38 @@ +
                    +
                    +

                    +
                    +
                    +

                    +
                    +
                    + + +

                    +
                    +
                    +

                    +

                    + 0) : ?> + + + + + + +

                    +
                    +
                    +

                    +

                    + 0) : ?> + + + + + + +

                    +
                    +
                    + diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.inc new file mode 100644 index 00000000..b418f01e --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeAgent.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 choosing between predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc new file mode 100644 index 00000000..1b7eff27 --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -0,0 +1,157 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeController 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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Save answers + foreach($choiceLists as &$list) + { + $pos = intval($list['number']) - 1; + $answer = (array_key_exists($pos, $answers)) ? $answers[$pos] : null; + $this->Choiceinput->setCharacterSubmission($list['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 lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + + // Match lists with user answers + foreach($choiceLists as $i => &$list) + { + if(!array_key_exists($i, $answers)) { + return false; + } + if($list['questtypes_choiceinput_choice_id'] != $answers[$i]) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Task + $task = $this->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + } + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($choiceLists as &$list) { + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + + /** + * 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->Choiceinput->getChoiceinputQuest($quest['id']); + + // Process text + $textParts = preg_split('/(\$\$)/', ' '.$task['text'].' '); + + // Get lists + $choiceLists = $this->Choiceinput->getChoiceinputLists($quest['id']); + foreach($choiceLists as &$list) + { + $list['values'] = $this->Choiceinput->getChoiceinputChoices($list['id']); + $list['answer'] = $this->Choiceinput->getCharacterSubmission($list['id'], $character['id']); + $list['right'] = ($list['questtypes_choiceinput_choice_id'] == $list['answer']); + } + + + // Pass data to view + $this->set('texts', $textParts); + $this->set('choiceLists', $choiceLists); + } + + } + +?> diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc new file mode 100644 index 00000000..e908600f --- /dev/null +++ b/questtypes/choiceinput/ChoiceinputQuesttypeModel.inc @@ -0,0 +1,153 @@ + + * @copyright 2014 Heinrich-Heine-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 ChoiceinputQuesttypeAgent for choosing between + * predefined input values. + * + * @author Oliver Hanraths + */ + class ChoiceinputQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get choiceinput-text for a Quest. + * + * @param int $questId ID of Quest + * @return array Choiceinput-text + */ + public function getChoiceinputQuest($questId) + { + $data = $this->db->query( + 'SELECT text '. + 'FROM questtypes_choiceinput '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all lists of input values for a choiceinput-text. + * + * @param int $questId ID of Quest + * @return array List + */ + public function getChoiceinputLists($questId) + { + return $this->db->query( + 'SELECT id, number, questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists '. + 'WHERE questtypes_choiceinput_quest_id = ? '. + 'ORDER BY number ASC', + 'i', + $questId + ); + } + + + /** + * Get the list of values for a choiceinput-list. + * + * @param int $listId ID of list + * @return array Input values + */ + public function getChoiceinputChoices($listId) + { + return $this->db->query( + 'SELECT id, pos, text '. + 'FROM questtypes_choiceinput_choices '. + 'WHERE questtypes_choiceinput_list_id = ? '. + 'ORDER BY pos ASC', + 'i', + $listId + ); + } + + + /** + * Save Character’s submitted answer for one choiceinput-list. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this list + */ + public function setCharacterSubmission($listId, $characterId, $answer) + { + if(is_null($answer)) + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, NULL) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = NULL', + 'ii', + $listId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_choiceinput_lists_characters '. + '(questtypes_choiceinput_list_id, character_id, questtypes_choiceinput_choice_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_choiceinput_choice_id = ?', + 'iiii', + $listId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get answer of one choiceinput-list submitted by Character. + * + * @param int $regexId ID of list + * @param int $characterId ID of Character + * @return int Submitted answer for this list or null + */ + public function getCharacterSubmission($listId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_choiceinput_choice_id '. + 'FROM questtypes_choiceinput_lists_characters '. + 'WHERE questtypes_choiceinput_list_id = ? AND character_id = ? ', + 'ii', + $listId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_choiceinput_choice_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/choiceinput/html/quest.tpl b/questtypes/choiceinput/html/quest.tpl new file mode 100644 index 00000000..489cbc2f --- /dev/null +++ b/questtypes/choiceinput/html/quest.tpl @@ -0,0 +1,15 @@ +
                    + &$text) : ?> + 0) : ?> + + + t($text)?> + + +

                    + +
                    diff --git a/questtypes/choiceinput/html/submission.tpl b/questtypes/choiceinput/html/submission.tpl new file mode 100644 index 00000000..40f60505 --- /dev/null +++ b/questtypes/choiceinput/html/submission.tpl @@ -0,0 +1,12 @@ +
                    + &$text) : ?> + 0) : ?> + + + t($text)?> + +
                    diff --git a/questtypes/crossword/CrosswordQuesttypeAgent.inc b/questtypes/crossword/CrosswordQuesttypeAgent.inc new file mode 100644 index 00000000..9b137fe9 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeAgent.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 solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeAgent extends \hhu\z\QuesttypeAgent + { + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc new file mode 100644 index 00000000..ba3e5a0c --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -0,0 +1,351 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeController 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Save answer + $this->Crossword->setCharacterSubmission($word['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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Iterate words + foreach($words as &$word) + { + // Assemble answer for word + $answer = ''; + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + foreach(range($startY, $endY) as $y) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + foreach(range($startX, $endX) as $x) + { + if(array_key_exists($x, $answers) && array_key_exists($y, $answers[$x]) && !empty($answers[$x][$y])) { + $answer .= $answers[$x][$y]; + } + else { + $answer .= ' '; + } + } + } + + // Check answer + if(mb_strtolower($word['word'], 'UTF-8') != mb_strtolower($answer, 'UTF-8')) { + return false; + } + } + + + // All answer right + return true; + } + + + /** + * Action: quest. + * + * Display a text with lists with predefined values. + * + * @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 Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + if($this->request->getGetParam('show-answer') == 'true') { + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + } + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(array_key_exists('answer', $word)) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') { + $matrix[$x][$y]['answer'] = $answer; + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + + /** + * 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 words + $words = $this->Crossword->getWordsForQuest($quest['id']); + + // Create 2D-matrix + $matrix = array(); + $maxX = 0; + $maxY = 0; + foreach($words as $index => &$word) + { + // Character answer + $word['answer'] = $this->Crossword->getCharacterSubmission($word['id'], $character['id']); + + // Insert word + if($word['vertical']) + { + $x = $word['pos_x']; + $startY = $word['pos_y']; + $endY = $startY + mb_strlen($word['word'], 'UTF-8') - 1; + + $matrix = array_pad($matrix, $x+1, array()); + $matrix[$x] = array_pad($matrix[$x], $endY+1, null); + $maxX = max($maxX, $x); + $maxY = max($maxY, $endY); + + foreach(range($startY, $endY) as $y) + { + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $y-$startY, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($y == $startY) { + $matrix[$x][$y]['indices'][] = $index; + } + + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $y-$startY, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + else + { + $startX = $word['pos_x']; + $endX = $startX + mb_strlen($word['word'], 'UTF-8') - 1; + $y = $word['pos_y']; + + $matrix = array_pad($matrix, $endX+1, array()); + $maxX = max($maxX, $endX); + $maxY = max($maxY, $y); + + foreach(range($startX, $endX) as $x) + { + $matrix[$x] = array_pad($matrix[$x], $y+1, null); + + $matrix[$x][$y] = array( + 'char' => mb_substr($word['word'], $x-$startX, 1, 'UTF-8'), + 'indices' => (array_key_exists($x, $matrix) && array_key_exists($y, $matrix[$x]) && !is_null($matrix[$x][$y]) && array_key_exists('indices', $matrix[$x][$y])) ? $matrix[$x][$y]['indices'] : array(), + 'answer' => null, + 'right' => false + ); + if($x == $startX) { + $matrix[$x][$y]['indices'][] = $index; + } + if(!is_null($word['answer'])) + { + $answer = mb_substr($word['answer'], $x-$startX, 1, 'UTF-8'); + if($answer != ' ') + { + $matrix[$x][$y]['answer'] = $answer; + $matrix[$x][$y]['right'] = (mb_strtolower($matrix[$x][$y]['char'], 'UTF-8') == mb_strtolower($answer, 'UTF-8')); + } + } + } + } + } + + + // Pass data to view + $this->set('words', $words); + $this->set('maxX', $maxX); + $this->set('maxY', $maxY); + $this->set('matrix', $matrix); + } + + } + +?> diff --git a/questtypes/crossword/CrosswordQuesttypeModel.inc b/questtypes/crossword/CrosswordQuesttypeModel.inc new file mode 100644 index 00000000..78a42821 --- /dev/null +++ b/questtypes/crossword/CrosswordQuesttypeModel.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 CrosswordQuesttypeAgent for solving a crossword. + * + * @author Oliver Hanraths + */ + class CrosswordQuesttypeModel extends \hhu\z\QuesttypeModel + { + + + + + /** + * Get all words for a crossword-Quest. + * + * @param int $questId ID of Quest + * @return array Words + */ + public function getWordsForQuest($questId) + { + return $this->db->query( + 'SELECT id, question, word, vertical, pos_x, pos_y '. + 'FROM questtypes_crossword_words '. + 'WHERE quest_id = ? ', + 'i', + $questId + ); + } + + + /** + * Save Character’s submitted answer for one crossword-word. + * + * @param int $regexId ID of word + * @param int $characterId ID of Character + * @param string $answer Submitted answer for this word + */ + public function setCharacterSubmission($wordId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_crossword_words_characters '. + '(questtypes_crossword_word_id, character_id, answer) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'answer = ?', + 'iiss', + $wordId, $characterId, $answer, + $answer + ); + } + + + /** + * Get answer of one crossword-word submitted by Character. + * + * @param int $regexId ID of lisword + * @param int $characterId ID of Character + * @return int Submitted answer for this word or null + */ + public function getCharacterSubmission($wordId, $characterId) + { + $data = $this->db->query( + 'SELECT answer '. + 'FROM questtypes_crossword_words_characters '. + 'WHERE questtypes_crossword_word_id = ? AND character_id = ? ', + 'ii', + $wordId, $characterId + ); + if(!empty($data)) { + return $data[0]['answer']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/crossword/html/quest.tpl b/questtypes/crossword/html/quest.tpl new file mode 100644 index 00000000..d202eb8f --- /dev/null +++ b/questtypes/crossword/html/quest.tpl @@ -0,0 +1,49 @@ +
                    + + + + + + + + + + +
                    + + 0) : ?> + + +
                    +
                      + +
                    1. + + : + + : + + +
                    2. + +
                    + +
                    + diff --git a/questtypes/crossword/html/submission.tpl b/questtypes/crossword/html/submission.tpl new file mode 100644 index 00000000..c47e414d --- /dev/null +++ b/questtypes/crossword/html/submission.tpl @@ -0,0 +1,48 @@ +
                    + + + + + + + + + + +
                    + + 0) : ?> + + +
                    +
                      + +
                    1. + + : + + : + + +
                    2. + +
                    +
                    + 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..5fbf6328 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -0,0 +1,203 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Determine user answer + $answer = null; + if(array_key_exists($drop['id'], $answers) && !empty($answers[$drop['id']])) + { + $a = intval(substr($answers[$drop['id']], 4)); + if($a !== false && $a > 0) { + $answer = $a; + } + } + + // Update database record + $this->Dragndrop->setCharacterSubmission($drop['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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + + // Get Drags + $drags = $this->Dragndrop->getDrags($dndField['quest_id'], true); + + // Match drags with user answers + foreach($drags as &$drag) + { + $founds = array_keys($answers, 'drag'.$drag['id']); + if(count($founds) != 1) { + return false; + } + if(!$this->Dragndrop->dragMatchesDrop($drag['id'], $founds[0])) { + return false; + } + } + + + // Set status + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + + /** + * 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 Drag&Drop field + $dndField = $this->Dragndrop->getDragndrop($quest['id']); + $dndField['media'] = $this->Media->getSeminaryMediaById($dndField['questmedia_id']); + + // Get Drags + $drags = array(); + $dragsByIndex = $this->Dragndrop->getDrags($dndField['quest_id']); + foreach($dragsByIndex as &$drag) { + $drag['media'] = $this->Media->getSeminaryMediaById($drag['questmedia_id']); + $drags[$drag['id']] = $drag; + } + + // Get Drops + $drops = $this->Dragndrop->getDrops($dndField['quest_id']); + + // Get Character answers + foreach($drops as &$drop) + { + $drop['answer'] = $this->Dragndrop->getCharacterSubmission($drop['id'], $character['id']); + if(!is_null($drop['answer'])) + { + $drop['answer'] = $drags[$drop['answer']]; + unset($drags[$drop['answer']['id']]); + } + } + + + // Pass data to view + $this->set('seminary', $seminary); + $this->set('field', $dndField); + $this->set('drops', $drops); + $this->set('drags', $drags); + } + + } + +?> diff --git a/questtypes/dragndrop/DragndropQuesttypeModel.inc b/questtypes/dragndrop/DragndropQuesttypeModel.inc new file mode 100644 index 00000000..2aadb335 --- /dev/null +++ b/questtypes/dragndrop/DragndropQuesttypeModel.inc @@ -0,0 +1,180 @@ + + * @copyright 2014 Heinrich-Heine-Universität Düsseldorf + * @license http://www.gnu.org/licenses/gpl.html + * @link https://bitbucket.org/coderkun/the-legend-of-z + */ + + namespace hhu\z\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 + * @param boolean $onlyUsed Only Drag-items that are used for a Drop-item + * @return array Drag-items + */ + public function getDrags($dragndropId, $onlyUsed=false) + { + return $this->db->query( + 'SELECT id, questmedia_id '. + 'FROM questtypes_dragndrop_drags '. + 'WHERE questtypes_dragndrop_id = ?'. + ($onlyUsed + ? ' AND EXISTS ('. + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_drags '. + 'WHERE questtypes_dragndrop_drag_id = questtypes_dragndrop_drags.id'. + ')' + : null + ), + 'i', + $dragndropId + ); + } + + + /** + * Check if a Drag-item mathes a Drop-item. + * + * @param int $dragId ID of Drag-field + * @param int $dropId ID of Drop-field + * @return boolean Drag-item is valid for Drop-item + */ + public function dragMatchesDrop($dragId, $dropId) + { + $data = $this->db->query( + 'SELECT count(*) AS c '. + 'FROM questtypes_dragndrop_drops_drags '. + 'WHERE questtypes_dragndrop_drop_id = ? AND questtypes_dragndrop_drag_id = ?', + 'ii', + $dropId, $dragId + ); + if(!empty($data)) { + return ($data[0]['c'] > 0); + } + + + return false; + } + + + /** + * Save Character’s submitted answer for one 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) + { + if(is_null($answer)) + { + $this->db->query( + 'DELETE FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + } + else + { + $this->db->query( + 'INSERT INTO questtypes_dragndrop_drops_characters '. + '(questtypes_dragndrop_drop_id, character_id, questtypes_dragndrop_drag_id) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'questtypes_dragndrop_drag_id = ?', + 'iiii', + $dropId, $characterId, $answer, $answer + ); + } + } + + + /** + * Get Character’s saved answer for one Drop-field. + * + * @param int $dropId ID of Drop-field + * @param int $characterId ID of Character + * @return int ID of Drag-field or null + */ + public function getCharacterSubmission($dropId, $characterId) + { + $data = $this->db->query( + 'SELECT questtypes_dragndrop_drag_id '. + 'FROM questtypes_dragndrop_drops_characters '. + 'WHERE questtypes_dragndrop_drop_id = ? AND character_id = ?', + 'ii', + $dropId, $characterId + ); + if(!empty($data)) { + return $data[0]['questtypes_dragndrop_drag_id']; + } + + + return null; + } + + } + +?> diff --git a/questtypes/dragndrop/html/quest.tpl b/questtypes/dragndrop/html/quest.tpl new file mode 100644 index 00000000..4cb10b60 --- /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..c136fe1e --- /dev/null +++ b/questtypes/dragndrop/html/submission.tpl @@ -0,0 +1,15 @@ +
                    + +
                    + + + +
                    + +
                    + +
                    + + + +
                    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..a478cda5 --- /dev/null +++ b/questtypes/dummy/DummyQuesttypeController.inc @@ -0,0 +1,93 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..bca8c33a --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -0,0 +1,262 @@ + + * @copyright 2014 Heinrich-Heine-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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Save answers + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + foreach($questions as &$question) + { + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + foreach($answers as &$answer) + { + $userAnswer = (array_key_exists($answer['pos']-1, $userAnswers)) ? true : false; + $this->Multiplechoice->setCharacterSubmission($answer['id'], $character['id'], $userAnswer); + } + } + } + + + /** + * 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) + { + // Save temporary user answer of last question + $answers = (!is_array($answers)) ? array() : $answers; + $pos = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Get questions + $questions = $this->Multiplechoice->getQuestionsOfQuest($quest['id']); + + // Iterate questions + foreach($questions as &$question) + { + // Get answers + $userAnswers = $this->getUserAnswers($quest['id'], $question['id']); + $answers = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Match answers with user answers + foreach($answers as &$answer) + { + if(is_null($answer['tick'])) { + continue; + } + if($answer['tick']) { + if(!array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + else { + if(array_key_exists($answer['pos']-1, $userAnswers)) { + return false; + } + } + } + } + + + // All questions correct answerd + return true; + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Get count of questions + $count = $this->Multiplechoice->getQuestionsCountOfQuest($quest['id']); + + // Get position + $pos = 1; + if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('submit-answer'))) + { + if(!is_null($this->request->getPostParam('question'))) + { + // Get current position + $pos = intval($this->request->getPostParam('question')); + if($pos < 0 || $pos > $count) { + throw new \nre\exceptions\ParamsNotValidException($pos); + } + + // Save temporary answer of user + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + $answers = ($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('answers'))) ? $this->request->getPostParam('answers') : array(); + $this->saveUserAnswers($quest['id'], $question['id'], $answers); + + // Go to next position + $pos++; + } + else { + throw new \nre\exceptions\ParamsNotValidException('pos'); + } + } + + // Get current question + $question = $this->Multiplechoice->getQuestionOfQuest($quest['id'], $pos); + + // Get answers + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + + // Get previous user answers + if($this->request->getGetParam('show-answer') == 'true') + { + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('question', $question); + $this->set('pos', $pos); + $this->set('count', $count); + } + + + /** + * 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']); + + // Get answers + foreach($questions as &$question) + { + $question['answers'] = $this->Multiplechoice->getAnswersOfQuestion($question['id']); + + // Get user answers + foreach($question['answers'] as &$answer) { + $answer['useranswer'] = $this->Multiplechoice->getCharacterSubmission($answer['id'], $character['id']); + } + } + + + // Pass data to view + $this->set('questions', $questions); + } + + + + + /** + * Save the answers of a user for a question temporary in the + * session. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @param array $userAnswers Answers of user for the question + */ + private function saveUserAnswers($questId, $questionId, $userAnswers) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + $_SESSION['answers'][$questId][$questionId] = array(); + + // Save answres + foreach($userAnswers as $pos => &$answer) { + $_SESSION['answers'][$questId][$questionId][$pos] = $answer; + } + } + + + /** + * Get the temporary saved answers of a user for a question. + * + * @param int $questId ID of Quest + * @param int $questionId ID of multiple choice question + * @return array Answers of user for the question + */ + private function getUserAnswers($questId, $questionId) + { + // Ensure session structure + if(!array_key_exists('answers', $_SESSION)) { + $_SESSION['answers'] = array(); + } + if(!array_key_exists($questId, $_SESSION['answers'])) { + $_SESSION['answers'][$questId] = array(); + } + if(!array_key_exists($questionId, $_SESSION['answers'][$questId])) { + $_SESSION['answers'][$questId][$questionId] = array(); + } + + + // Return answers + return $_SESSION['answers'][$questId][$questionId]; + } + + } + +?> diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc new file mode 100644 index 00000000..6c4931b7 --- /dev/null +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeModel.inc @@ -0,0 +1,159 @@ + + * @copyright 2014 Heinrich-Heine-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 the count of multiple choice questions for a Quest. + * + * @param int $questId ID of Quest to get count for + * @return int Conut of questions + */ + public function getQuestionsCountOfQuest($questId) + { + $data = $this->db->query( + 'SELECT count(id) AS c '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + if(!empty($data)) { + return $data[0]['c']; + } + + + return 0; + } + + + /** + * 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 '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ?', + 'i', + $questId + ); + } + + + /** + * Get one multiple choice question of a Quest. + * + * @param int $questId ID of Quest + * @param int $pos Position of question + * @return array Question data + */ + public function getQuestionOfQuest($questId, $pos) + { + $data = $this->db->query( + 'SELECT id, pos, question '. + 'FROM questtypes_multiplechoice '. + 'WHERE quest_id = ? AND pos = ?', + 'ii', + $questId, $pos + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + + /** + * Get all answers of a multiple choice question. + * + * @param int $questionId ID of multiple choice question + * @return array Answers of question + */ + public function getAnswersOfQuestion($questionId) + { + return $this->db->query( + 'SELECT id, pos, answer, tick '. + 'FROM questtypes_multiplechoice_answers '. + 'WHERE questtypes_multiplechoice_id = ?', + 'i', + $questionId + ); + } + + + /** + * Save Character’s submitted answer for one option. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @param boolean $answer Submitted answer for this option + */ + public function setCharacterSubmission($answerId, $characterId, $answer) + { + $this->db->query( + 'INSERT INTO questtypes_multiplechoice_characters '. + '(questtypes_multiplechoice_answer_id, character_id, ticked) '. + 'VALUES '. + '(?, ?, ?) '. + 'ON DUPLICATE KEY UPDATE '. + 'ticked = ?', + 'iiii', + $answerId, $characterId, $answer, $answer + ); + } + + + /** + * Get answer of one option submitted by Character. + * + * @param int $answerId ID of multiple choice answer + * @param int $characterId ID of Character + * @return boolean Submitted answer of Character or false + */ + public function getCharacterSubmission($answerId, $characterId) + { + $data = $this->db->query( + 'SELECT ticked '. + 'FROM questtypes_multiplechoice_characters '. + 'WHERE questtypes_multiplechoice_answer_id = ? AND character_id = ? ', + 'ii', + $answerId, $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..eba80286 --- /dev/null +++ b/questtypes/multiplechoice/html/quest.tpl @@ -0,0 +1,21 @@ +
                    +
                    + : +

                    +
                      + &$answer) : ?> +
                    1. + /> + +
                    2. + +
                    +
                    + + + + + + + +
                    diff --git a/questtypes/multiplechoice/html/submission.tpl b/questtypes/multiplechoice/html/submission.tpl new file mode 100644 index 00000000..ebbbd493 --- /dev/null +++ b/questtypes/multiplechoice/html/submission.tpl @@ -0,0 +1,16 @@ +
                      + &$question) : ?> +
                    1. +

                      t($question['question'])?>

                      +
                        + +
                      1. + + × + +
                      2. + +
                      +
                    2. + +
                    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..18e55d1b --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -0,0 +1,166 @@ + + * @copyright 2014 Heinrich-Heine-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. + * + * @throws SubmissionNotValidException + * @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('answers', $_FILES)) + { + $answer = $_FILES['answers']; + + // Check error + if($answer['error'] !== 0) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException($answer['error']) + ); + } + + // Check mimetype + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + $answerMimetype = null; + foreach($mimetypes as &$mimetype) { + if($mimetype['mimetype'] == $answer['type']) { + $answerMimetype = $mimetype; + break; + } + } + if(is_null($answerMimetype)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\WrongFiletypeException($answer['type']) + ); + } + + // Check file size + if($answer['size'] > $answerMimetype['size']) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\MaxFilesizeException() + ); + } + + // Save file + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + throw new \hhu\z\exceptions\SubmissionNotValidException( + new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) + ); + } + } + } + + + /** + * 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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // Answer (Submission) + $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + + // Has Character already solved Quest? + $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + + // Get allowed mimetypes + $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); + + + // Pass data to view + $this->set('submission', $characterSubmission); + $this->set('solved', $solved); + $this->set('mimetypes', $mimetypes); + $this->set('exception', $exception); + } + + + /** + * 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..2d0a17e3 --- /dev/null +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -0,0 +1,106 @@ + + * @copyright 2014 Heinrich-Heine-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 + { + /** + * Required models + * + * @var array + */ + public $models = array('uploads'); + + + + + /** + * Save Character’s submitted upload. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character + * @param array $file Submitted upload + */ + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + { + // Save file on harddrive + $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + if($uploadId === false) { + return false; + } + + // Create database record + $this->db->query( + 'INSERT INTO questtypes_submit_characters '. + '(quest_id, character_id, upload_id) '. + 'VALUES '. + '(?, ?, ?) ', + 'iii', + $questId, $characterId, $uploadId + ); + + + return true; + } + + + /** + * Get upload 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 upload_id '. + 'FROM questtypes_submit_characters '. + 'WHERE quest_id = ? AND character_id = ?', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $this->Uploads->getUploadById($data[0]['upload_id']); + } + + + return null; + } + + + /** + * Get allowed mimetypes for uploading a file. + * + * @param int $seminaryId ID of Seminary + * @return array Allowed mimetypes + */ + public function getAllowedMimetypes($seminaryId) + { + return $this->db->query( + 'SELECT id, mimetype, size '. + 'FROM questtypes_submit_mimetypes '. + 'WHERE seminary_id = ?', + 'i', + $seminaryId + ); + } + + } + +?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl new file mode 100644 index 00000000..7132de5e --- /dev/null +++ b/questtypes/submit/html/quest.tpl @@ -0,0 +1,27 @@ + +

                    + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + getNestedException()->getType())?> + getNestedException() instanceof \hhu\z\exceptions\WrongFiletypeException) : ?> + + getNestedException() instanceof \hhu\z\exceptions\FileUploadException) : ?> + getNestedException()->getNestedMessage())?> + + getNestedException()->getMessage()?> + +

                    + + +
                    + +

                    :

                    +
                      + +
                    • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                    • + +
                    + +
                    + + (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..b3d89275 --- /dev/null +++ b/questtypes/submit/html/submission.tpl @@ -0,0 +1,11 @@ +
                    + (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) +

                    + + + + + + + +
                    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..5be23690 --- /dev/null +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -0,0 +1,171 @@ + + * @copyright 2014 Heinrich-Heine-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 + * @param Exception $exception Character submission exception + */ + public function quest($seminary, $questgroup, $quest, $character, $exception) + { + // 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..6616ba4d --- /dev/null +++ b/questtypes/textinput/html/quest.tpl @@ -0,0 +1,11 @@ +
                    +

                    + &$text) : ?> + 0) : ?> + + + t($text)?> + +

                    + +
                    diff --git a/questtypes/textinput/html/submission.tpl b/questtypes/textinput/html/submission.tpl new file mode 100644 index 00000000..b3d606f4 --- /dev/null +++ b/questtypes/textinput/html/submission.tpl @@ -0,0 +1,7 @@ + &$text) : ?> + 0) : ?> + + + +t($text)?> + diff --git a/requests/WebRequest.inc b/requests/WebRequest.inc new file mode 100644 index 00000000..2e0f19b9 --- /dev/null +++ b/requests/WebRequest.inc @@ -0,0 +1,401 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\requests; + + + /** + * Representation of a web-request. + * + * @author coderkun + */ + class WebRequest extends \nre\core\Request + { + /** + * Passed GET-parameters + * + * @var array + */ + private $getParams = array(); + /** + * Passed POST-parameters + * + * @var array + */ + private $postParams = array(); + /** + * Stored routes + * + * @var array + */ + private $routes = array(); + /** + * Stored reverse-routes + * + * @var array + */ + private $reverseRoutes = array(); + + + + + /** + * Construct a new web-request. + */ + public function __construct() + { + // Detect current request + $this->detectRequest(); + + // Load GET-parameters + $this->loadGetParams(); + + // Load POST-parameters + $this->loadPostParams(); + + // Detect AJAX + $this->detectAJAX(); + } + + + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + if($index == 0) { + return $this->getGetParam('layout', $defaultIndex); + } + else { + return parent::getParam($index-1, $defaultIndex); + } + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameters + */ + public function getParams($offset=0) + { + if($offset == 0) + { + return array_merge( + array( + $this->getGetParam('layout', 'toplevel') + ), + parent::getParams() + ); + } + + + return array_slice($this->params, $offset-1); + } + + + /** + * Get a GET-parameter. + * + * @param string $key Key of GET-parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of GET-parameter + */ + public function getGetParam($key, $defaultIndex=null) + { + // Check key + if(array_key_exists($key, $this->getParams)) + { + // Return value + return $this->getParams[$key]; + } + + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all GET-parameters. + * + * @return array GET-Parameters + */ + public function getGetParams() + { + return $this->getParams; + } + + + /** + * Get a POST-parameter. + * + * @param string $key Key of POST-parameter + * @param string $defaultValue Default value for this parameter + * @return string Value of POST-parameter + */ + public function getPostParam($key, $defaultValue=null) + { + // Check key + if(array_key_exists($key, $this->postParams)) + { + // Return value + return $this->postParams[$key]; + } + + + // Return default value + return $defaultValue; + } + + + /** + * Get all POST-parameters. + * + * @return array POST-parameters + */ + public function getPostParams() + { + return $this->postParams; + } + + + /** + * Get the method of the current request. + * + * @return string Current request method + */ + public function getRequestMethod() + { + return $_SERVER['REQUEST_METHOD']; + } + + + /** + * Add a URL-route. + * + * @param string $pattern Regex-Pattern that defines the routing + * @param string $replacement Regex-Pattern for replacement + * @param bool $isLast Stop after that rule + */ + public function addRoute($pattern, $replacement, $isLast=false) + { + // Store route + $this->routes[] = $this->newRoute($pattern, $replacement, $isLast); + } + + + /** + * Add a reverse URL-route. + * + * @param string $pattern Regex-Pattern that defines the reverse routing + * @param string $replacement Regex-Pattern for replacement + * @param bool $isLast Stop after that rule + */ + public function addReverseRoute($pattern, $replacement, $isLast=false) + { + // Store reverse route + $this->reverseRoutes[] = $this->newRoute($pattern, $replacement, $isLast); + } + + + /** + * Apply stored reverse-routes to an URL + * + * @param string $url URL to apply reverse-routes to + * @return string Reverse-routed URL + */ + public function applyReverseRoutes($url) + { + return $this->applyRoutes($url, $this->reverseRoutes); + } + + + /** + * Revalidate the current request + */ + public function revalidate() + { + $this->detectRequest(); + } + + + /** + * Get a SERVER-parameter. + * + * @param string $key Key of SERVER-parameter + * @return string Value of SERVER-parameter + */ + public function getServerParam($key) + { + if(array_key_exists($key, $_SERVER)) { + return $_SERVER[$key]; + } + + + return null; + } + + + + + /** + * Detect the current HTTP-request. + */ + private function detectRequest() + { + // URL ermitteln + $url = isset($_GET) && array_key_exists('url', $_GET) ? $_GET['url'] : ''; + $url = trim($url, '/'); + + // Routes anwenden + $url = $this->applyRoutes($url, $this->routes); + + // URL splitten + $params = explode('/', $url); + if(empty($params[0])) { + $params = array(); + } + + + // Parameter speichern + $this->params = $params; + } + + + /** + * Determine parameters passed by GET. + */ + private function loadGetParams() + { + if(isset($_GET)) { + $this->getParams = $_GET; + } + } + + + /** + * Determine parameters passed by POST. + */ + private function loadPostParams() + { + if(isset($_POST)) { + $this->postParams = $_POST; + } + } + + + /** + * Detect an AJAX-request by checking the X-Requested-With + * header and set the layout to 'ajax' in this case. + */ + private function detectAjax() + { + // Get request headers + $headers = apache_request_headers(); + + // Check X-Requested-With header and set layout + if(array_key_exists('X-Requested-With', $headers) && $headers['X-Requested-With'] == 'XMLHttpRequest') { + if(!array_key_exists('layout', $this->getParams)) { + $this->getParams['layout'] = 'ajax'; + } + } + } + + + /** + * Create a new URL-route. + * + * @param string $pattern Regex-Pattern that defines the reverse routing + * @param string $replacement Regex-Pattern for replacement + * @param bool $isLast Stop after that rule + * @return array New URL-route + */ + private function newRoute($pattern, $replacement, $isLast=false) + { + return array( + 'pattern' => $pattern, + 'replacement' => $replacement, + 'isLast' => $isLast + ); + } + + + /** + * Apply given routes to an URL + * + * @param string $url URL to apply routes to + * @param array $routes Routes to apply + * @return string Routed URL + */ + private function applyRoutes($url, $routes) + { + // Traverse given routes + foreach($routes as &$route) + { + // Create and apply Regex + $urlR = preg_replace( + '>'.$route['pattern'].'>i', + $route['replacement'], + $url + ); + + // Split URL + $get = ''; + if(($gpos = strrpos($urlR, '?')) !== false) { + $get = substr($urlR, $gpos+1); + $urlR = substr($urlR, 0, $gpos); + } + + // Has current route changed anything? + if($urlR != $url || !empty($get)) + { + // Extract GET-parameters + if(strlen($get) > 0) + { + $gets = explode('&', $get); + foreach($gets as $get) + { + $get = explode('=', $get); + if(!array_key_exists($get[0], $this->getParams)) { + $this->getParams[$get[0]] = $get[1]; + } + } + } + + + // Stop when route “isLast” + if($route['isLast']) { + $url = $urlR; + break; + } + } + + + // Set new URL + $url = $urlR; + } + + + // Return routed URL + return $url; + } + + } + +?> diff --git a/responses/WebResponse.inc b/responses/WebResponse.inc new file mode 100644 index 00000000..2ca25079 --- /dev/null +++ b/responses/WebResponse.inc @@ -0,0 +1,250 @@ + + * @copyright 2013 coderkun (http://www.coderkun.de) + * @license http://www.gnu.org/licenses/gpl.html + * @link http://www.coderkun.de/projects/nre + */ + + namespace nre\responses; + + + /** + * Representation of a web-response. + * + * @author coderkun + */ + class WebResponse extends \nre\core\Response + { + /** + * Applied GET-parameters + * + * @var array + */ + private $getParams = array(); + /** + * Changed header lines + * + * @var array + */ + private $headers = array(); + + + + + /** + * Add a parameter. + * + * @param mixed $value Value of parameter + */ + public function addParam($value) + { + if(array_key_exists('layout', $this->getParams)) { + parent::addParam($value); + } + else { + $this->addGetParam('layout', $value); + } + } + + + /** + * Add multiple parameters. + * + * @param mixed $value1 Value of first parameter + * @param mixed … Values of further parameters + */ + public function addParams($value1) + { + $this->addParam($value1); + + $this->params = array_merge( + $this->params, + array_slice( + func_get_args(), + 1 + ) + ); + } + + + /** + * Delete all stored parameters (from offset on). + * + * @param int $offset Offset-index + */ + public function clearParams($offset=0) + { + if($offset == 0) { + unset($this->getParams['layout']); + } + + parent::clearParams(max(0, $offset-1)); + } + + + /** + * Get a parameter. + * + * @param int $index Index of parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of parameter + */ + public function getParam($index, $defaultIndex=null) + { + if($index == 0) { + return $this->getGetParam('layout', $defaultIndex); + } + else { + return parent::getParam($index-1, $defaultIndex); + } + } + + + /** + * Get all parameters from index on. + * + * @param int $offset Offset-index + * @return array Parameter values + */ + public function getParams($offset=0) + { + if($offset == 0) + { + if(!array_key_exists('layout', $this->getParams)) { + return array(); + } + + return array_merge( + array( + $this->getParams['layout'] + ), + $this->params + ); + } + + + return array_slice($this->params, $offset-1); + } + + + /** + * Add a GET-parameter. + * + * @param string $key Key of GET-parameter + * @param mixed $value Value of GET-parameter + */ + public function addGetParam($key, $value) + { + $this->getParams[$key] = $value; + } + + + /** + * Add multiple GET-parameters. + * + * @param array $params Associative arary with key-value GET-parameters + */ + public function addGetParams($params) + { + $this->getParams = array_merge( + $this->getParams, + $params + ); + } + + + /** + * Get a GET-parameter. + * + * @param int $index Index of GET-parameter + * @param string $defaultIndex Index of default configuration value for this parameter + * @return string Value of GET-parameter + */ + public function getGetParam($key, $defaultIndex=null) + { + // Check key + if(array_key_exists($key, $this->getParams)) + { + // Return value + return $this->getParams[$key]; + } + + // Return default value + return \nre\core\Config::getDefault($defaultIndex); + } + + + /** + * Get all GET-parameters. + * + * @return array All GET-parameters + */ + public function getGetParams() + { + return $this->getParams; + } + + + /** + * Add a line to the response header. + * + * @param string $headerLine Header line + * @param bool $replace Replace existing header line + * @param int $http_response_code HTTP-response code + */ + public function addHeader($headerLine, $replace=true, $http_response_code=null) + { + $this->headers[] = $this->newHeader($headerLine, $replace, $http_response_code); + } + + + /** + * Clear all stored headers. + */ + public function clearHeaders() + { + $this->headers = array(); + } + + + /** + * Send stored headers. + */ + public function header() + { + foreach($this->headers as $header) + { + header( + $header['string'], + $header['replace'], + $header['responseCode'] + ); + } + } + + + + + /** + * Create a new header line. + * + * @param string $headerLine Header line + * @param bool $replace Replace existing header line + * @param int $http_response_code HTTP-response code + */ + private function newHeader($headerLine, $replace=true, $http_response_code=null) + { + return array( + 'string' => $headerLine, + 'replace' => $replace, + 'responseCode' => $http_response_code + ); + } + + } + +?> diff --git a/seminarymedia/empty b/seminarymedia/empty new file mode 100644 index 00000000..e69de29b diff --git a/tmp/empty b/tmp/empty new file mode 100644 index 00000000..e69de29b diff --git a/uploads/empty b/uploads/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/achievement.tpl b/views/binary/media/achievement.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/achievement.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/avatar.tpl b/views/binary/media/avatar.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/avatar.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/binary/media/seminary.tpl b/views/binary/media/seminary.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminary.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/media/seminaryheader.tpl b/views/binary/media/seminaryheader.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/media/seminaryheader.tpl @@ -0,0 +1 @@ + diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/index.tpl new file mode 100644 index 00000000..0d6fb0df --- /dev/null +++ b/views/binary/uploads/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/achievements/index.tpl b/views/html/achievements/index.tpl new file mode 100644 index 00000000..051b1d1e --- /dev/null +++ b/views/html/achievements/index.tpl @@ -0,0 +1,87 @@ + +
                    + +
                    + + +

                    +

                    +
                    +
                    +

                    +
                      + +
                    1. + + + + + + + + + +

                      +

                      +
                    2. + +
                    +
                    +
                    +

                    +
                      + +
                    1. + +

                      +

                      +
                    2. + +
                    +
                    +
                    +

                    +
                    +

                    +
                    + +
                    +
                    +

                    . : .

                    +
                      + +
                    • + + + +

                      +

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

                      +

                      +
                    • + + +
                    • + + + +

                      + +

                      + +

                      + + +
                      +
                      + +
                      +

                      %

                      +
                      + +
                    • + +
                    diff --git a/views/html/charactergroups/group.tpl b/views/html/charactergroups/group.tpl new file mode 100644 index 00000000..0f6f88d3 --- /dev/null +++ b/views/html/charactergroups/group.tpl @@ -0,0 +1,50 @@ + +
                    + +
                    + + +
                    + +

                    +

                    ""

                    +
                    +
                      +
                    • .
                    • +
                    • XP
                    • +
                    • 1) ? _('Members') : _('Member')?>
                    • +
                    + +
                    +

                    +
                      + +
                    • + +

                      + +

                      +

                      XP

                      +
                    • + +
                    +
                    + +
                    +

                    +
                      + +
                    • +

                      + format(new \DateTime($quest['created']))?> + + / XP +

                      +
                    • + +
                    +
                    diff --git a/views/html/charactergroups/groupsgroup.tpl b/views/html/charactergroups/groupsgroup.tpl new file mode 100644 index 00000000..3d337cf3 --- /dev/null +++ b/views/html/charactergroups/groupsgroup.tpl @@ -0,0 +1,25 @@ + +
                    + +
                    + + + +

                    + +
                      + +
                    1. XP
                    2. + +
                    + + +

                    +
                      + +
                    • + +
                    diff --git a/views/html/charactergroups/index.tpl b/views/html/charactergroups/index.tpl new file mode 100644 index 00000000..d88d25eb --- /dev/null +++ b/views/html/charactergroups/index.tpl @@ -0,0 +1,14 @@ + +
                    + +
                    + + +

                    +
                      + +
                    • + +
                    diff --git a/views/html/charactergroupsquests/quest.tpl b/views/html/charactergroupsquests/quest.tpl new file mode 100644 index 00000000..6c696737 --- /dev/null +++ b/views/html/charactergroupsquests/quest.tpl @@ -0,0 +1,53 @@ + +
                    + +
                    + + + +

                    +Maximale Belohnung: XP + +
                    +

                    +

                    + + + + +

                    + +

                    +

                    + +
                    + + +
                    +

                    +

                    +
                    + + +
                    +

                    +

                    +
                    + + +
                    +

                    +
                      + +
                    • + format(new \DateTime($group['created']))?> + + XP +
                    • + +
                    +
                    diff --git a/views/html/characters/character.tpl b/views/html/characters/character.tpl new file mode 100644 index 00000000..925a9df0 --- /dev/null +++ b/views/html/characters/character.tpl @@ -0,0 +1,101 @@ + +
                    + +
                    + +

                    +

                    +

                    + +
                    +
                    +
                    +
                    + +
                    +

                    :  %

                    +
                    +
                    +

                    +

                    +
                    +
                    +

                    +

                    XP

                    +
                    +
                    +

                    .

                    +

                    +
                    + +

                    +
                      + +
                    • + + + +

                      +

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

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

                    +
                      + +
                    • + +

                       XPs

                      +
                    • + +
                    +
                    + +
                    +

                    +
                      + &$rankCharacter) : ?> +
                    • + +

                      .

                      +

                      ( XPs)

                      +
                    • + +
                    • + +

                      .

                      +

                      ( XPs)

                      +
                    • + &$rankCharacter) : ?> +
                    • + +

                      .

                      +

                      ( XPs)

                      +
                    • + +
                    +
                    +
                    + +
                    +

                    +
                      + +
                    • +

                      (/)

                      +
                      + +
                      +
                    • + +
                    +
                    + diff --git a/views/html/characters/index.tpl b/views/html/characters/index.tpl new file mode 100644 index 00000000..91833a0e --- /dev/null +++ b/views/html/characters/index.tpl @@ -0,0 +1,17 @@ + +
                    + +
                    + +

                    +

                    + +
                      + +
                    • +

                      +

                      +

                      XP

                      +
                    • + +
                    diff --git a/views/html/characters/register.tpl b/views/html/characters/register.tpl new file mode 100644 index 00000000..2d0879ac --- /dev/null +++ b/views/html/characters/register.tpl @@ -0,0 +1,82 @@ + +
                    + +
                    + +

                    +

                    + +
                    + +
                      + &$settings) : ?> +
                    • +
                        + $value) : ?> +
                      • + +
                      • + +
                      +
                    • + +
                    + +
                    + + +
                    + + +
                    + + +
                      + &$settings) : ?> +
                    • + +
                    + +
                    + + + + + required="required"/> + + + + + +
                    + +
                    + +
                    diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl new file mode 100644 index 00000000..0f68bc07 --- /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..a62f4e11 --- /dev/null +++ b/views/html/html.tpl @@ -0,0 +1,56 @@ + + + + + + The Legend of Z + + + + + + + + + + + + + + +
                    + +
                    +
                    + +
                    + + + + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl new file mode 100644 index 00000000..3dc754ae --- /dev/null +++ b/views/html/introduction/index.tpl @@ -0,0 +1,36 @@ +

                    +

                    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/library/index.tpl b/views/html/library/index.tpl new file mode 100644 index 00000000..2dd6cd09 --- /dev/null +++ b/views/html/library/index.tpl @@ -0,0 +1,28 @@ + +
                    + +
                    + + +

                    +

                    +
                    +

                    0) ? $numberFormatter->format(round($totalCharacterQuestcount/$totalQuestcount*100)) : 0) ?>

                    +
                    + +
                    +
                    +
                      + +
                    • + +

                      +

                      Fortschritt: /

                      +
                      + +
                      +
                    • + +
                    diff --git a/views/html/library/topic.tpl b/views/html/library/topic.tpl new file mode 100644 index 00000000..b68d2a67 --- /dev/null +++ b/views/html/library/topic.tpl @@ -0,0 +1,30 @@ + +
                    + +
                    + + +

                    +
                    +

                    Themenfortschritt: /

                    +
                    + +
                    +
                    + +

                    Quests zu diesem Thema:

                    +
                      + +
                    • +

                      +
                        + +
                      • + +
                      +
                    • + +
                    diff --git a/views/html/menu/index.tpl b/views/html/menu/index.tpl new file mode 100644 index 00000000..dbab43c2 --- /dev/null +++ b/views/html/menu/index.tpl @@ -0,0 +1,9 @@ +
                  • The Legend of Z
                  • + 0) : ?>
                  • +
                  • + 0) : ?> + +
                  • + +
                  • + diff --git a/views/html/questgroups/create.tpl b/views/html/questgroups/create.tpl new file mode 100644 index 00000000..233713cc --- /dev/null +++ b/views/html/questgroups/create.tpl @@ -0,0 +1,16 @@ + +
                    + +
                    + +

                    +

                    +

                    + +
                    +
                    + +
                    +
                    + +
                    diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl new file mode 100644 index 00000000..84c7177c --- /dev/null +++ b/views/html/questgroups/questgroup.tpl @@ -0,0 +1,58 @@ + +
                    + +
                    + +

                    + + + + +

                    :

                    + +

                    + + +

                    + + + + + 0) : ?> +

                    +
                      + +
                    • + +
                      +
                      +

                      Fortschritt:

                      +
                      + +
                      +

                      / XP

                      +
                      +
                    • + +
                    + + + + + 0) : ?> +

                    + + diff --git a/views/html/questgroupshierarchypath/index.tpl b/views/html/questgroupshierarchypath/index.tpl new file mode 100644 index 00000000..abe15abe --- /dev/null +++ b/views/html/questgroupshierarchypath/index.tpl @@ -0,0 +1,17 @@ + 0) : ?> + + diff --git a/views/html/quests/create.tpl b/views/html/quests/create.tpl new file mode 100644 index 00000000..94bc47b3 --- /dev/null +++ b/views/html/quests/create.tpl @@ -0,0 +1,41 @@ + +
                    + +
                    + +

                    +

                    +

                    + +
                    +
                    + +
                    + +
                    + +
                    + +
                    +
                    +
                    +
                    +
                    +
                    +
                    +
                    +
                    +
                    +
                    +
                    +
                    + +
                    diff --git a/views/html/quests/createmedia.tpl b/views/html/quests/createmedia.tpl new file mode 100644 index 00000000..003ea042 --- /dev/null +++ b/views/html/quests/createmedia.tpl @@ -0,0 +1,19 @@ + +
                    + +
                    + +

                    +

                    Create Questsmedia

                    + + +

                    New mediaId:

                    + + +

                    Error:

                    + +
                    +
                    +
                    + +
                    diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl new file mode 100644 index 00000000..00d7e576 --- /dev/null +++ b/views/html/quests/index.tpl @@ -0,0 +1,49 @@ + +
                    + +
                    + +

                    +

                    + +
                    + + + + + + + + + + + + + + + + + + + + + +
                    + + + + XPs
                    + + + +
                    diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl new file mode 100644 index 00000000..5256b4f7 --- /dev/null +++ b/views/html/quests/quest.tpl @@ -0,0 +1,113 @@ + +
                    + +
                    + +

                    + +

                    + + 0) : ?> +
                    +

                    +
                    + + +

                    + + + + + + + +

                    + 0 || !empty($questtext['abort_text'])) : ?> +
                      + +
                    • + + +
                    • + +
                    + + +
                    +
                    + + + +
                    + +

                    +

                    + +

                    +

                    + +
                    + + + +
                    + +

                    +

                    t($quest['task'])?>

                    + + + +

                    : +

                      + +
                    • +
                    • + +
                    + + +

                    + +
                    + + + +
                    +

                    + 0) : ?> +
                      + + +
                    • + : + + + + + +
                    • + +
                    • + + + + + : + + + + + + + + + +
                    • + + +
                    + + : + +
                    + diff --git a/views/html/quests/submission.tpl b/views/html/quests/submission.tpl new file mode 100644 index 00000000..10fde93a --- /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..a00ce56d --- /dev/null +++ b/views/html/quests/submissions.tpl @@ -0,0 +1,37 @@ + +
                    + +
                    + +

                    + +

                    + +
                    +

                    +
                      + +
                    • + +
                    • + +
                    + +

                    +
                      + +
                    • + +
                    • + +
                    + +

                    +
                      + +
                    • + +
                    • + +
                    +
                    diff --git a/views/html/seminaries/create.tpl b/views/html/seminaries/create.tpl new file mode 100644 index 00000000..22a9acaa --- /dev/null +++ b/views/html/seminaries/create.tpl @@ -0,0 +1,15 @@ + +
                    + +
                    + +

                    +

                    + +
                    +
                    + +
                    +
                    + +
                    diff --git a/views/html/seminaries/delete.tpl b/views/html/seminaries/delete.tpl new file mode 100644 index 00000000..8e53fdb5 --- /dev/null +++ b/views/html/seminaries/delete.tpl @@ -0,0 +1,13 @@ + +
                    + +
                    + +

                    +

                    + + +
                    + + +
                    diff --git a/views/html/seminaries/edit.tpl b/views/html/seminaries/edit.tpl new file mode 100644 index 00000000..27974c1b --- /dev/null +++ b/views/html/seminaries/edit.tpl @@ -0,0 +1,15 @@ + +
                    + +
                    + +

                    +

                    + +
                    +
                    + +
                    +
                    + +
                    diff --git a/views/html/seminaries/index.tpl b/views/html/seminaries/index.tpl new file mode 100644 index 00000000..40cdcd7c --- /dev/null +++ b/views/html/seminaries/index.tpl @@ -0,0 +1,31 @@ +

                    + 0) : ?> + + +
                      + +
                    • + + + +
                      +

                      + 0) : ?> + + + + +

                      +

                      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..b28b5a14 --- /dev/null +++ b/views/html/seminaries/seminary.tpl @@ -0,0 +1,40 @@ + +
                    + +
                    + +

                    + 0) : ?> + + +

                    + +

                    + + diff --git a/views/html/seminarybar/index.tpl b/views/html/seminarybar/index.tpl new file mode 100644 index 00000000..c2b62e47 --- /dev/null +++ b/views/html/seminarybar/index.tpl @@ -0,0 +1,48 @@ +
                    +

                    + + +
                    + + +
                    +

                    +

                    +
                    + + + +
                    +

                    +
                      +
                    • + + + +

                      +

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

                      +
                    • +
                    +
                    + + +
                    + +

                    +
                      + +
                    • + +

                      +

                      ( XPs)

                      +
                    • + +
                    +

                    + +
                    diff --git a/views/html/seminarymenu/index.tpl b/views/html/seminarymenu/index.tpl new file mode 100644 index 00000000..56b9307b --- /dev/null +++ b/views/html/seminarymenu/index.tpl @@ -0,0 +1,5 @@ +
                  • + 0) : ?>
                  • +
                  • +
                  • +
                  • 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..2d5484dc --- /dev/null +++ b/views/html/users/create.tpl @@ -0,0 +1,18 @@ +

                    +

                    + +
                    +
                    + +
                    + +
                    + +
                    + +
                    + +
                    +
                    + +
                    diff --git a/views/html/users/delete.tpl b/views/html/users/delete.tpl new file mode 100644 index 00000000..10f280ab --- /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..510aaed3 --- /dev/null +++ b/views/html/users/edit.tpl @@ -0,0 +1,18 @@ +

                    +

                    + +
                    +
                    + +
                    + +
                    + +
                    + +
                    + +
                    +
                    + +
                    diff --git a/views/html/users/index.tpl b/views/html/users/index.tpl new file mode 100644 index 00000000..9f36049c --- /dev/null +++ b/views/html/users/index.tpl @@ -0,0 +1,9 @@ +

                    + +
                      + +
                    1. format(new \DateTime($user['created'])))?>
                    2. + +
                    diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl new file mode 100644 index 00000000..f141f4cd --- /dev/null +++ b/views/html/users/login.tpl @@ -0,0 +1,15 @@ +

                    + +

                    + +

                    .

                    + +
                    +
                    + +
                    + +
                    +
                    + +
                    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/register.tpl b/views/html/users/register.tpl new file mode 100644 index 00000000..29843569 --- /dev/null +++ b/views/html/users/register.tpl @@ -0,0 +1,90 @@ +

                    + +

                    + +
                      + &$settings) : ?> +
                    • +
                        + $value) : ?> +
                      • + getMessage(); + break; + } ?> +
                      • + +
                      +
                    • + +
                    + +
                    +
                    + + />
                    + + />
                    + + />
                    + + />
                    + + />
                    +
                    + +
                    diff --git a/views/html/users/user.tpl b/views/html/users/user.tpl new file mode 100644 index 00000000..b5e23aa7 --- /dev/null +++ b/views/html/users/user.tpl @@ -0,0 +1,35 @@ +

                    + 0) : ?> + + +

                    +

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

                    + +

                    +
                      + +
                    • + +

                      + +

                      + 0) : ?> + + + + +

                      +

                      +
                    • + +
                    + +

                    + 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..4bb219d1 --- /dev/null +++ b/www/css/desktop.css @@ -0,0 +1,348 @@ +@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%;margin-top:25px} +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:1px 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:87.5%} +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:0;margin:0;padding:0} +legend{border:0;padding:0;margin-bottom:15px} +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} +a:hover{color:#62c5cd} +.fa{padding:0 10px 0 0} +.fwb{font-weight:700} + +header{background:#0f373c;margin-bottom:19px;position:fixed;width:100%;z-index:99} +header nav{z-index:99;margin:0 5%} + +header .fa{color:#5e9499} +header a:hover .fa{color:#7fb0b4} + +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:#d9e5e7} +menu a:hover,#profile:hover{color:#fff} + +menu .active{color:#fff} +menu .active .fa,.crewards .unlocked .fa{color:#bcd75e} + +menu .smnry{font-size:0.875em;padding:8px 0 8px 12px;background:#0c2e32;border-bottom:2px solid #0f373c;border-radius:3px} + +#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:#d9e5e7} + +article{padding:70px 0 30px} +aside{display:none} + +.moodpic{margin:-15px -5.5% 0 -5.5%;overflow:hidden} +.moodpic img{width:100vw} + +.breadcrumbs li{display:block;font-size:.875em} +.breadcrumbs .fa{padding-right:5px;font-size:.75em;color:#989693} + +.questgroups li{margin:0 0 25px 0;border-radius:3px;overflow:hidden} +.questgroups img{display:block;width:100%;} +.questgroups section{padding:20px;background:#fff} + +.qg li{margin-left:10px;padding:0 0 25px 30px;border:2px solid #dbd9d5;border-width:0 0 0 2px} +.qgicon{margin:17px 0 0 -39px;background:#f7f5f2;font-size:1.25em;float:left} +.qgicon.locked{margin-top:-2px} +.qgtitle a{background:#5cb6bd;border-radius:3px 3px 0 0;display:block;padding:20px;color:#fff;font-weight:700} +.qgtitle a:hover{background:#62c5cd} +.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} + +#qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} +.qtext{padding-right:15px} +.qtext img,.grpqimg{float:right;margin-left:15px;max-width:30%;border-radius:3px} + +.xpinfo{display:none} +.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,input[type="submit"]{display:inline-block;margin-top:10px;color:#fff;font-weight:700;padding:7px 20px;border-radius:4px} +.orange,input[type="submit"]{text-shadow:1px 2px #d4701a;background:#f5821f;border:solid #d4701a;border-width:0 0 3px 0} +.orange:hover,input[type="submit"]:hover{color:#fff;background:#f09140} + +input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;border-color:#d48c4e} + +.admin li{margin:0 0 15px 0;display:inline-block} +.admin li a{padding:4px 15px;background:#5cb6bd;border-bottom:2px solid #50a0a6;color:#fff;border-radius:3px} +.admin li a:hover{background:#62c5cd} + +/** Login & Registration **/ + +.logreg{width:auto;display:inline-block;padding:15px 20px;background:#eae8e4;border-radius:3px} +.logreg label{display:block;font-size:.875em} +.logreg input{margin:5px 0 15px} +.logreg .cta{display:block} + + +/** Character Profile **/ + +.cportrait img{width:100%} + +.cdata{display:inline-block;background:#fff;border-radius:3px;padding:12px 20px 0 20px;margin-bottom:5px} +.cdata.square{text-align:center;width:10%;padding-top:0} +.cdata.square.blue{background:#5cb6bd;color:#fff} +.cdata .xpbar,.ctopics .xpbar{width:100%} +.cdata .value{font-size:1.5em;margin:0;padding-top:8px} + +.crewards li{background:#fff;margin-bottom:5px;padding:15px 15px 1px 15px;border-radius:3px;font-size:.875em} +.crewards li:last-child{margin-bottom:0} +.crewards li .fa{font-size:1.25em} +.crewards .locked{color:#aca8a1;padding:10px;} + +.cgroups img{float:left;border-radius:3px;margin-right:5px;height:35px} +.cgroups p{float:left;0;padding:0 12px;line-height:35px;background:#fff;border-radius:0 3px 3px 0} +.cgroups a{float:left;padding:0 12px;line-height:35px;background:#50a4ab;color:#fff;background:#50a4ab;border-radius:3px 0 0 3px} + +.cranks li{clear:both;padding:8px 0 8px 8px;border-radius:3px} +.cranks li:nth-child(odd){background:#fff} +.cranks img{float:left;margin-right:15px;width:50px;height:50px;border-radius:25px} +.cranks p,.ctopics p{margin:0;padding:0} + +.ctopics .xpbar,.ltopics .xpbar{background:#e4e1dd} +.ctopics .xpbar span{background:#50a4ab} + + +/** Charactergroup List **/ + +.cglist li{list-style-type:decimal;background:#fff;margin:0 0 6px 25px;padding:5px 15px;border-radius:3px} +.cglist .xp{float:right} +.cgqlist li{list-style-type:square;margin-left:25px;padding:2px 15px} + +/** Charactergroup Profile **/ + +.gbanner img{margin-top:10px;border-radius:3px} +.gbanner h1{margin:10px 0 5px 0} + +.gdata{margin:20px 0 40px 0} +.gdata li{float:left;background:#fff;padding:5px 15px;margin:0 5px 5px 0;border-radius:3px} + +.gchars li{background:#fff;padding:15px 0;border-radius:3px;float:left;margin-bottom:5px;width:49%} +.gchars li:nth-child(even){float:right} +.gchars img{width:50px;height:50px;border-radius:25px} +.gchars p{margin:0;padding:0;text-align:center} +.gchars .fa{position:absolute;margin:-10px 0 0} + +.gquests li{padding:12px 15px 0 15px;border-radius:3px} +.gquests li:nth-child(odd){background:#fff} +.gquests .date{color:#aca8a1;display:block} +.gquests .xp{display:block} + + +/** Charactergroup Quest **/ + +.grpqlist li{background:#fff;margin-bottom:6px;padding:5px 15px 5px 0;font-size:.875em;border-radius:3px} +.grpqlist .date{padding:0 15px;border-right:1px solid #dad8d5} +.grpqlist .group{padding:0 15px} +.grpqlist .xp{float:right} + + +/** Achievements **/ + +.rare ol{margin-bottom:40px} +.rare li{margin-bottom:15px} +.rare img{float:left;width:45px;height:45px;margin:4px 10px 0 0;border-radius:3px} +.rare p{margin:0} + +.achmnts li{background:#fff;margin-bottom:10px;padding:15px;border-radius:3px} +.achmnts img{width:55px;height:55px;float:left;margin-right:15px;border-radius:3px} +.achmnts p{margin:0 0 4px} +.achmnts .unlcked{margin-top:3px;font-size:.875em;font-weight:normal;display:block} +.achmnts .desc{font-size:.875em;padding-top:10px} +.achmnts .prgrss{margin-top:15px} +.achmnts .xpbar{margin:8px 0 0 0;width:80%} +.achmnts .xpnumeric{margin:0} + + +/** Library **/ + +.libindxpr{margin-top:20px} +.libindxpr p{font-weight:bold;margin:0} +.libindxpr .xpbar{float:none;margin:9px 0 20px 0;background:#e4e1dd;width:100%} +.libindxpr .xpbar span{background:#50a4ab} + +.libindx li{text-align:center;background:#fff;border-radius:3px;margin:0 0 15px 0;padding:15px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box} +.libindx i{margin-bottom:15px;font-size:1.25em;padding:0} +.libindx .ltopc{overflow:hidden;text-overflow:ellipsis} +.libindx .xpbar{width:100%;margin:0} + +.libtop li{background:#fff;padding:5px 5px 5px 10px;border-radius:3px;margin-bottom:6px} +.libtop p{margin-bottom:2px} +.libtop .addon li{display:inline;font-size:.875em;padding:0} +.libtop .addon li:after{content:", "} +.libtop .addon li:last-child:after{content: ""} + + +/** Quest Types **/ + +.mchoice{list-style-type:lower-alpha;list-style-position:inside} +.mchoice li{margin:0 0 10px 0} +.mchoice input[type=checkbox]{display:inline-block;margin:-19px 10px 0 24px;vertical-align:top} +.mchoice label{width:90%;display:inline-block;margin:-25px 0 0 0;vertical-align:top} + +.submit{padding:15px;background:#eae8e4;border-radius:3px;margin-bottom:15px} +.submit p{margin:15px 0 0 0;font-weight:bold} +.submit ul{list-style-type:square;margin-left:20px} +.error{padding:2px 5px;border:1px solid #850000;background:#ebd3d3;color:#850000;border-radius:3px} + +.textinput input[type=text]{height:16px} + +.crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} +.crossword td{background:#d7d4cf;padding:1px} +.crossword input[type=text]{text-align:center;height:26px;width:100%;min-width:8px;max-width:40px;margin:0;padding:0;border:none;text-transform:uppercase} +.crossword ol{list-style-type:decimal;margin-left:25px} +.crossword li{margin-top:20px} +.crossword .index{position:absolute;font-size:0.625em;margin:-2px 0 0 2px} + +.opponent{width:50%;float:left} +.opponent .portrait{height:250px;overflow:hidden;margin-bottom:15px} +.opponent .hero{background:#fff;max-width:130px} +.opponent .boss{max-width:100%} +.opponent p{text-align:center} +.opponent .fa{font-size:1.25em;color:#c7135b;padding-right:0} +.bossfight .option{background:#fff;margin-bottom:10px;padding:15px 15px 5px;border-radius:3px} + + +/** Media Queries **/ + +@media only screen and (min-width:480px){ +.questgroups li,.ctopics li,.fll48{width:48%;display:inline-block;margin-right:2%} +.questgroups li:nth-child(even),.ctopics li:nth-child(even),.flr48{width:48%;display:inline-block;margin-right:0;vertical-align:top} +.xpinfo{display:inline-block;float:left;padding-right:20px} +.xpbar{width:50%} + +.cinfo{float:left;width:70%} +.cportrait{float:right;width:25%} + +.gbanner img{float:left;margin:20px 20px 0 0} +.gbanner h1{margin:25px 0 2px} + +.gchars li{width:32%;margin-right:5px} +.gchars li:nth-child(even){float:left} + +.achmnts .xpbar{width:89%} + +.libindx li{float:left;width:49%} +.libindx li:nth-child(2n){float:right} +.libindx .ltopc{height:80px} + +.opponent .hero{max-width:200px} +} + +@media only screen and (min-width:768px){ +.xpbar{width:70%} + +.gchars li{width:19%} + +.gquests .date{display:inline;margin-right:15px} +.gquests .xp{float:right} + +.rare,.hunter{float:left;width:49%} +.hunter{float:right} +.achmnts .desc{padding-top:0} +.achmnts .unlcked{font-size:.75em;float:right;margin:-6px -4px 0 0;color:#878787} + +.libindx li{float:left;width:32%;margin-right:2%} +.libindx li:nth-child(2n){float:left} +.libindx li:nth-child(3n){margin-right:0} + +.bossfight li{float:left;width:44%} +.bossfight li:nth-child(even){float:right} +.bossfight input,.bossfight p{text-align:center} +} + +@media only screen and (min-width:1024px){ +header{position:fixed;left:0;top:0;bottom:0;width:300px;margin:0} +header nav{margin:30px 10% 0} +menu{display:block;opacity:1;position:relative} +menu a{padding:10px 0} +#profile{float:none;margin:0 0 15px 12px} +#toggle,.toggle{display:none} +.wrap{margin:0 0 0 300px} +article{padding:20px 40px 80px 40px} +.moodpic{margin:-20px -40px 0 -40px} +.breadcrumbs li{display:inline;padding-right:5px} + +.gchars li{width:19%} +} + +@media only screen and (min-width:1366px){ +html{overflow-y:scroll;height:100%} +body{background:#eae8e4;height:100%} +.wrap{width:800px;max-width:800px;height:auto !important;min-height:100%;height:100%;position:relative;overflow:hidden} +article{background:#f7f5f2;float:left} +.moodpic{width:880px;height:230px} +.moodpic img{width:100%} +} + +@media only screen and (min-width:1600px){ +aside{display:block;float:left;width:350px;min-width:350px;margin:0 0 0 40px} +aside section{margin:20px 0 40px 0} +aside .char{width:120px;float:left;margin-right:10px} +aside .charstats{background:#f7f5f2;border-radius:3px;padding:10px 0;margin-top:50px} +aside .charstats li{font-size:.875em;padding:2px 0} +aside .cranks li:nth-child(odd){background:#f7f5f2} +} \ 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'; + +?> diff --git a/www/js/dnd.js b/www/js/dnd.js new file mode 100644 index 00000000..ff1340c7 --- /dev/null +++ b/www/js/dnd.js @@ -0,0 +1,54 @@ +/** + * Drag&Drop-functions + */ + +function onDragStart(event) +{ + jQuery(event.currentTarget).addClass("drag"); + event.dataTransfer.setData("Text", event.target.id); +} + +function onDragEnter(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + jQuery(event.target).addClass('drop'); + } +} + +function onDragOver(event) +{ + if(event.target.nodeName == "DIV" && !(event.target.parentNode.id == "dropZone" && event.target.hasChildNodes())) { + event.preventDefault(); + } +} + +function onDragLeave(event) +{ + jQuery(event.target).removeClass('drop'); +} + +function onDragEnd(event) +{ + jQuery(event.currentTarget).removeClass("drag"); +} + +function onDrop(event, setId) +{ + setId = (typeof setId == 'undefined') ? true : setId; + jQuery(event.currentTarget).removeClass('drag'); + jQuery(event.target).removeClass('drop'); + event.preventDefault(); + + var data = event.dataTransfer.getData("Text"); + var dataElement = $('#'+data); + if(dataElement.parent() && $('#dnd_'+dataElement.parent().attr('id'))) { + $('#dnd_'+dataElement.parent().attr('id')).attr('value', "null"); + } + jQuery(event.target).append(dataElement); + + if(setId) { + console.log(data); + $('#dnd_' + jQuery(event.target).attr('id')).attr('value', data); + } + +} diff --git a/www/js/jquery.nicescroll.min.js b/www/js/jquery.nicescroll.min.js new file mode 100644 index 00000000..312ddd4d --- /dev/null +++ b/www/js/jquery.nicescroll.min.js @@ -0,0 +1,111 @@ +/* jquery.nicescroll 3.2.0 InuYaksa*2013 MIT http://areaaperta.com/nicescroll */(function(e){var y=!1,D=!1,J=5E3,K=2E3,x=0,L=function(){var e=document.getElementsByTagName("script"),e=e[e.length-1].src.split("?")[0];return 0f){if(b.getScrollTop()>=b.page.maxh)return!0}else if(0>=b.getScrollTop())return!0;b.scrollmom&&b.scrollmom.stop(); +b.lastdeltay+=f;b.debounced("mousewheely",function(){var d=b.lastdeltay;b.lastdeltay=0;b.rail.drag||b.doScrollBy(d)},120)}d.stopImmediatePropagation();return d.preventDefault()}var b=this;this.version="3.4.0";this.name="nicescroll";this.me=c;this.opt={doc:e("body"),win:!1};e.extend(this.opt,F);this.opt.snapbackspeed=80;if(k)for(var q in b.opt)"undefined"!=typeof k[q]&&(b.opt[q]=k[q]);this.iddoc=(this.doc=b.opt.doc)&&this.doc[0]?this.doc[0].id||"":"";this.ispage=/BODY|HTML/.test(b.opt.win?b.opt.win[0].nodeName: +this.doc[0].nodeName);this.haswrapper=!1!==b.opt.win;this.win=b.opt.win||(this.ispage?e(window):this.doc);this.docscroll=this.ispage&&!this.haswrapper?e(window):this.win;this.body=e("body");this.iframe=this.isfixed=this.viewport=!1;this.isiframe="IFRAME"==this.doc[0].nodeName&&"IFRAME"==this.win[0].nodeName;this.istextarea="TEXTAREA"==this.win[0].nodeName;this.forcescreen=!1;this.canshowonmouseevent="scroll"!=b.opt.autohidemode;this.page=this.view=this.onzoomout=this.onzoomin=this.onscrollcancel= +this.onscrollend=this.onscrollstart=this.onclick=this.ongesturezoom=this.onkeypress=this.onmousewheel=this.onmousemove=this.onmouseup=this.onmousedown=!1;this.scroll={x:0,y:0};this.scrollratio={x:0,y:0};this.cursorheight=20;this.scrollvaluemax=0;this.observerremover=this.observer=this.scrollmom=this.scrollrunning=this.checkrtlmode=!1;do this.id="ascrail"+K++;while(document.getElementById(this.id));this.hasmousefocus=this.hasfocus=this.zoomactive=this.zoom=this.selectiondrag=this.cursorfreezed=this.cursor= +this.rail=!1;this.visibility=!0;this.hidden=this.locked=!1;this.cursoractive=!0;this.overflowx=b.opt.overflowx;this.overflowy=b.opt.overflowy;this.nativescrollingarea=!1;this.checkarea=0;this.events=[];this.saved={};this.delaylist={};this.synclist={};this.lastdeltay=this.lastdeltax=0;this.detected=M();var f=e.extend({},this.detected);this.ishwscroll=(this.canhwscroll=f.hastransform&&b.opt.hwacceleration)&&b.haswrapper;this.istouchcapable=!1;f.cantouch&&(f.ischrome&&!f.isios&&!f.isandroid)&&(this.istouchcapable= +!0,f.cantouch=!1);f.cantouch&&(f.ismozilla&&!f.isios)&&(this.istouchcapable=!0,f.cantouch=!1);b.opt.enablemouselockapi||(f.hasmousecapture=!1,f.haspointerlock=!1);this.delayed=function(d,c,g,e){var f=b.delaylist[d],h=(new Date).getTime();if(!e&&f&&f.tt)return!1;f&&f.tt&&clearTimeout(f.tt);if(f&&f.last+g>h&&!f.tt)b.delaylist[d]={last:h+g,tt:setTimeout(function(){b.delaylist[d].tt=0;c.call()},g)};else if(!f||!f.tt)b.delaylist[d]={last:h,tt:0},setTimeout(function(){c.call()},0)};this.debounced=function(d, +c,g){var f=b.delaylist[d];(new Date).getTime();b.delaylist[d]=c;f||setTimeout(function(){var c=b.delaylist[d];b.delaylist[d]=!1;c.call()},g)};this.synched=function(d,c){b.synclist[d]=c;(function(){b.onsync||(v(function(){b.onsync=!1;for(d in b.synclist){var c=b.synclist[d];c&&c.call(b);b.synclist[d]=!1}}),b.onsync=!0)})();return d};this.unsynched=function(d){b.synclist[d]&&(b.synclist[d]=!1)};this.css=function(d,c){for(var g in c)b.saved.css.push([d,g,d.css(g)]),d.css(g,c[g])};this.scrollTop=function(d){return"undefined"== +typeof d?b.getScrollTop():b.setScrollTop(d)};this.scrollLeft=function(d){return"undefined"==typeof d?b.getScrollLeft():b.setScrollLeft(d)};BezierClass=function(b,c,g,f,e,h,l){this.st=b;this.ed=c;this.spd=g;this.p1=f||0;this.p2=e||1;this.p3=h||0;this.p4=l||1;this.ts=(new Date).getTime();this.df=this.ed-this.st};BezierClass.prototype={B2:function(b){return 3*b*b*(1-b)},B3:function(b){return 3*b*(1-b)*(1-b)},B4:function(b){return(1-b)*(1-b)*(1-b)},getNow:function(){var b=1-((new Date).getTime()-this.ts)/ +this.spd,c=this.B2(b)+this.B3(b)+this.B4(b);return 0>b?this.ed:this.st+Math.round(this.df*c)},update:function(b,c){this.st=this.getNow();this.ed=b;this.spd=c;this.ts=(new Date).getTime();this.df=this.ed-this.st;return this}};if(this.ishwscroll){this.doc.translate={x:0,y:0,tx:"0px",ty:"0px"};f.hastranslate3d&&f.isios&&this.doc.css("-webkit-backface-visibility","hidden");var r=function(){var d=b.doc.css(f.trstyle);return d&&"matrix"==d.substr(0,6)?d.replace(/^.*\((.*)\)$/g,"$1").replace(/px/g,"").split(/, +/): +!1};this.getScrollTop=function(d){if(!d){if(d=r())return 16==d.length?-d[13]:-d[5];if(b.timerscroll&&b.timerscroll.bz)return b.timerscroll.bz.getNow()}return b.doc.translate.y};this.getScrollLeft=function(d){if(!d){if(d=r())return 16==d.length?-d[12]:-d[4];if(b.timerscroll&&b.timerscroll.bh)return b.timerscroll.bh.getNow()}return b.doc.translate.x};this.notifyScrollEvent=document.createEvent?function(b){var c=document.createEvent("UIEvents");c.initUIEvent("scroll",!1,!0,window,1);b.dispatchEvent(c)}: +document.fireEvent?function(b){var c=document.createEventObject();b.fireEvent("onscroll");c.cancelBubble=!0}:function(b,c){};f.hastranslate3d&&b.opt.enabletranslate3d?(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate3d("+b.doc.translate.tx+ +","+b.doc.translate.ty+",0px)");c||b.notifyScrollEvent(b.win[0])}):(this.setScrollTop=function(d,c){b.doc.translate.y=d;b.doc.translate.ty=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])},this.setScrollLeft=function(d,c){b.doc.translate.x=d;b.doc.translate.tx=-1*d+"px";b.doc.css(f.trstyle,"translate("+b.doc.translate.tx+","+b.doc.translate.ty+")");c||b.notifyScrollEvent(b.win[0])})}else this.getScrollTop=function(){return b.docscroll.scrollTop()}, +this.setScrollTop=function(d){return b.docscroll.scrollTop(d)},this.getScrollLeft=function(){return b.docscroll.scrollLeft()},this.setScrollLeft=function(d){return b.docscroll.scrollLeft(d)};this.getTarget=function(b){return!b?!1:b.target?b.target:b.srcElement?b.srcElement:!1};this.hasParent=function(b,c){if(!b)return!1;for(var g=b.target||b.srcElement||b||!1;g&&g.id!=c;)g=g.parentNode||!1;return!1!==g};var u={thin:1,medium:3,thick:5};this.getOffset=function(){if(b.isfixed)return{top:parseFloat(b.win.css("top")), +left:parseFloat(b.win.css("left"))};if(!b.viewport)return b.win.offset();var d=b.win.offset(),c=b.viewport.offset();return{top:d.top-c.top+b.viewport.scrollTop(),left:d.left-c.left+b.viewport.scrollLeft()}};this.updateScrollBar=function(d){if(b.ishwscroll)b.rail.css({height:b.win.innerHeight()}),b.railh&&b.railh.css({width:b.win.innerWidth()});else{var c=b.getOffset(),g=c.top,f=c.left,g=g+l(b.win,"border-top-width",!0);b.win.outerWidth();b.win.innerWidth();var f=f+(b.rail.align?b.win.outerWidth()- +l(b.win,"border-right-width")-b.rail.width:l(b.win,"border-left-width")),e=b.opt.railoffset;e&&(e.top&&(g+=e.top),b.rail.align&&e.left&&(f+=e.left));b.locked||b.rail.css({top:g,left:f,height:d?d.h:b.win.innerHeight()});b.zoom&&b.zoom.css({top:g+1,left:1==b.rail.align?f-20:f+b.rail.width+4});b.railh&&!b.locked&&(g=c.top,f=c.left,d=b.railh.align?g+l(b.win,"border-top-width",!0)+b.win.innerHeight()-b.railh.height:g+l(b.win,"border-top-width",!0),f+=l(b.win,"border-left-width"),b.railh.css({top:d,left:f, +width:b.railh.width}))}};this.doRailClick=function(d,c,g){var f;b.locked||(b.cancelEvent(d),c?(c=g?b.doScrollLeft:b.doScrollTop,f=g?(d.pageX-b.railh.offset().left-b.cursorwidth/2)*b.scrollratio.x:(d.pageY-b.rail.offset().top-b.cursorheight/2)*b.scrollratio.y,c(f)):(c=g?b.doScrollLeftBy:b.doScrollBy,f=g?b.scroll.x:b.scroll.y,d=g?d.pageX-b.railh.offset().left:d.pageY-b.rail.offset().top,g=g?b.view.w:b.view.h,f>=d?c(g):c(-g)))};b.hasanimationframe=v;b.hascancelanimationframe=w;b.hasanimationframe?b.hascancelanimationframe|| +(w=function(){b.cancelAnimationFrame=!0}):(v=function(b){return setTimeout(b,15-Math.floor(+new Date/1E3)%16)},w=clearInterval);this.init=function(){b.saved.css=[];if(f.isie7mobile)return!0;f.hasmstouch&&b.css(b.ispage?e("html"):b.win,{"-ms-touch-action":"none"});b.zindex="auto";b.zindex=!b.ispage&&"auto"==b.opt.zindex?h()||"auto":b.opt.zindex;!b.ispage&&"auto"!=b.zindex&&b.zindex>x&&(x=b.zindex);b.isie&&(0==b.zindex&&"auto"==b.opt.zindex)&&(b.zindex="auto");if(!b.ispage||!f.cantouch&&!f.isieold&& +!f.isie9mobile){var d=b.docscroll;b.ispage&&(d=b.haswrapper?b.win:b.doc);f.isie9mobile||b.css(d,{"overflow-y":"hidden"});b.ispage&&f.isie7&&("BODY"==b.doc[0].nodeName?b.css(e("html"),{"overflow-y":"hidden"}):"HTML"==b.doc[0].nodeName&&b.css(e("body"),{"overflow-y":"hidden"}));f.isios&&(!b.ispage&&!b.haswrapper)&&b.css(e("body"),{"-webkit-overflow-scrolling":"touch"});var c=e(document.createElement("div"));c.css({position:"relative",top:0,"float":"right",width:b.opt.cursorwidth,height:"0px","background-color":b.opt.cursorcolor, +border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius,"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.hborder=parseFloat(c.outerHeight()-c.innerHeight());b.cursor=c;var g=e(document.createElement("div"));g.attr("id",b.id);g.addClass("nicescroll-rails");var l,k,n=["left","right"],G;for(G in n)k=n[G],(l=b.opt.railpadding[k])?g.css("padding-"+k,l+"px"):b.opt.railpadding[k]=0;g.append(c);g.width=Math.max(parseFloat(b.opt.cursorwidth), +c.outerWidth())+b.opt.railpadding.left+b.opt.railpadding.right;g.css({width:g.width+"px",zIndex:b.zindex,background:b.opt.background,cursor:"default"});g.visibility=!0;g.scrollable=!0;g.align="left"==b.opt.railalign?0:1;b.rail=g;c=b.rail.drag=!1;b.opt.boxzoom&&(!b.ispage&&!f.isieold)&&(c=document.createElement("div"),b.bind(c,"click",b.doZoom),b.zoom=e(c),b.zoom.css({cursor:"pointer","z-index":b.zindex,backgroundImage:"url("+L+"zoomico.png)",height:18,width:18,backgroundPosition:"0px 0px"}),b.opt.dblclickzoom&& +b.bind(b.win,"dblclick",b.doZoom),f.cantouch&&b.opt.gesturezoom&&(b.ongesturezoom=function(d){1.5d.scale&&b.doZoomOut(d);return b.cancelEvent(d)},b.bind(b.win,"gestureend",b.ongesturezoom)));b.railh=!1;if(b.opt.horizrailenabled){b.css(d,{"overflow-x":"hidden"});c=e(document.createElement("div"));c.css({position:"relative",top:0,height:b.opt.cursorwidth,width:"0px","background-color":b.opt.cursorcolor,border:b.opt.cursorborder,"background-clip":"padding-box","-webkit-border-radius":b.opt.cursorborderradius, +"-moz-border-radius":b.opt.cursorborderradius,"border-radius":b.opt.cursorborderradius});c.wborder=parseFloat(c.outerWidth()-c.innerWidth());b.cursorh=c;var m=e(document.createElement("div"));m.attr("id",b.id+"-hr");m.addClass("nicescroll-rails");m.height=Math.max(parseFloat(b.opt.cursorwidth),c.outerHeight());m.css({height:m.height+"px",zIndex:b.zindex,background:b.opt.background});m.append(c);m.visibility=!0;m.scrollable=!0;m.align="top"==b.opt.railvalign?0:1;b.railh=m;b.railh.drag=!1}b.ispage? +(g.css({position:"fixed",top:"0px",height:"100%"}),g.align?g.css({right:"0px"}):g.css({left:"0px"}),b.body.append(g),b.railh&&(m.css({position:"fixed",left:"0px",width:"100%"}),m.align?m.css({bottom:"0px"}):m.css({top:"0px"}),b.body.append(m))):(b.ishwscroll?("static"==b.win.css("position")&&b.css(b.win,{position:"relative"}),d="HTML"==b.win[0].nodeName?b.body:b.win,b.zoom&&(b.zoom.css({position:"absolute",top:1,right:0,"margin-right":g.width+4}),d.append(b.zoom)),g.css({position:"absolute",top:0}), +g.align?g.css({right:0}):g.css({left:0}),d.append(g),m&&(m.css({position:"absolute",left:0,bottom:0}),m.align?m.css({bottom:0}):m.css({top:0}),d.append(m))):(b.isfixed="fixed"==b.win.css("position"),d=b.isfixed?"fixed":"absolute",b.isfixed||(b.viewport=b.getViewport(b.win[0])),b.viewport&&(b.body=b.viewport,!1==/relative|absolute/.test(b.viewport.css("position"))&&b.css(b.viewport,{position:"relative"})),g.css({position:d}),b.zoom&&b.zoom.css({position:d}),b.updateScrollBar(),b.body.append(g),b.zoom&& +b.body.append(b.zoom),b.railh&&(m.css({position:d}),b.body.append(m))),f.isios&&b.css(b.win,{"-webkit-tap-highlight-color":"rgba(0,0,0,0)","-webkit-touch-callout":"none"}),f.isie&&b.opt.disableoutline&&b.win.attr("hideFocus","true"),f.iswebkit&&b.opt.disableoutline&&b.win.css({outline:"none"}));!1===b.opt.autohidemode?(b.autohidedom=!1,b.rail.css({opacity:b.opt.cursoropacitymax}),b.railh&&b.railh.css({opacity:b.opt.cursoropacitymax})):!0===b.opt.autohidemode?(b.autohidedom=e().add(b.rail),f.isie8&& +(b.autohidedom=b.autohidedom.add(b.cursor)),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh)),b.railh&&f.isie8&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"scroll"==b.opt.autohidemode?(b.autohidedom=e().add(b.rail),b.railh&&(b.autohidedom=b.autohidedom.add(b.railh))):"cursor"==b.opt.autohidemode?(b.autohidedom=e().add(b.cursor),b.railh&&(b.autohidedom=b.autohidedom.add(b.cursorh))):"hidden"==b.opt.autohidemode&&(b.autohidedom=!1,b.hide(),b.locked=!1);if(f.isie9mobile)b.scrollmom=new H(b),b.onmangotouch= +function(d){d=b.getScrollTop();var c=b.getScrollLeft();if(d==b.scrollmom.lastscrolly&&c==b.scrollmom.lastscrollx)return!0;var g=d-b.mangotouch.sy,f=c-b.mangotouch.sx;if(0!=Math.round(Math.sqrt(Math.pow(f,2)+Math.pow(g,2)))){var p=0>g?-1:1,e=0>f?-1:1,h=+new Date;b.mangotouch.lazy&&clearTimeout(b.mangotouch.lazy);80s?s=Math.round(s/2):s>b.page.maxh&&(s=b.page.maxh+Math.round((s-b.page.maxh)/2)):(0>s&&(h=s=0),s>b.page.maxh&&(s=b.page.maxh,h=0));if(b.railh&&b.railh.scrollable){var m=b.rail.drag.sl-k;b.ishwscroll&&b.opt.bouncescroll?0>m?m=Math.round(m/2):m>b.page.maxw&&(m=b.page.maxw+ +Math.round((m-b.page.maxw)/2)):(0>m&&(l=m=0),m>b.page.maxw&&(m=b.page.maxw,l=0))}g=!1;if(b.rail.drag.dl)g=!0,"v"==b.rail.drag.dl?m=b.rail.drag.sl:"h"==b.rail.drag.dl&&(s=b.rail.drag.st);else{var p=Math.abs(p),k=Math.abs(k),n=b.opt.directionlockdeadzone;if("v"==b.rail.drag.ck){if(p>n&&k<=0.3*p)return b.rail.drag=!1,!0;k>n&&(b.rail.drag.dl="f",e("body").scrollTop(e("body").scrollTop()))}else if("h"==b.rail.drag.ck){if(k>n&&p<=0.3*az)return b.rail.drag=!1,!0;p>n&&(b.rail.drag.dl="f",e("body").scrollLeft(e("body").scrollLeft()))}}b.synched("touchmove", +function(){b.rail.drag&&2==b.rail.drag.pt&&(b.prepareTransition&&b.prepareTransition(0),b.rail.scrollable&&b.setScrollTop(s),b.scrollmom.update(l,h),b.railh&&b.railh.scrollable?(b.setScrollLeft(m),b.showCursor(s,m)):b.showCursor(s),f.isie10&&document.selection.clear())});f.ischrome&&b.istouchcapable&&(g=!1);if(g)return b.cancelEvent(d)}}}b.onmousedown=function(d,c){if(!(b.rail.drag&&1!=b.rail.drag.pt)){if(b.locked)return b.cancelEvent(d);b.cancelScroll();b.rail.drag={x:d.clientX,y:d.clientY,sx:b.scroll.x, +sy:b.scroll.y,pt:1,hr:!!c};var g=b.getTarget(d);!b.ispage&&f.hasmousecapture&&g.setCapture();b.isiframe&&!f.hasmousecapture&&(b.saved.csspointerevents=b.doc.css("pointer-events"),b.css(b.doc,{"pointer-events":"none"}));return b.cancelEvent(d)}};b.onmouseup=function(d){if(b.rail.drag&&(f.hasmousecapture&&document.releaseCapture(),b.isiframe&&!f.hasmousecapture&&b.doc.css("pointer-events",b.saved.csspointerevents),1==b.rail.drag.pt))return b.rail.drag=!1,b.cancelEvent(d)};b.onmousemove=function(d){if(b.rail.drag&& +1==b.rail.drag.pt){if(f.ischrome&&0==d.which)return b.onmouseup(d);b.cursorfreezed=!0;if(b.rail.drag.hr){b.scroll.x=b.rail.drag.sx+(d.clientX-b.rail.drag.x);0>b.scroll.x&&(b.scroll.x=0);var c=b.scrollvaluemaxw;b.scroll.x>c&&(b.scroll.x=c)}else b.scroll.y=b.rail.drag.sy+(d.clientY-b.rail.drag.y),0>b.scroll.y&&(b.scroll.y=0),c=b.scrollvaluemax,b.scroll.y>c&&(b.scroll.y=c);b.synched("mousemove",function(){b.rail.drag&&1==b.rail.drag.pt&&(b.showCursor(),b.rail.drag.hr?b.doScrollLeft(Math.round(b.scroll.x* +b.scrollratio.x),b.opt.cursordragspeed):b.doScrollTop(Math.round(b.scroll.y*b.scrollratio.y),b.opt.cursordragspeed))});return b.cancelEvent(d)}};if(f.cantouch||b.opt.touchbehavior)b.onpreventclick=function(d){if(b.preventclick)return b.preventclick.tg.onclick=b.preventclick.click,b.preventclick=!1,b.cancelEvent(d)},b.bind(b.win,"mousedown",b.ontouchstart),b.onclick=f.isios?!1:function(d){return b.lastmouseup?(b.lastmouseup=!1,b.cancelEvent(d)):!0},b.opt.grabcursorenabled&&f.cursorgrabvalue&&(b.css(b.ispage? +b.doc:b.win,{cursor:f.cursorgrabvalue}),b.css(b.rail,{cursor:f.cursorgrabvalue}));else{var r=function(d){if(b.selectiondrag){if(d){var c=b.win.outerHeight();d=d.pageY-b.selectiondrag.top;0=c&&(d-=c);b.selectiondrag.df=d}0!=b.selectiondrag.df&&(b.doScrollBy(2*-Math.floor(b.selectiondrag.df/6)),b.debounced("doselectionscroll",function(){r()},50))}};b.hasTextSelected="getSelection"in document?function(){return 0b.page.maxh?b.doScrollTop(b.page.maxh):(b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y)), +b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x)),b.cursoractive&&b.noticeCursor());b.scroll.y&&0==b.getScrollTop()&&b.doScrollTo(Math.floor(b.scroll.y*b.scrollratio.y));return b};this.resize=b.onResize;this.lazyResize=function(d){d=isNaN(d)?30:d;b.delayed("resize",b.resize,d);return b};this._bind=function(d,c,g,f){b.events.push({e:d,n:c,f:g,b:f,q:!1});d.addEventListener?d.addEventListener(c,g,f||!1):d.attachEvent?d.attachEvent("on"+c,g):d["on"+c]=g};this.jqbind=function(d,c,g){b.events.push({e:d, +n:c,f:g,q:!0});e(d).bind(c,g)};this.bind=function(d,c,g,e){var h="jquery"in d?d[0]:d;"mousewheel"==c?"onwheel"in b.win?b._bind(h,"wheel",g,e||!1):(d="undefined"!=typeof document.onmousewheel?"mousewheel":"DOMMouseScroll",n(h,d,g,e||!1),"DOMMouseScroll"==d&&n(h,"MozMousePixelScroll",g,e||!1)):h.addEventListener?(f.cantouch&&/mouseup|mousedown|mousemove/.test(c)&&b._bind(h,"mousedown"==c?"touchstart":"mouseup"==c?"touchend":"touchmove",function(b){if(b.touches){if(2>b.touches.length){var d=b.touches.length? +b.touches[0]:b;d.original=b;g.call(this,d)}}else b.changedTouches&&(d=b.changedTouches[0],d.original=b,g.call(this,d))},e||!1),b._bind(h,c,g,e||!1),f.cantouch&&"mouseup"==c&&b._bind(h,"touchcancel",g,e||!1)):b._bind(h,c,function(d){if((d=d||window.event||!1)&&d.srcElement)d.target=d.srcElement;"pageY"in d||(d.pageX=d.clientX+document.documentElement.scrollLeft,d.pageY=d.clientY+document.documentElement.scrollTop);return!1===g.call(h,d)||!1===e?b.cancelEvent(d):!0})};this._unbind=function(b,c,g,f){b.removeEventListener? +b.removeEventListener(c,g,f):b.detachEvent?b.detachEvent("on"+c,g):b["on"+c]=!1};this.unbindAll=function(){for(var d=0;d +(b.newscrolly-h)*(e-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();!1==b.opt.bouncescroll&&(0>e?e=0:e>b.page.maxh&&(e=b.page.maxh),0>c?c=0:c>b.page.maxw&&(c=b.page.maxw));if(b.scrollrunning&&c==b.newscrollx&&e==b.newscrolly)return!1;b.newscrolly=e;b.newscrollx=c;b.newscrollspeed=g||!1;if(b.timer)return!1;b.timer=setTimeout(function(){var g=b.getScrollTop(),h=b.getScrollLeft(),l,k;l=c-h;k=e-g;l=Math.round(Math.sqrt(Math.pow(l,2)+Math.pow(k,2)));l=b.newscrollspeed&&1=b.newscrollspeed&&(l*=b.newscrollspeed);b.prepareTransition(l,!0);b.timerscroll&&b.timerscroll.tm&&clearInterval(b.timerscroll.tm);0c?c=0:c>b.page.maxh&&(c=b.page.maxh);0>e?e=0:e>b.page.maxw&&(e=b.page.maxw);if(c!=b.newscrolly||e!=b.newscrollx)return b.doScrollPos(e,c,b.opt.snapbackspeed);b.onscrollend&&b.scrollrunning&&b.onscrollend.call(b,{type:"scrollend",current:{x:e,y:c},end:{x:b.newscrollx,y:b.newscrolly}});b.scrollrunning= +!1}):(this.doScrollLeft=function(c,f){var g=b.scrollrunning?b.newscrolly:b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.scrollrunning?b.newscrollx:b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){function e(){if(b.cancelAnimationFrame)return!0;b.scrollrunning=!0;if(r=1-r)return b.timer=v(e)||1;var c=0,d=sy=b.getScrollTop();if(b.dst.ay){var d=b.bzscroll?b.dst.py+b.bzscroll.getNow()*b.dst.ay:b.newscrolly,g=d-sy;if(0>g&&db.newscrolly)d= +b.newscrolly;b.setScrollTop(d);d==b.newscrolly&&(c=1)}else c=1;var f=sx=b.getScrollLeft();if(b.dst.ax){f=b.bzscroll?b.dst.px+b.bzscroll.getNow()*b.dst.ax:b.newscrollx;g=f-sx;if(0>g&&fb.newscrollx)f=b.newscrollx;b.setScrollLeft(f);f==b.newscrollx&&(c+=1)}else c+=1;2==c?(b.timer=0,b.cursorfreezed=!1,b.bzscroll=!1,b.scrollrunning=!1,0>d?d=0:d>b.page.maxh&&(d=b.page.maxh),0>f?f=0:f>b.page.maxw&&(f=b.page.maxw),f!=b.newscrollx||d!=b.newscrolly?b.doScrollPos(f,d):b.onscrollend&&b.onscrollend.call(b, +{type:"scrollend",current:{x:sx,y:sy},end:{x:b.newscrollx,y:b.newscrolly}})):b.timer=v(e)||1}f="undefined"==typeof f||!1===f?b.getScrollTop(!0):f;if(b.timer&&b.newscrolly==f&&b.newscrollx==c)return!0;b.timer&&w(b.timer);b.timer=0;var h=b.getScrollTop(),l=b.getScrollLeft();(0>(b.newscrolly-h)*(f-h)||0>(b.newscrollx-l)*(c-l))&&b.cancelScroll();b.newscrolly=f;b.newscrollx=c;if(!b.bouncescroll||!b.rail.visibility)0>b.newscrolly?b.newscrolly=0:b.newscrolly>b.page.maxh&&(b.newscrolly=b.page.maxh);if(!b.bouncescroll|| +!b.railh.visibility)0>b.newscrollx?b.newscrollx=0:b.newscrollx>b.page.maxw&&(b.newscrollx=b.page.maxw);b.dst={};b.dst.x=c-l;b.dst.y=f-h;b.dst.px=l;b.dst.py=h;var k=Math.round(Math.sqrt(Math.pow(b.dst.x,2)+Math.pow(b.dst.y,2)));b.dst.ax=b.dst.x/k;b.dst.ay=b.dst.y/k;var n=0,q=k;0==b.dst.x?(n=h,q=f,b.dst.ay=1,b.dst.py=0):0==b.dst.y&&(n=l,q=c,b.dst.ax=1,b.dst.px=0);k=b.getTransitionSpeed(k);g&&1>=g&&(k*=g);b.bzscroll=0=b.page.maxh||l==b.page.maxw&&c>=b.page.maxw)&&b.checkContentSize();var r=1;b.cancelAnimationFrame=!1;b.timer=1;b.onscrollstart&&!b.scrollrunning&&b.onscrollstart.call(b,{type:"scrollstart",current:{x:l,y:h},request:{x:c,y:f},end:{x:b.newscrollx,y:b.newscrolly},speed:k});e();(h==b.page.maxh&&f>=h||l==b.page.maxw&&c>=l)&&b.checkContentSize();b.noticeCursor()}},this.cancelScroll=function(){b.timer&&w(b.timer);b.timer=0;b.bzscroll=!1;b.scrollrunning=!1;return b}):(this.doScrollLeft=function(c, +f){var g=b.getScrollTop();b.doScrollPos(c,g,f)},this.doScrollTop=function(c,f){var g=b.getScrollLeft();b.doScrollPos(g,c,f)},this.doScrollPos=function(c,f,g){var e=c>b.page.maxw?b.page.maxw:c;0>e&&(e=0);var h=f>b.page.maxh?b.page.maxh:f;0>h&&(h=0);b.synched("scroll",function(){b.setScrollTop(h);b.setScrollLeft(e)})},this.cancelScroll=function(){});this.doScrollBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.y-c)*b.scrollratio.y):(b.timer?b.newscrolly:b.getScrollTop(!0))-c;if(b.bouncescroll){var e= +Math.round(b.view.h/2);g<-e?g=-e:g>b.page.maxh+e&&(g=b.page.maxh+e)}b.cursorfreezed=!1;py=b.getScrollTop(!0);if(0>g&&0>=py)return b.noticeCursor();if(g>b.page.maxh&&py>=b.page.maxh)return b.checkContentSize(),b.noticeCursor();b.doScrollTop(g)};this.doScrollLeftBy=function(c,f){var g=0,g=f?Math.floor((b.scroll.x-c)*b.scrollratio.x):(b.timer?b.newscrollx:b.getScrollLeft(!0))-c;if(b.bouncescroll){var e=Math.round(b.view.w/2);g<-e?g=-e:g>b.page.maxw+e&&(g=b.page.maxw+e)}b.cursorfreezed=!1;px=b.getScrollLeft(!0); +if(0>g&&0>=px||g>b.page.maxw&&px>=b.page.maxw)return b.noticeCursor();b.doScrollLeft(g)};this.doScrollTo=function(c,f){f&&Math.round(c*b.scrollratio.y);b.cursorfreezed=!1;b.doScrollTop(c)};this.checkContentSize=function(){var c=b.getContentSize();(c.h!=b.page.h||c.w!=b.page.w)&&b.resize(!1,c)};b.onscroll=function(c){b.rail.drag||b.cursorfreezed||b.synched("scroll",function(){b.scroll.y=Math.round(b.getScrollTop()*(1/b.scrollratio.y));b.railh&&(b.scroll.x=Math.round(b.getScrollLeft()*(1/b.scrollratio.x))); +b.noticeCursor()})};b.bind(b.docscroll,"scroll",b.onscroll);this.doZoomIn=function(c){if(!b.zoomactive){b.zoomactive=!0;b.zoomrestore={style:{}};var h="position top left zIndex backgroundColor marginTop marginBottom marginLeft marginRight".split(" "),g=b.win[0].style,l;for(l in h){var k=h[l];b.zoomrestore.style[k]="undefined"!=typeof g[k]?g[k]:""}b.zoomrestore.style.width=b.win.css("width");b.zoomrestore.style.height=b.win.css("height");b.zoomrestore.padding={w:b.win.outerWidth()-b.win.width(),h:b.win.outerHeight()- +b.win.height()};f.isios4&&(b.zoomrestore.scrollTop=e(window).scrollTop(),e(window).scrollTop(0));b.win.css({position:f.isios4?"absolute":"fixed",top:0,left:0,"z-index":x+100,margin:"0px"});h=b.win.css("backgroundColor");(""==h||/transparent|rgba\(0, 0, 0, 0\)|rgba\(0,0,0,0\)/.test(h))&&b.win.css("backgroundColor","#fff");b.rail.css({"z-index":x+101});b.zoom.css({"z-index":x+102});b.zoom.css("backgroundPosition","0px -18px");b.resizeZoom();b.onzoomin&&b.onzoomin.call(b);return b.cancelEvent(c)}};this.doZoomOut= +function(c){if(b.zoomactive)return b.zoomactive=!1,b.win.css("margin",""),b.win.css(b.zoomrestore.style),f.isios4&&e(window).scrollTop(b.zoomrestore.scrollTop),b.rail.css({"z-index":b.zindex}),b.zoom.css({"z-index":b.zindex}),b.zoomrestore=!1,b.zoom.css("backgroundPosition","0px 0px"),b.onResize(),b.onzoomout&&b.onzoomout.call(b),b.cancelEvent(c)};this.doZoom=function(c){return b.zoomactive?b.doZoomOut(c):b.doZoomIn(c)};this.resizeZoom=function(){if(b.zoomactive){var c=b.getScrollTop();b.win.css({width:e(window).width()- +b.zoomrestore.padding.w+"px",height:e(window).height()-b.zoomrestore.padding.h+"px"});b.onResize();b.setScrollTop(Math.min(b.page.maxh,c))}};this.init();e.nicescroll.push(this)},H=function(e){var c=this;this.nc=e;this.steptime=this.lasttime=this.speedy=this.speedx=this.lasty=this.lastx=0;this.snapy=this.snapx=!1;this.demuly=this.demulx=0;this.lastscrolly=this.lastscrollx=-1;this.timer=this.chky=this.chkx=0;this.time=function(){return+new Date};this.reset=function(e,l){c.stop();var k=c.time();c.steptime= +0;c.lasttime=k;c.speedx=0;c.speedy=0;c.lastx=e;c.lasty=l;c.lastscrollx=-1;c.lastscrolly=-1};this.update=function(e,l){var k=c.time();c.steptime=k-c.lasttime;c.lasttime=k;var k=l-c.lasty,t=e-c.lastx,b=c.nc.getScrollTop(),q=c.nc.getScrollLeft(),b=b+k,q=q+t;c.snapx=0>q||q>c.nc.page.maxw;c.snapy=0>b||b>c.nc.page.maxh;c.speedx=t;c.speedy=k;c.lastx=e;c.lasty=l};this.stop=function(){c.nc.unsynched("domomentum2d");c.timer&&clearTimeout(c.timer);c.timer=0;c.lastscrollx=-1;c.lastscrolly=-1};this.doSnapy=function(e, +l){var k=!1;0>l?(l=0,k=!0):l>c.nc.page.maxh&&(l=c.nc.page.maxh,k=!0);0>e?(e=0,k=!0):e>c.nc.page.maxw&&(e=c.nc.page.maxw,k=!0);k&&c.nc.doScrollPos(e,l,c.nc.opt.snapbackspeed)};this.doMomentum=function(e){var l=c.time(),k=e?l+e:c.lasttime;e=c.nc.getScrollLeft();var t=c.nc.getScrollTop(),b=c.nc.page.maxh,q=c.nc.page.maxw;c.speedx=0=l-k;if(0>t||t>b||0>e||e>q)k=!1;e=c.speedx&&k?c.speedx:!1;if(c.speedy&&k&&c.speedy||e){var f=Math.max(16, +c.steptime);50r||r>q))e=0.1;if(c.speedy&&(u=Math.floor(c.lastscrolly-c.speedy*(1-c.demulxy)),c.lastscrolly=u,0>u||u>b))e=0.1;c.demulxy=Math.min(1,c.demulxy+e);c.nc.synched("domomentum2d", +function(){c.speedx&&(c.nc.getScrollLeft()!=c.chkx&&c.stop(),c.chkx=r,c.nc.setScrollLeft(r));c.speedy&&(c.nc.getScrollTop()!=c.chky&&c.stop(),c.chky=u,c.nc.setScrollTop(u));c.timer||(c.nc.hideCursor(),c.doSnapy(r,u))});1>c.demulxy?c.timer=setTimeout(d,f):(c.stop(),c.nc.hideCursor(),c.doSnapy(r,u))};d()}else c.doSnapy(c.nc.getScrollLeft(),c.nc.getScrollTop())}},A=e.fn.scrollTop;e.cssHooks.pageYOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")||!1)&&c.ishwscroll?c.getScrollTop():A.call(k)}, +set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollTop(parseInt(c)):A.call(k,c);return this}};e.fn.scrollTop=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollTop():A.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollTop(parseInt(k)):A.call(e(this),k)})};var B=e.fn.scrollLeft;e.cssHooks.pageXOffset={get:function(k,c,h){return(c=e.data(k,"__nicescroll")|| +!1)&&c.ishwscroll?c.getScrollLeft():B.call(k)},set:function(k,c){var h=e.data(k,"__nicescroll")||!1;h&&h.ishwscroll?h.setScrollLeft(parseInt(c)):B.call(k,c);return this}};e.fn.scrollLeft=function(k){if("undefined"==typeof k){var c=this[0]?e.data(this[0],"__nicescroll")||!1:!1;return c&&c.ishwscroll?c.getScrollLeft():B.call(this)}return this.each(function(){var c=e.data(this,"__nicescroll")||!1;c&&c.ishwscroll?c.setScrollLeft(parseInt(k)):B.call(e(this),k)})};var C=function(k){var c=this;this.length= +0;this.name="nicescrollarray";this.each=function(e){for(var h=0;h Date: Tue, 22 Apr 2014 11:29:43 +0200 Subject: [PATCH 297/340] solved & unsolved msg design --- views/html/quests/quest.tpl | 17 +++++++++-------- www/css/desktop.css | 4 +++- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 5256b4f7..72a6d37e 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -38,14 +38,15 @@ -
                    - -

                    -

                    - -

                    -

                    - + +
                    +

                    +

                    + +
                    +

                    +

                    +
                    diff --git a/www/css/desktop.css b/www/css/desktop.css index 4bb219d1..cae344db 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -237,6 +237,9 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo /** Quest Types **/ +.solved,.error{margin-top:15px;padding:5px 15px;border:1px solid #4F8A10;background:#DFF2BF;color:#4F8A10;border-radius:3px} +.error{border:1px solid #850000;background:#ebd3d3;color:#850000} +.solved p,.error p{margin-bottom:5px} .mchoice{list-style-type:lower-alpha;list-style-position:inside} .mchoice li{margin:0 0 10px 0} @@ -246,7 +249,6 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .submit{padding:15px;background:#eae8e4;border-radius:3px;margin-bottom:15px} .submit p{margin:15px 0 0 0;font-weight:bold} .submit ul{list-style-type:square;margin-left:20px} -.error{padding:2px 5px;border:1px solid #850000;background:#ebd3d3;color:#850000;border-radius:3px} .textinput input[type=text]{height:16px} From c596beec4c8283ac7b44f55b2a6d64e9e134c559 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 11:48:29 +0200 Subject: [PATCH 298/340] achievement list formatting fix --- views/html/achievements/index.tpl | 5 +---- www/css/desktop.css | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/views/html/achievements/index.tpl b/views/html/achievements/index.tpl index 051b1d1e..2d63d0e2 100644 --- a/views/html/achievements/index.tpl +++ b/views/html/achievements/index.tpl @@ -56,10 +56,7 @@ -

                    -

                    - format(new \DateTime($achievement['created'])))?> -

                    +

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

                    diff --git a/www/css/desktop.css b/www/css/desktop.css index cae344db..cefaf583 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -209,7 +209,7 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .achmnts li{background:#fff;margin-bottom:10px;padding:15px;border-radius:3px} .achmnts img{width:55px;height:55px;float:left;margin-right:15px;border-radius:3px} -.achmnts p{margin:0 0 4px} +.achmnts p,.achmnts h3{margin:0 0 4px;font-size:1em} .achmnts .unlcked{margin-top:3px;font-size:.875em;font-weight:normal;display:block} .achmnts .desc{font-size:.875em;padding-top:10px} .achmnts .prgrss{margin-top:15px} From d222ca96b6bee7d047a54793cffdd30390cb920d Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 11:54:52 +0200 Subject: [PATCH 299/340] add press text to introduction page --- views/html/introduction/index.tpl | 50 ++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index 3dc754ae..92d071fd 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -1,6 +1,4 @@

                    -

                    Ein Projekt zum Thema „Gamification im Lehrumfeld“ – basierend auf Die Legende von Zyren.

                    -

                    @@ -15,22 +13,38 @@
                    -

                    Entwickler

                    +

                    Bereits im Sommersemester 2013 wurde das Projekt „Die Legende von Zyren“ unterstützt durch den Lehrförderungsfond der Heinrich-Heine-Universität Düsseldorf ins Leben gerufen, um die Inhalte der Vorlesung „Wissensrepräsentation“ den Studierenden des Faches Informationswissenschaft mit Hilfe von Spielelementen und -modellen zu vermitteln. Die innovative Lernumgebung besteht aus einem virtuellen Textadventure, dass über eine webbasierte Plattform zugänglich ist und realen Spielen in einer Präsenzveranstaltung, in denen die Studierenden unmittelbar in das Abenteuer eintauchen und in Teams spielerisch gegeneinander antreten.

                    +

                    Auf der Plattform spielt sich jeder der Studierenden mit seinem virtuellen Avatar durch das Reich von Zyren und erlernt und vertieft auf spielerische Weise die Inhalte der Vorlesung „Wissensrepräsentation“, die in Form von Herausforderungen oder Rätseln in das Abenteuer eingebunden wurden und über den Fortlauf und den Erfolg des Spiels entscheiden.

                    +

                    In der zusätzlichen Präsenzveranstaltung tauchen die Studierenden direkt in das Abenteuer ein und die vertiefen die Inhalte spielerisch. Hier schließen sich die Studierenden in Teams (Gilden) zusammen, müssen eigenverantwortlich Lerninhalte erarbeiten, Probleme lösen und in speziellen Gildenaufgaben gegen andere Teams antreten, um ihr kollaborativ erarbeitetes Wissen auf die Probe zu stellen.

                    +

                    Für jede erfolgreiche absolvierte Herausforderung auf der Plattform oder in der Übung erhalten die Studierenden Erfahrungspunkte und Belohnungen.

                    +

                    Um das Konzept auch anderen Fachbereichen und Lehrveranstaltungen zugänglich zu machen, wurde im Frühjahr 2014 das Projekt Questlab (Arbeitstitel „The Legend of Z“) gestartet um das Konzept zu generalisieren. Lehrende können die Plattform nun nutzen um eigene Aufgaben (Quests) zu kreieren und hochzuladen und sie optional in eine Geschichte einzubinden, die sie selbst gestalten können. Zudem wurde das Responsive Design überarbeitet und bietet nun optimalen Zugriff auf die Plattform über alle mobilen Endgeräte.

                    + +

                    Die Legende von Zyren in der Presse

                    -

                    - Heinrich-Heine-Universität Düsseldorf -

                    +

                    Das Team

                    +

                    Projektleitung:

                    +
                      +
                    • Kathrin Knautz
                    • +
                    +

                    Entwicklung und Evaluation des Prototypens:

                    +
                      +
                    • Lisa Orszullok
                    • +
                    • Simone Soubusta
                    • +
                    • Julia Göretz
                    • +
                    • Anja Wintermeyer
                    • +
                    +

                    Entwicklung „The Legend of Z“:

                    +
                      +
                    • Oliver Hanraths
                    • +
                    • Daniel Miskovic
                    • +
                    From fdf20b33786597f98fee2bf7ecc99358a9a10651 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 11:55:51 +0200 Subject: [PATCH 300/340] add login form to 403 error pages and add referrer param for user login --- controllers/ErrorController.inc | 3 +++ controllers/UsersController.inc | 12 ++++++++++-- views/html/error/index.tpl | 15 +++++++++++++++ views/html/introduction/index.tpl | 2 +- views/html/users/login.tpl | 5 ++++- 5 files changed, 33 insertions(+), 4 deletions(-) diff --git a/controllers/ErrorController.inc b/controllers/ErrorController.inc index efdae4f4..a27e3afd 100644 --- a/controllers/ErrorController.inc +++ b/controllers/ErrorController.inc @@ -23,6 +23,8 @@ + + /** * Action: index. * @@ -41,6 +43,7 @@ // Display statuscode and message $this->set('code', $httpStatusCode); $this->set('string', \nre\core\WebUtils::$httpStrings[$httpStatusCode]); + $this->set('userId', $this->Auth->getUserId()); } } diff --git a/controllers/UsersController.inc b/controllers/UsersController.inc index 90e50912..f0888cce 100644 --- a/controllers/UsersController.inc +++ b/controllers/UsersController.inc @@ -117,11 +117,13 @@ public function login() { $username = ''; + $referrer = null; // Log the user in if($this->request->getRequestMethod() == 'POST' && !is_null($this->request->getPostParam('login'))) { $username = $this->request->getPostParam('username'); + $referrer = $this->request->getPostParam('referrer'); $userId = $this->Users->login( $username, $this->request->getPostParam('password') @@ -131,14 +133,20 @@ { $this->Auth->setUserId($userId); $user = $this->Users->getUserById($userId); - - $this->redirect($this->linker->link(array($user['url']), 1)); + + if(!empty($referrer)) { + $this->redirect($referrer); + } + else { + $this->redirect($this->linker->link(array($user['url']), 1)); + } } } // Pass data to view $this->set('username', $username); + $this->set('referrer', $referrer); $this->set('failed', ($this->request->getRequestMethod() == 'POST')); } diff --git a/views/html/error/index.tpl b/views/html/error/index.tpl index 0f68bc07..a0b02462 100644 --- a/views/html/error/index.tpl +++ b/views/html/error/index.tpl @@ -1,2 +1,17 @@

                    :

                    + + +

                    +
                    +
                    + +
                    + +
                    +
                    + + + +
                    + diff --git a/views/html/introduction/index.tpl b/views/html/introduction/index.tpl index 92d071fd..42979a96 100644 --- a/views/html/introduction/index.tpl +++ b/views/html/introduction/index.tpl @@ -4,7 +4,7 @@
                    -
                    +

                    diff --git a/views/html/users/login.tpl b/views/html/users/login.tpl index f141f4cd..d51423f0 100644 --- a/views/html/users/login.tpl +++ b/views/html/users/login.tpl @@ -7,9 +7,12 @@
                    -
                    +

                    + + +
                    From e630c6a5c47b50ea8f3a6c995257e226d27c4b6e Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 11:56:30 +0200 Subject: [PATCH 301/340] do not access user data if user is not logged-in in SeminaryRoleController --- app/controllers/SeminaryRoleController.inc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/SeminaryRoleController.inc b/app/controllers/SeminaryRoleController.inc index 69e9a38b..f6f0007e 100644 --- a/app/controllers/SeminaryRoleController.inc +++ b/app/controllers/SeminaryRoleController.inc @@ -80,8 +80,11 @@ // Get Seminary and Character data try { self::$seminary = $this->Seminaries->getSeminaryByUrl($this->request->getParam(3)); - self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); - self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + if(!is_null(self::$user)) + { + self::$user['seminaryroles'] = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById(self::$user['id'], self::$seminary['id'])); + self::$character = $this->Characters->getCharacterForUserAndSeminary(self::$user['id'], self::$seminary['id']); + } } catch(\nre\exceptions\IdNotFoundException $e) { } From 6e238f9a1cefd252d2272fd2c7412812c720040d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 12:05:54 +0200 Subject: [PATCH 302/340] css solved class ambiguity fix --- views/html/quests/quest.tpl | 2 +- www/css/desktop.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 72a6d37e..96b19b08 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -39,7 +39,7 @@ -
                    +

                    diff --git a/www/css/desktop.css b/www/css/desktop.css index cefaf583..42a10445 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -237,9 +237,9 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo /** Quest Types **/ -.solved,.error{margin-top:15px;padding:5px 15px;border:1px solid #4F8A10;background:#DFF2BF;color:#4F8A10;border-radius:3px} +.success,.error{margin-top:15px;padding:5px 15px;border:1px solid #4F8A10;background:#DFF2BF;color:#4F8A10;border-radius:3px} .error{border:1px solid #850000;background:#ebd3d3;color:#850000} -.solved p,.error p{margin-bottom:5px} +.success p,.error p{margin-bottom:5px} .mchoice{list-style-type:lower-alpha;list-style-position:inside} .mchoice li{margin:0 0 10px 0} From 805f7294b0cc98b7afec39c66d0005e0ee93b004 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 12:17:35 +0200 Subject: [PATCH 303/340] new design for already solved quest navigation --- views/html/quests/quest.tpl | 16 +++++++++------- www/css/desktop.css | 4 +++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/views/html/quests/quest.tpl b/views/html/quests/quest.tpl index 96b19b08..6e232bab 100644 --- a/views/html/quests/quest.tpl +++ b/views/html/quests/quest.tpl @@ -58,13 +58,15 @@ -

                    : -

                      - -
                    • -
                    • - -
                    +
                    +

                    : +

                    +

                    diff --git a/www/css/desktop.css b/www/css/desktop.css index 42a10445..5891199b 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -241,6 +241,8 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .error{border:1px solid #850000;background:#ebd3d3;color:#850000} .success p,.error p{margin-bottom:5px} +.solvdmsg{margin-top:20px} + .mchoice{list-style-type:lower-alpha;list-style-position:inside} .mchoice li{margin:0 0 10px 0} .mchoice input[type=checkbox]{display:inline-block;margin:-19px 10px 0 24px;vertical-align:top} @@ -250,7 +252,7 @@ input[type="submit"][disabled]{text-shadow:1px 2px #d48c4e;background:#f9ac69;bo .submit p{margin:15px 0 0 0;font-weight:bold} .submit ul{list-style-type:square;margin-left:20px} -.textinput input[type=text]{height:16px} +.textinput input[type=text]{height:16px;font-size:.875em} .crossword table{width:100%;max-width:800px;border-spacing:2px;border-collapse:separate} .crossword td{background:#d7d4cf;padding:1px} From 0bf109e18f00ab0498ba7221d8ccc43b554e04cc Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 12:52:10 +0200 Subject: [PATCH 304/340] scrolling textbox for seminary group prologue texts --- views/html/questgroups/questgroup.tpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 84c7177c..c373400c 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -12,9 +12,11 @@

                    +

                    +
                    From e0b10444d182f6cacae3c4b6d2f0d5810c75ff8d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 13:02:04 +0200 Subject: [PATCH 305/340] solved quest link hover state --- www/css/desktop.css | 1 + 1 file changed, 1 insertion(+) diff --git a/www/css/desktop.css b/www/css/desktop.css index 5891199b..ae0eef8f 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -108,6 +108,7 @@ aside{display:none} .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:hover{color:#62c5cd} .qglist .qgtitle .solved .fa{color:#bcd75e} .qglist .qgtitle .bonus{background:#f5821f} From 52f9b1025675d053bbc2ff20d3823030637983d6 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 13:46:06 +0200 Subject: [PATCH 306/340] fix language construct mistake for calculating solved-status for Questgroups --- models/QuestgroupsModel.inc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index 26ae9a10..f2e18e30 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -283,7 +283,8 @@ } // Get next Quests - while(!is_null($currentQuest) && $nextQuests = $this->Quests->getNextQuests($currentQuest['id']) && !empty($nextQuests)) + $nextQuests = !is_null($currentQuest) ? $this->Quests->getNextQuests($currentQuest['id']) : null; + while(!is_null($currentQuest) && !empty($nextQuests)) { // Get choosed Quest $currentQuest = null; @@ -302,6 +303,8 @@ if(!$this->Quests->hasCharacterSolvedQuest($currentQuest['id'], $characterId)) { return false; } + + $nextQuests = !is_null($currentQuest) ? $this->Quests->getNextQuests($currentQuest['id']) : null; } } From d35148c7794622b8c262150f9afd54898d8d3e45 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 13:46:43 +0200 Subject: [PATCH 307/340] set solved-status for all Questgroups and add CSS-class --- controllers/QuestgroupsController.inc | 4 +++- views/html/questgroups/questgroup.tpl | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index 1064be74..a43c7d2b 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -94,10 +94,12 @@ // Get additional data foreach($hierarchy['questgroups'] as $i => &$group) { + $group['solved'] = $this->Questgroups->hasCharacterSolvedQuestgroup($group['id'], $character['id']); + // Check permission of Questgroups if($i >= 1 && count(array_intersect(array('admin','moderator'), IntermediateController::$user['seminaryroles'])) == 0) { - if(!$this->Questgroups->hasCharacterSolvedQuestgroup($hierarchy['questgroups'][$i-1]['id'], $character['id'])) + if(!$hierarchy['questgroups'][$i-1]['solved']) { $hierarchy['questgroups'] = array_slice($hierarchy['questgroups'], 0, $i); break; diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index c373400c..4b6735d3 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -25,7 +25,13 @@
                    • - + + + + + + +

                      Fortschritt:

                      From 8edfea4c2136e34e3c7f975ed5c9949b69386b4e Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 13:47:09 +0200 Subject: [PATCH 308/340] do not show scrollbox for Questgroups if there is no text to display --- views/html/questgroups/questgroup.tpl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index 4b6735d3..a72f0242 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -12,11 +12,13 @@

                      + 0): ?>

                      + From 95bef91098936bcfe53077a7bb4a383c3a52d91d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 14:01:37 +0200 Subject: [PATCH 309/340] bonus quest design fix (finally) --- views/html/questgroups/questgroup.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/html/questgroups/questgroup.tpl b/views/html/questgroups/questgroup.tpl index a72f0242..644b26b2 100644 --- a/views/html/questgroups/questgroup.tpl +++ b/views/html/questgroups/questgroup.tpl @@ -59,7 +59,7 @@ 0) : ?>
                    • - +
                    • From 80bafda95838733923dcfdd7f51238024165f775 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 22 Apr 2014 14:04:16 +0200 Subject: [PATCH 310/340] bonus quest hover state --- www/css/desktop.css | 1 + 1 file changed, 1 insertion(+) diff --git a/www/css/desktop.css b/www/css/desktop.css index ae0eef8f..78d60373 100644 --- a/www/css/desktop.css +++ b/www/css/desktop.css @@ -111,6 +111,7 @@ aside{display:none} .qglist .qgtitle .solved:hover{color:#62c5cd} .qglist .qgtitle .solved .fa{color:#bcd75e} .qglist .qgtitle .bonus{background:#f5821f} +.qglist .qgtitle .bonus:hover{background:#f68e34} #qtextbox{font-size:.875em;border-radius:5px;background:#fff;border:15px solid #fff;height:200px;overflow:hidden} .qtext{padding-right:15px} From 9a6b38f05a99718010a954071ad4b9c103aa179f Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 22 Apr 2014 19:20:58 +0200 Subject: [PATCH 311/340] allow editing and deleting of seminaries only to Seminary-admins --- controllers/SeminariesController.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index 06f95652..f848fa4d 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -44,8 +44,8 @@ */ public $seminaryPermissions = array( 'seminary' => array('admin', 'moderator', 'user', 'guest'), - 'edit' => array('admin', 'moderator'), - 'delete' => array('admin', 'moderator') + 'edit' => array('admin'), + 'delete' => array('admin') ); From 83200337991a5a502635d672ccfbb0d206e16f81 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 11:47:05 +0200 Subject: [PATCH 312/340] calculate XP-sums of Questgroups much more efficiently --- controllers/QuestgroupsController.inc | 6 +- controllers/SeminariesController.inc | 6 +- models/QuestgroupsModel.inc | 106 ++++++++++++++++---------- 3 files changed, 73 insertions(+), 45 deletions(-) diff --git a/controllers/QuestgroupsController.inc b/controllers/QuestgroupsController.inc index a43c7d2b..3cd33c37 100644 --- a/controllers/QuestgroupsController.inc +++ b/controllers/QuestgroupsController.inc @@ -106,8 +106,10 @@ } } - // Get Character XPs - $group['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($group['id'], $character['id']); + // Get cumulated data + $data = $this->Questgroups->getCumulatedDataForQuestgroup($group['id'], $character['id']); + $group['xps'] = $data['xps']; + $group['character_xps'] = $data['character_xps']; } } } diff --git a/controllers/SeminariesController.inc b/controllers/SeminariesController.inc index f848fa4d..45bfd155 100644 --- a/controllers/SeminariesController.inc +++ b/controllers/SeminariesController.inc @@ -132,8 +132,10 @@ $questgroup['text'] = $text; } - // Get Character XPs - $hierarchy['questgroups'][$i]['character_xps'] = $this->Questgroups->getAchievedXPsForQuestgroup($questgroup['id'], $character['id']); + // Get cumulated data + $data = $this->Questgroups->getCumulatedDataForQuestgroup($questgroup['id'], $character['id']); + $questgroup['xps'] = $data['xps']; + $questgroup['character_xps'] = $data['character_xps']; // Get Media $questgroup['picture'] = null; diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index f2e18e30..eaf11751 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -76,13 +76,6 @@ ); } - // Add additional data - foreach($questgroups as &$questgroup) - { - // Total XPs - $questgroup['xps'] = $this->getAchievableXPsForQuestgroup($questgroup['id']); - } - // Return Questgroups return $questgroups; @@ -357,37 +350,46 @@ */ public function getRelatedQuestsgroupsOfQuest($questId) { - $questgroups = array(); - $questtexts = $this->Questtexts->getQuesttextsOfQuest($questId); - foreach($questtexts as &$questtext) { - $questgroups = array_merge($questgroups, $this->getRelatedQuestsgroupsOfQuesttext($questtext['id'])); - } + return $this->db->query( + 'SELECT questgroups_questtexts.questgroup_id AS id '. + 'FROM quests '. + 'INNER JOIN questtexts ON questtexts.quest_id = quests.id '. + 'INNER JOIN questgroups_questtexts ON questgroups_questtexts.questtext_id = questtexts.id '. + 'WHERE quests.id = ?', + 'i', + $questId + ); - - return $questgroups; } /** - * Summarize XPs of all Quests for a Questgroup and its - * sub-Questgroups. + * Calculate cumulated data for a Questgroup, its + * sub-Questgroups and all its Quests. * - * @param int $questgroupId ID of Questgroup - * @return int Sum of XPs + * @param int $questgroupId ID of Questgroup + * @param int $characterId ID of Character + * @param array $calculatedQuests IDs of already calculated Quests + * @return array Cumulated data for Questgroup */ - public function getAchievableXPsForQuestgroup($questgroupId) + public function getCumulatedDataForQuestgroup($questgroupId, $characterId, &$calculatedQuests=array()) { - // Sum of XPs - $xps = 0; + // Cumulated data + $data = array( + 'xps' => 0, + 'character_xps' => 0 + ); // Current Questgroup $questgroup = $this->getQuestgroupById($questgroupId); - $questgroup['hierarchy'] = $this->Questgroupshierarchy->getHierarchyForQuestgroup($questgroup['id']); // Quests of current Questgroup $quest = $this->Quests->getFirstQuestOfQuestgroup($questgroup['id']); - if(!is_null($quest)) { - $xps += $this->getAchievableXPsForQuest($quest); + if(!is_null($quest)) + { + $questData = $this->getCumulatedDataForQuest($quest, $characterId, $calculatedQuests); + $data['xps'] += $questData['xps']; + $data['character_xps'] += $questData['character_xps']; } // XPs of child Questgroups @@ -398,47 +400,69 @@ foreach($childQuestgroupshierarchy as &$hierarchy) { $questgroups = $this->getQuestgroupsForHierarchy($hierarchy['id'], $questgroup['id']); - foreach($questgroups as &$questgroup) { - $xps += $this->getAchievableXPsForQuestgroup($questgroup['id']); + foreach($questgroups as &$questgroup) + { + $childData = $this->getCumulatedDataForQuestgroup($questgroup['id'], $characterId, $calculatedQuests); + $data['xps'] += $childData['xps']; + $data['character_xps'] += $childData['character_xps']; } } } - // Return summarized XPs - return $xps; + // Return cumulated data + return $data; } /** - * Summarize XPs of the given Quest, its following Quests and - * its related Questgroups. + * Calculate cumulated data of the given Quest, its following + * Quests and its related Questgroups. * - * @param array $quest Quest to summarize XPs for - * @return int Sum of XPs + * @param array $quest Quest data + * @param int $characterId ID of Character + * @param array $calculatedQuests IDs of already calculated Quests + * @return array Cumulated data for Quest */ - public function getAchievableXPsForQuest($quest) + public function getCumulatedDataForQuest($quest, $characterId, &$calculatedQuests=array()) { - // XPs for the given Quest - $xps = $quest['xps']; + // Cumulated data + $data = array( + 'xps' => $quest['xps'], + 'character_xps' => ($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) ? $quest['xps'] : 0 + ); // Related Questgroups $relatedQuestgroups = $this->getRelatedQuestsgroupsOfQuest($quest['id']); - foreach($relatedQuestgroups as &$relatedQuestgroup) { - $xps += $this->getAchievableXPsForQuestgroup($relatedQuestgroup['id']); + foreach($relatedQuestgroups as &$relatedQuestgroup) + { + $relatedData = $this->getCumulatedDataForQuestgroup($relatedQuestgroup['id'], $characterId, $calculatedQuests); + $data['xps'] += $relatedData['xps']; + $data['character_xps'] += $relatedData['character_xps']; } // Next Quests $nextQuests = $this->Quests->getNextQuests($quest['id']); - $nextXPs = array(0); + $allNextData = array( + 'xps' => array(0), + 'character_xps' => array(0), + ); foreach($nextQuests as &$nextQuest) { - $nextXPs[] = $this->getAchievableXPsForQuest($nextQuest); + if(!in_array($nextQuest['id'], $calculatedQuests)) + { + $nextData = $this->getCumulatedDataForQuest($nextQuest, $characterId, $calculatedQuests); + $allNextData['xps'][] = $nextData['xps']; + $allNextData['character_xps'][] = $nextData['character_xps']; + $calculatedQuests[] = $nextQuest['id']; + } } - $xps += max($nextXPs); + $data['xps'] += max($allNextData['xps']); + $data['character_xps'] += max($allNextData['character_xps']); - return $xps; + // Return cumulated data + return $data; } From 8209c71dc9520a631284a77ee840048391314d1d Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 12:28:55 +0200 Subject: [PATCH 313/340] remove Questtype ?Dummy? --- questtypes/dummy/DummyQuesttypeAgent.inc | 24 ----- questtypes/dummy/DummyQuesttypeController.inc | 93 ------------------- questtypes/dummy/DummyQuesttypeModel.inc | 25 ----- questtypes/dummy/html/quest.tpl | 4 - questtypes/dummy/html/submission.tpl | 0 5 files changed, 146 deletions(-) delete mode 100644 questtypes/dummy/DummyQuesttypeAgent.inc delete mode 100644 questtypes/dummy/DummyQuesttypeController.inc delete mode 100644 questtypes/dummy/DummyQuesttypeModel.inc delete mode 100644 questtypes/dummy/html/quest.tpl delete mode 100644 questtypes/dummy/html/submission.tpl diff --git a/questtypes/dummy/DummyQuesttypeAgent.inc b/questtypes/dummy/DummyQuesttypeAgent.inc deleted file mode 100644 index f0de5cb1..00000000 --- a/questtypes/dummy/DummyQuesttypeAgent.inc +++ /dev/null @@ -1,24 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 deleted file mode 100644 index a478cda5..00000000 --- a/questtypes/dummy/DummyQuesttypeController.inc +++ /dev/null @@ -1,93 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 - * @param Exception $exception Character submission exception - */ - public function quest($seminary, $questgroup, $quest, $character, $exception) - { - // 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 deleted file mode 100644 index c4aadf3e..00000000 --- a/questtypes/dummy/DummyQuesttypeModel.inc +++ /dev/null @@ -1,25 +0,0 @@ - - * @copyright 2014 Heinrich-Heine-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 deleted file mode 100644 index f482a07b..00000000 --- a/questtypes/dummy/html/quest.tpl +++ /dev/null @@ -1,4 +0,0 @@ -
                      - - -
                      diff --git a/questtypes/dummy/html/submission.tpl b/questtypes/dummy/html/submission.tpl deleted file mode 100644 index e69de29b..00000000 From 5680d8d68a8c4bfcd82381f609a6fde5d672c28e Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 12:44:07 +0200 Subject: [PATCH 314/340] correct UploadsAgent for Seminary uploads --- configs/AppConfig.inc | 15 +++-- controllers/UploadsController.inc | 43 +++++------- models/UploadsModel.inc | 65 +++++++++---------- seminaryuploads/empty | 0 .../uploads/{index.tpl => seminary.tpl} | 0 5 files changed, 53 insertions(+), 70 deletions(-) create mode 100644 seminaryuploads/empty rename views/binary/uploads/{index.tpl => seminary.tpl} (100%) diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index f7ed6931..38fef10e 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -60,12 +60,13 @@ * @var array */ public static $dirs = array( - 'locale' => 'locale', - 'media' => 'media', - 'seminarymedia' => 'seminarymedia', - 'questtypes' => 'questtypes', - 'temporary' => 'tmp', - 'uploads' => 'uploads' + 'locale' => 'locale', + 'media' => 'media', + 'seminarymedia' => 'seminarymedia', + 'questtypes' => 'questtypes', + 'temporary' => 'tmp', + 'uploads' => 'uploads', + 'seminaryuploads' => 'seminaryuploads' ); @@ -159,7 +160,7 @@ array('charactergroupsquests/(?!(quest))', 'charactergroupsquests/quest/$1', true), array('media/(.*)', 'media/$1?layout=binary', false), array('uploads/(.*)', 'uploads/$1?layout=binary', false), - array('uploads/(?!(index))', 'uploads/index/$1', true) + array('uploads/(?!(index|seminary))', 'uploads/index/$1', true) ); diff --git a/controllers/UploadsController.inc b/controllers/UploadsController.inc index 428f980f..f651a414 100644 --- a/controllers/UploadsController.inc +++ b/controllers/UploadsController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('uploads', 'users', 'userroles'); + public $models = array('uploads', 'users', 'userroles', 'seminaries'); /** * User permissions * @@ -56,51 +56,40 @@ /** - * Action: index. + * Action: seminary. * - * Display an upload. + * Display a Seminary upload. * * @throws AccessDeniedException * @throws IdNotFoundException + * @param string $seminaryUrl URL-title of Seminary * @param string $uploadUrl URL-name of the upload */ - public function index($uploadUrl) + public function seminary($seminaryUrl, $uploadUrl) { + // Get Seminary + $seminary = $this->Seminaries->getSeminaryByUrl($seminaryUrl); + // Get Upload - $upload = $this->Uploads->getUploadByUrl($uploadUrl); + $upload = $this->Uploads->getSeminaryuploadByUrl($seminary['id'], $uploadUrl); // Check permissions - $user = $this->Users->getUserById($this->Auth->getUserId()); - $user['roles'] = array(); - foreach($this->Userroles->getUserrolesForUserById($user['id']) as $role) { - $user['roles'][] = $role['name']; - } if(!$upload['public']) { + $user = $this->Users->getUserById($this->Auth->getUserId()); + $user['roles'] = array_map(function($r) { return $r['name']; }, $this->Userroles->getUserrolesForUserById($user['id'])); + // System roles if(count(array_intersect(array('admin', 'moderator'), $user['roles'])) == 0) { // Owner of file if($upload['created_user_id'] != $user['id']) { - if(!is_null($upload['seminary_id'])) { + // Seminary roles + $userSeminaryRoles = array_map(function($r) { return $r['name']; }, $this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id'])); + if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { throw new \nre\exceptions\AccessDeniedException(); } - else - { - // Seminary - $seminary = $this->Seminaries->getSeminaryById($upload['seminary_id']); - - // Seminary roles - $userSeminaryRoles = array(); - foreach($this->Userseminaryroles->getUserseminaryrolesForUserById($user['id'], $seminary['id']) as $role) { - $userSeminaryRoles[] = $role['name']; - } - - if(count(array_intersect(array('admin', 'moderator'), $userSeminaryRoles)) == 0) { - throw new \nre\exceptions\AccessDeniedException(); - } - } } } } @@ -109,7 +98,7 @@ $this->response->addHeader("Content-type: ".$upload['mimetype'].""); // Set filename - $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$upload['id']; + $upload['filename'] = ROOT.DS.\nre\configs\AppConfig::$dirs['seminaryuploads'].DS.$upload['url']; if(!file_exists($upload['filename'])) { throw new \nre\exceptions\IdNotFoundException($uploadUrl); } diff --git a/models/UploadsModel.inc b/models/UploadsModel.inc index 078b1ed4..fc4ec2fd 100644 --- a/models/UploadsModel.inc +++ b/models/UploadsModel.inc @@ -38,45 +38,36 @@ * Upload a file and create a database record. * * @param int $userId ID of user that uploads the file - * @param string $filename Name of file to upload + * @param int $seminaryId ID of Seminary + * @param string $name Name of file to upload + * @param string $filename Filename of file to upload * @param string $tmpFilename Name of temporary uploaded file * @param string $mimetype Mimetype of file to upload - * @param int $seminaryId Optional ID of Seminary if the upload is in the context of one * @return mixed ID of database record or false */ - public function uploadFile($userId, $filename, $tmpFilename, $mimetype, $seminaryId=null) + public function uploadSeminaryFile($userId, $seminaryId, $name, $filename, $tmpFilename, $mimetype) { $uploadId = false; $this->db->setAutocommit(false); try { // Create database record - if(is_null($seminaryId)) - { - $this->db->query( - 'INSERT INTO uploads '. - '(created_user_id, name, url, mimetype) '. - 'VALUES '. - '(?, ? ,? ,?)', - 'isss', - $userId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype - ); - } - else - { - $this->db->query( - 'INSERT INTO uploads '. - '(created_user_id, seminary_id, name, url, mimetype) '. - 'VALUES '. - '(?, ?, ? ,? ,?)', - 'iisss', - $userId, $seminaryId, $filename, \nre\core\Linker::createLinkParam($filename), $mimetype - ); - } + $this->db->query( + 'INSERT INTO seminaryuploads '. + '(created_user_id, seminary_id, name, url, mimetype) '. + 'VALUES '. + '(?, ? ,? ,?, ?)', + 'iisss', + $userId, + $seminaryId, + $name, + \nre\core\Linker::createLinkParam($filename), + $mimetype + ); $uploadId = $this->db->getInsertId(); // Create filename - $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['uploads'].DS.$uploadId; + $filename = ROOT.DS.\nre\configs\AppConfig::$dirs['seminaryuploads'].DS.$filename; if(!move_uploaded_file($tmpFilename, $filename)) { $this->db->rollback(); @@ -101,17 +92,17 @@ * @param int $uploadId ID of the uploaded file * @return array Upload data */ - public function getUploadById($uploadId) + public function getSeminaryuploadById($seminaryuploadId) { $data = $this->db->query( 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. - 'FROM uploads '. + 'FROM seminaryuploads '. 'WHERE id = ?', 'i', - $uploadId + $seminaryuploadId ); if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException($uploadId); + throw new \nre\exceptions\IdNotFoundException($seminaryuploadId); } @@ -123,20 +114,22 @@ * Get an upload by its URL. * * @throws IdNotFoundException + * @param int $seminaryId ID of Seminary * @param int $uploadId ID of the uploaded file * @return array Upload data */ - public function getUploadByUrl($uploadUrl) + public function getSeminaryuploadByUrl($seminaryId, $seminaryuploadUrl) { $data = $this->db->query( 'SELECT id, created, created_user_id, seminary_id, name, url, mimetype, public '. - 'FROM uploads '. - 'WHERE url = ?', - 's', - $uploadUrl + 'FROM seminaryuploads '. + 'WHERE seminary_id = ? AND url = ?', + 'is', + $seminaryId, + $seminaryuploadUrl ); if(empty($data)) { - throw new \nre\exceptions\IdNotFoundException($uploadUrl); + throw new \nre\exceptions\IdNotFoundException($seminaryuploadUrl); } diff --git a/seminaryuploads/empty b/seminaryuploads/empty new file mode 100644 index 00000000..e69de29b diff --git a/views/binary/uploads/index.tpl b/views/binary/uploads/seminary.tpl similarity index 100% rename from views/binary/uploads/index.tpl rename to views/binary/uploads/seminary.tpl From 69ab058d3f76e4e3ad092b8bb05511fb100e3cd8 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 12:47:56 +0200 Subject: [PATCH 315/340] ignore Seminary uploads --- .hgignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.hgignore b/.hgignore index 43dff51a..60d03278 100644 --- a/.hgignore +++ b/.hgignore @@ -1,5 +1,6 @@ -syntax: regex +syntax: regexp ^media/* ^tmp/* ^uploads/* ^seminarymedia/* +^seminaryuploads/* From 23d9465c282bd4cf58e456a84fff0a168bbd8427 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 15:08:53 +0200 Subject: [PATCH 316/340] improve Questtype ?submit? and store additional data for Character answers --- app/QuesttypeController.inc | 12 +++ configs/AppConfig.inc | 4 +- controllers/QuestsController.inc | 5 +- models/QuestsModel.inc | 27 +++++++ .../BossfightQuesttypeController.inc | 14 ++++ .../ChoiceinputQuesttypeController.inc | 14 ++++ .../CrosswordQuesttypeController.inc | 14 ++++ .../DragndropQuesttypeController.inc | 14 ++++ .../MultiplechoiceQuesttypeController.inc | 14 ++++ .../submit/SubmitQuesttypeController.inc | 79 ++++++++++++++++--- questtypes/submit/SubmitQuesttypeModel.inc | 62 ++++++++++++--- questtypes/submit/html/quest.tpl | 33 +++++++- questtypes/submit/html/submission.tpl | 34 ++++++-- .../TextinputQuesttypeController.inc | 14 ++++ 14 files changed, 302 insertions(+), 38 deletions(-) diff --git a/app/QuesttypeController.inc b/app/QuesttypeController.inc index 25544ab2..9b13512e 100644 --- a/app/QuesttypeController.inc +++ b/app/QuesttypeController.inc @@ -41,6 +41,18 @@ public abstract function saveAnswersOfCharacter($seminary, $questgroup, $quest, $character, $answers); + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public abstract function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data); + + /** * Check if answers of a Character for a Quest match the correct ones. * diff --git a/configs/AppConfig.inc b/configs/AppConfig.inc index 38fef10e..28f4c00f 100644 --- a/configs/AppConfig.inc +++ b/configs/AppConfig.inc @@ -117,12 +117,12 @@ ), 'prename' => array( 'minlength' => 2, - 'maxlength' => 128, + 'maxlength' => 32, 'regex' => '/^\S*$/' ), 'surname' => array( 'minlength' => 2, - 'maxlength' => 128, + 'maxlength' => 32, 'regex' => '/^\S*$/' ), 'password' => array( diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 6da2906d..04fd1d31 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -625,7 +625,7 @@ $this->Quests->setQuestSubmitted($quest['id'], $character['id']); // Redirect - $this->redirect($this->linker->link('Prolog', 5, true)); + //$this->redirect($this->linker->link('Prolog', 5, true)); } } catch(\hhu\z\exceptions\SubmissionNotValidException $e) { @@ -703,6 +703,9 @@ $this->Quests->setQuestUnsolved($quest['id'], $character['id']); } + // Save additional data for Character answers + $questtypeAgent->controller->saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $this->request->getPostParam('characterdata')); + // Redirect $this->redirect($this->linker->link(array('submissions', $seminary['url'], $questgroup['url'], $quest['url']), 1)); } diff --git a/models/QuestsModel.inc b/models/QuestsModel.inc index ffffb536..d4175545 100644 --- a/models/QuestsModel.inc +++ b/models/QuestsModel.inc @@ -406,6 +406,33 @@ } + /** + * Get the last status of a Quest for a Character. + * + * @param int $questId ID of Quest + * @param int $characterId ID of Character to get status for + * @return int Last status + */ + public function getLastQuestStatus($questId, $characterId) + { + $data = $this->db->query( + 'SELECT id, created, status '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = ? '. + 'ORDER BY created DESC '. + 'LIMIT 1', + 'ii', + $questId, $characterId + ); + if(!empty($data)) { + return $data[0]; + } + + + return null; + } + + /** * Create a new Quest. * diff --git a/questtypes/bossfight/BossfightQuesttypeController.inc b/questtypes/bossfight/BossfightQuesttypeController.inc index 366aabc8..4f8001e7 100644 --- a/questtypes/bossfight/BossfightQuesttypeController.inc +++ b/questtypes/bossfight/BossfightQuesttypeController.inc @@ -53,6 +53,20 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + } + + /** * Check if answers of a Character for a Quest match the correct ones. * diff --git a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc index 1b7eff27..be75b852 100644 --- a/questtypes/choiceinput/ChoiceinputQuesttypeController.inc +++ b/questtypes/choiceinput/ChoiceinputQuesttypeController.inc @@ -48,6 +48,20 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + } + + /** * Check if answers of a Character for a Quest match the correct ones. * diff --git a/questtypes/crossword/CrosswordQuesttypeController.inc b/questtypes/crossword/CrosswordQuesttypeController.inc index ba3e5a0c..318271f9 100644 --- a/questtypes/crossword/CrosswordQuesttypeController.inc +++ b/questtypes/crossword/CrosswordQuesttypeController.inc @@ -81,6 +81,20 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + } + + /** * Check if answers of a Character for a Quest match the correct ones. * diff --git a/questtypes/dragndrop/DragndropQuesttypeController.inc b/questtypes/dragndrop/DragndropQuesttypeController.inc index 5fbf6328..9797f8fd 100644 --- a/questtypes/dragndrop/DragndropQuesttypeController.inc +++ b/questtypes/dragndrop/DragndropQuesttypeController.inc @@ -65,6 +65,20 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + } + + /** * Check if answers of a Character for a Quest match the correct ones. * diff --git a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc index bca8c33a..b025f758 100644 --- a/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc +++ b/questtypes/multiplechoice/MultiplechoiceQuesttypeController.inc @@ -55,6 +55,20 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + } + + /** * Check if answers of a Character for a Quest match the correct ones. * diff --git a/questtypes/submit/SubmitQuesttypeController.inc b/questtypes/submit/SubmitQuesttypeController.inc index 18e55d1b..e064c542 100644 --- a/questtypes/submit/SubmitQuesttypeController.inc +++ b/questtypes/submit/SubmitQuesttypeController.inc @@ -24,7 +24,7 @@ * * @var array */ - public $models = array('quests'); + public $models = array('quests', 'uploads', 'users'); @@ -41,11 +41,8 @@ */ 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('answers', $_FILES)) + if(array_key_exists('answers', $_FILES)) { $answer = $_FILES['answers']; @@ -78,8 +75,17 @@ ); } + // Create filename + $filename = sprintf( + '%s,%s,%s.%s', + $character['url'], + mb_substr($quest['url'], 0, 32), + date('Ymd-His'), + mb_substr(mb_substr($answer['name'], strrpos($answer['name'], '.')+1), 0, 4) + ); + // Save file - if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer)) { + if(!$this->Submit->setCharacterSubmission($seminary['id'], $quest['id'], $this->Auth->getUserId(), $character['id'], $answer, $filename)) { throw new \hhu\z\exceptions\SubmissionNotValidException( new \hhu\z\exceptions\FileUploadException(error_get_last()['message']) ); @@ -88,6 +94,21 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + $this->Submit->addCommentForCharacterAnswer($this->Auth->getUserId(), $data['submission_id'], $data['comment']); + } + + /** * Check if answers of a Character for a Quest match the correct ones. * @@ -119,19 +140,38 @@ */ public function quest($seminary, $questgroup, $quest, $character, $exception) { - // Answer (Submission) - $characterSubmission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + // Answers (Submissions) + $characterSubmissions = $this->Submit->getCharacterSubmissions($quest['id'], $character['id']); + foreach($characterSubmissions as &$submission) + { + $submission['upload'] = $this->Uploads->getSeminaryuploadById($submission['upload_id']); + $submission['comments'] = $this->Submit->getCharacterSubmissionComments($submission['id']); + foreach($submission['comments'] as &$comment) + { + try { + $comment['user'] = $this->Users->getUserById($comment['created_user_id']); + $comment['user']['character'] = $this->Characters->getCharacterForUserAndSeminary($comment['user']['id'], $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + } + } // Has Character already solved Quest? $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); + // Last Quest status for Character + $lastStatus = $this->Quests->getLastQuestStatus($quest['id'], $character['id']); + // Get allowed mimetypes $mimetypes = $this->Submit->getAllowedMimetypes($seminary['id']); // Pass data to view - $this->set('submission', $characterSubmission); + $this->set('seminary', $seminary); + $this->set('submissions', $characterSubmissions); $this->set('solved', $solved); + $this->set('lastStatus', $lastStatus); $this->set('mimetypes', $mimetypes); $this->set('exception', $exception); } @@ -149,15 +189,30 @@ */ public function submission($seminary, $questgroup, $quest, $character) { - // Get Character submission - $submission = $this->Submit->getCharacterSubmission($quest['id'], $character['id']); + // Get Character submissions + $submissions = $this->Submit->getCharacterSubmissions($quest['id'], $character['id']); + foreach($submissions as &$submission) + { + $submission['upload'] = $this->Uploads->getSeminaryuploadById($submission['upload_id']); + $submission['comments'] = $this->Submit->getCharacterSubmissionComments($submission['id']); + foreach($submission['comments'] as &$comment) + { + try { + $comment['user'] = $this->Users->getUserById($comment['created_user_id']); + $comment['user']['character'] = $this->Characters->getCharacterForUserAndSeminary($comment['user']['id'], $seminary['id']); + } + catch(\nre\exceptions\IdNotFoundException $e) { + } + } + } // Status $solved = $this->Quests->hasCharacterSolvedQuest($quest['id'], $character['id']); // Pass data to view - $this->set('submission', $submission); + $this->set('seminary', $seminary); + $this->set('submissions', $submissions); $this->set('solved', $solved); } diff --git a/questtypes/submit/SubmitQuesttypeModel.inc b/questtypes/submit/SubmitQuesttypeModel.inc index 2d0a17e3..5d821655 100644 --- a/questtypes/submit/SubmitQuesttypeModel.inc +++ b/questtypes/submit/SubmitQuesttypeModel.inc @@ -32,14 +32,15 @@ /** * Save Character’s submitted upload. * + * @param int $seminaryId ID of Seminary * @param int $questId ID of Quest * @param int $characterId ID of Character * @param array $file Submitted upload */ - public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file) + public function setCharacterSubmission($seminaryId, $questId, $userId, $characterId, $file, $filename) { // Save file on harddrive - $uploadId = $this->Uploads->uploadFile($userId, $file['name'], $file['tmp_name'], $file['type'], $seminaryId); + $uploadId = $this->Uploads->uploadSeminaryFile($userId, $seminaryId, $file['name'], $filename, $file['tmp_name'], $file['type']); if($uploadId === false) { return false; } @@ -60,27 +61,44 @@ /** - * Get upload submitted by Character. + * Add a comment for the answer of a Character. + * + * @param int $userId ID of user that comments + * @param int $submissionId ID of Character answer to comment + * @param string $comment Comment text + */ + public function addCommentForCharacterAnswer($userId, $submissionId, $comment) + { + $this->db->query( + 'INSERT INTO questtypes_submit_characters_comments '. + '(created_user_id, questtypes_submit_character_id, comment) '. + 'VALUES '. + '(?, ?, ?)', + 'iis', + $userId, + $submissionId, + $comment + ); + } + + + /** + * Get all uploads 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) + public function getCharacterSubmissions($questId, $characterId) { - $data = $this->db->query( - 'SELECT upload_id '. + return $this->db->query( + 'SELECT id, created, upload_id '. 'FROM questtypes_submit_characters '. - 'WHERE quest_id = ? AND character_id = ?', + 'WHERE quest_id = ? AND character_id = ? '. + 'ORDER BY created ASC', 'ii', $questId, $characterId ); - if(!empty($data)) { - return $this->Uploads->getUploadById($data[0]['upload_id']); - } - - - return null; } @@ -101,6 +119,24 @@ ); } + + /** + * Get all comments for a Character submission. + * + * @param int $characterSubmissionId ID of Character submission + * @return array Comments for this submission + */ + public function getCharacterSubmissionComments($characterSubmissionId) + { + return $this->db->query( + 'SELECT id, created, created_user_id, comment '. + 'FROM questtypes_submit_characters_comments '. + 'WHERE questtypes_submit_character_id = ?', + 'i', + $characterSubmissionId + ); + } + } ?> diff --git a/questtypes/submit/html/quest.tpl b/questtypes/submit/html/quest.tpl index 7132de5e..4581c40d 100644 --- a/questtypes/submit/html/quest.tpl +++ b/questtypes/submit/html/quest.tpl @@ -11,17 +11,42 @@

                      - + $submissions[count($submissions)-1]['created'])) : ?>

                      :

                        -
                      • ( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                      • +
                      • 0) : ?>( format(round($mimetype['size']/(1024*1024),2))?> MiB)
                      - - (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) + + + 0) : ?> +

                      +
                        + +
                      1. +
                        + format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>
                        + + + + 0) : ?> +
                          + +
                        1. + + format(new \DateTime($comment['created'])), $timeFormatter->format(new \DateTime($comment['created'])))?>:
                          + + +
                        2. + +
                        + +
                      2. + +
                      diff --git a/questtypes/submit/html/submission.tpl b/questtypes/submit/html/submission.tpl index b3d89275..f3c748ef 100644 --- a/questtypes/submit/html/submission.tpl +++ b/questtypes/submit/html/submission.tpl @@ -1,10 +1,32 @@ + 0) : ?> +
                        + +
                      1. +
                        + format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>
                        + 0) : ?> +
                          + +
                        1. + + format(new \DateTime($comment['created'])), $timeFormatter->format(new \DateTime($comment['created'])))?>:
                          + + +
                        2. + +
                        + +
                      2. + +
                      + +
                      - (format(new \DateTime($submission['created'])), $timeFormatter->format(new \DateTime($submission['created'])))?>) -

                      - - - - + + +
                      +
                      + diff --git a/questtypes/textinput/TextinputQuesttypeController.inc b/questtypes/textinput/TextinputQuesttypeController.inc index 5be23690..02db2d8b 100644 --- a/questtypes/textinput/TextinputQuesttypeController.inc +++ b/questtypes/textinput/TextinputQuesttypeController.inc @@ -47,6 +47,20 @@ } + /** + * Save additional data for 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 $data Additional (POST-) data + */ + public function saveDataForCharacterAnswers($seminary, $questgroup, $quest, $character, $data) + { + } + + /** * Check if answers of a Character for a Quest match the correct ones. * From 6bcaf8495e1e663581909aa734f24f9088378ea7 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 15:09:38 +0200 Subject: [PATCH 317/340] format overview of all Quests as list instead of table --- controllers/QuestsController.inc | 2 +- models/CharactersModel.inc | 20 +++++------ views/html/quests/index.tpl | 62 ++++++++++++++------------------ 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 04fd1d31..8a44aab3 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -340,7 +340,7 @@ $questmedia = $this->Media->getSeminaryMediaById($quest['questsmedia_id']); } - // Get submitted Character submissions + // Get submitted Character submissions waiting for approval $submittedSubmissionCharacters = $this->Characters->getCharactersSubmittedQuest($quest['id']); // Get unsolved Character submissions diff --git a/models/CharactersModel.inc b/models/CharactersModel.inc index b3eac8b7..e15198ca 100644 --- a/models/CharactersModel.inc +++ b/models/CharactersModel.inc @@ -379,17 +379,15 @@ '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 charactertype_url '. 'FROM v_characters AS characters '. 'LEFT JOIN charactertypes ON charactertypes.id = characters.charactertype_id '. - 'WHERE EXISTS ('. - 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. - ') AND NOT EXISTS ('. - 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. - ') AND NOT EXISTS ('. - 'SELECT character_id FROM quests_characters WHERE character_id = characters.id AND quest_id = ? AND status = ?'. - ')', - 'iiiiii', - $questId, QuestsModel::QUEST_STATUS_SUBMITTED, - $questId, QuestsModel::QUEST_STATUS_UNSOLVED, - $questId, QuestsModel::QUEST_STATUS_SOLVED + 'WHERE ('. + 'SELECT status '. + 'FROM quests_characters '. + 'WHERE quest_id = ? AND character_id = characters.id '. + 'ORDER BY created DESC '. + 'LIMIT 1'. + ') = ?', + 'ii', + $questId, QuestsModel::QUEST_STATUS_SUBMITTED ); } diff --git a/views/html/quests/index.tpl b/views/html/quests/index.tpl index 00d7e576..d603a056 100644 --- a/views/html/quests/index.tpl +++ b/views/html/quests/index.tpl @@ -7,43 +7,33 @@

                      - - - - - - - - - - - - - - - - - - - +
                      + Filter + +
                      -
                      - - - - XPs
                      - +
                      + + + + + From 4d237813906a8ef2cfc6606774daa63e0ae09109 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 15:10:45 +0200 Subject: [PATCH 318/340] remove small leftover from testing in QuestsController --- controllers/QuestsController.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/QuestsController.inc b/controllers/QuestsController.inc index 8a44aab3..f5a3284d 100644 --- a/controllers/QuestsController.inc +++ b/controllers/QuestsController.inc @@ -625,7 +625,7 @@ $this->Quests->setQuestSubmitted($quest['id'], $character['id']); // Redirect - //$this->redirect($this->linker->link('Prolog', 5, true)); + $this->redirect($this->linker->link('Prolog', 5, true)); } } catch(\hhu\z\exceptions\SubmissionNotValidException $e) { From 5ca9fde6b0f3cd696c0c27aed51b7b4e834d6c51 Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 16:10:43 +0200 Subject: [PATCH 319/340] update translations --- locale/de_DE/LC_MESSAGES/The Legend of Z.mo | Bin 9152 -> 9448 bytes locale/de_DE/LC_MESSAGES/The Legend of Z.po | 103 ++++++++++++-------- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.mo b/locale/de_DE/LC_MESSAGES/The Legend of Z.mo index bbbde6ffcb0f1f8bfdc90f658e3b5323f0e693e0..93c1e10e148cb066710f15f592aa82b1db15e19c 100644 GIT binary patch delta 3349 zcmYk;eN0t#9LMn=coClv!9YpTLk9Sef>IdfGpPB9iW-WASG~Yh;o7|yOt7wnXd>AI znYJ0}vdlJ@>snKDsnh0`&ZW-M=BnA+bd@Xr(&no7=ic9K{>Jybe&^hC&hPtsI(l;2 z&FP^ZQ(Zd@rH$xMY;YNK0dh-@FFdiSmRj3C;*bTR1XM7hEaS!&xkC9iHGdKz_ zqV99?uoCKyO0+-r#ym`>eN!B%7*mFH%B-;Zkaw6S?221a3AAGZcA!RVAF6>g~tAvv{gdU|bfJ!zN+aJ8B2O3cgyn%yo7xE5s+@7CDJ^v>v z;j|QE24g=|q9v#iEk*uJ1qXGc)?N>$F#a0aja*RXt*8cHM`gYf)ssD_sW^yg=u7J< z)SRA0&FK%QhHs-1y@z?&m7DUh2z7lGDzU~?#$T1qTu>r!A)PV1QHdNx_55?xgV*f& zEmY5wXjO@iLI+1<4whpshENIbN0x&*Xg!YF9p`j|dismK@H^_oE|$I$8Gf{`?DZbn{~1)& zqa4%=3Q;%CKn-Cz@@FbJXe65O5p1)5it4~6)S|nEN-%}5lSV8PH8lmOjul}SM!W<= z`e8O7WDQLjs=)v%p+?kPwW1zuN8W8ZP;29uy?z2U=ii_byM}u1KGIo}Fd*`+H;#2C zs-q19*#FABl?xi`y{H}>KxV^SM6KrEtZ8JageRcpbPB4c0o2rmP>HvpI6Ma!3YGlUZNSuaaa6R&gFo#hI zokdOE_oykki5jtgLR6GlQdacSnT{H=2{;s&SX)pTAHqC5f?C}-tvwib^(-H?_zF=Y zQ;K?D1!{yEkU!JR;V}$-M1|2cmvIE%v1ajhB{mn;gT<&R@nR=z#&Ng>^`ZG1b>Cgo zZt22TOCyttI?qJ)JQwxeanavHW~#j~4_*AQ#9E0OvX!XqQjdDk7SzhVF;B0Y+_{|c(XpD+%eAk@(*M27zTf11i{LN`qyR4SrG8c_9l!cDwN&_Pp0IE2br z;yHquh?MmlwH6i=1%yULWg^i+Y$a$mQl5=f=5pFdY$h~+u`-LwWP(|Vlp<>kEw;gg zc0;VJqtZt7A)X}aiFktD7%9yhHBuXiP!oqrqL3I!%ppb-T723OO9&M$y6Q;Hm}hVS z@i?Jf5i6R8wXqY_{13C|TJ7_Q8N@Q8hM+|I-%9N%Vm_f&td%*Fc(7 zi5ypP)JpF}EGO0w`9!SL#VWXgm_%$-`%|f`CZ-dyQbJ`VQAV(CjE_($jS^Xy+BG@$ ze7v=@RRoD4gm#BY1CdQ=7pT+{F7@A|Gs@B^k^fY_Og)$I5?amrT>6P};uWGRQA`8~ z6@9d}L~8t38($){Yo-yKh!=|7Gd`!r8+hOchqHBV zZ_ry;?Ns^vj(hD|zpuf)CVV5+7oS+*j4T|JU({ZnmKCj!%nxr&n;+MW%u3urkJnQd zzL?$;KXhg^9k<{7UvnC4PLcfTBgcg`{_w(#TvzW!K7WnJ@2&PYvrc~zsIRN$MNOG~ n@Cyn0gPx${uCJoM9&fdKxhI^Mbu|7z<8N70 delta 3040 zcmY+`3rv+|9LMp8<4HgS0TJ@XL5aiFgrlh-7ZuB-AiP8o;;pDG5vU-{%d4h&X)BAa zlDW#YTxo45mUE?;qc&S6X&dWeHqjMZu@zM27JYx`JZ$vLAD{Pq&w1bH|6JZPe_q(W zz_}3Xe$mi&kqKm(+n6?dEsQ_3`UqnZumLl1H;%+MjKxne62HM{yoCCGA9`^lUyj0Q zI1%Sz2Cl?-V;u9ez3?Vx^1+uFhgVTIM)C9!I1z_q8uF2uiayM@*XvLPG@%OHiKB5J zj>03><4A4hOhCt&Z#m$fxx$}t>_c_OP&Ce-)#q6$2W zdd@M_b2=~-zrgW$PS@$*^m3pZ`cWfvQLb(bN1ex^ZXA!}Fd11TQ;52+7`0mEI0h^2 zc^#_YX1oD+V+tNYeeXOvdTWC%h-Up&`86(RByOIfrHDaw=&`1t)-)5friG{i7NZKT#;I6~ z*|^_cKZ7dpg0%-#;GamBJe2uYB{4i*BOZ%-V1Yd^Ma@71s`8gG30u*NAEOGrgsh(V z#rikuEeNGr4bX!+PeVO#4yu3}$6jbctxYTHfhRFAm8g++;xPOk)A1tua0IVPpkUOL zm!djcYR{{YubTVpc{A#H4r)No9((XCy14KHGCSsFRE3A^=f_Zmoj{GW1J&VqRH5IY z-h#`h0tT#M?6md}o{49<{xvVXHt}0b2hVrRaSpT_ze0`Va_|GwkNh*kdB>745w$mp zP)k*cQ?LQ`IvzmnffLqVq%Y&4D($TqsF4?=mb?rj=-*UuFp~?bQ6t%hnzDn)Gt5zB zFs9RW%B{5uv4hDJBM15Zq$tZgDNaMDfkN+i<+_7s2QuUZbcRR9!|v% z9S*dcyHSONO$u(hDAdfPq8>C2HNrB~(o|sPccJd2w8i>MjtvFH7$5f7jqJcg?Ec_QjO9krKy)|sf8%16B=rKsnw zKrP7{WI&Fo=Ri}k2{qy!sNMM->W0Ip4nD#V+)Cz2M3t)Ta~e-YH}n*-Y*yDP$PQBWhY_?WJ8h zvF$Pd+0W<7z4%U!*l0?>!BJwzC zCVG|CV#$-_-cX&<{69sU4Do*!dC=9eA^I=(x>dC`{Asc; t@smP*N!i(XzFhzO_{AaqHxjnH{N>}kZvURdgplBEt)2##KYQ{~*MF5G2ZR6s diff --git a/locale/de_DE/LC_MESSAGES/The Legend of Z.po b/locale/de_DE/LC_MESSAGES/The Legend of Z.po index 8ada359f..e0e68d3e 100644 --- a/locale/de_DE/LC_MESSAGES/The Legend of Z.po +++ b/locale/de_DE/LC_MESSAGES/The Legend of Z.po @@ -1,8 +1,8 @@ msgid "" msgstr "" "Project-Id-Version: The Legend of Z\n" -"POT-Creation-Date: 2014-04-21 21:42+0100\n" -"PO-Revision-Date: 2014-04-22 00:29+0100\n" +"POT-Creation-Date: 2014-04-23 16:09+0100\n" +"PO-Revision-Date: 2014-04-23 16:10+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -73,23 +73,45 @@ msgstr "Fehler beim Dateiupload: %s" msgid "Allowed file types" msgstr "Erlaubte Dateiformate" +#: questtypes/submit/html/quest.tpl:20 +#, php-format +msgid "%s-files" +msgstr "%s-Dateien" + #: questtypes/submit/html/quest.tpl:20 msgid "max." msgstr "max." -#: questtypes/submit/html/quest.tpl:26 questtypes/submit/html/submission.tpl:2 +#: questtypes/submit/html/quest.tpl:28 +msgid "Past submissions" +msgstr "Vorherige Lösungen" + +#: questtypes/submit/html/quest.tpl:33 questtypes/submit/html/submission.tpl:6 #, php-format msgid "submitted at %s on %s h" msgstr "eingereicht am %s um %s Uhr" -#: questtypes/submit/html/submission.tpl:6 -#: questtypes/submit/html/submission.tpl:8 views/html/quests/quest.tpl:43 -#: views/html/quests/submissions.tpl:33 +#: questtypes/submit/html/quest.tpl:35 +msgid "This submission is waiting for approval" +msgstr "Diese Lösungen wartet auf freigabe" + +#: questtypes/submit/html/quest.tpl:42 +#: questtypes/submit/html/submission.tpl:12 +#, php-format +msgid "Comment from %s on %s at %s" +msgstr "Kommentar von %s am %s um %s Uhr" + +#: questtypes/submit/html/submission.tpl:27 +msgid "Comment" +msgstr "Kommentar" + +#: questtypes/submit/html/submission.tpl:30 views/html/quests/quest.tpl:43 +#: views/html/quests/submissions.tpl:29 msgid "solved" msgstr "gelöst" -#: questtypes/submit/html/submission.tpl:9 views/html/quests/quest.tpl:46 -#: views/html/quests/submissions.tpl:24 +#: questtypes/submit/html/submission.tpl:31 views/html/quests/quest.tpl:47 +#: views/html/quests/submissions.tpl:20 msgid "unsolved" msgstr "ungelöst" @@ -147,17 +169,17 @@ msgstr "Platz" msgid "You achieved %d of %d Achievements so far" msgstr "Du hast bislang %d von insgesamt %d Errungenschaften erreicht" -#: views/html/achievements/index.tpl:61 views/html/characters/character.tpl:39 +#: views/html/achievements/index.tpl:59 views/html/characters/character.tpl:39 #: views/html/seminarybar/index.tpl:28 #, php-format msgid "achieved at: %s" msgstr "erhalten am: %s" -#: views/html/achievements/index.tpl:72 +#: views/html/achievements/index.tpl:68 msgid "Secret Achievement" msgstr "Geheime Errungenschaft" -#: views/html/achievements/index.tpl:77 +#: views/html/achievements/index.tpl:72 msgid "Continue playing to unlock this secret Achievement" msgstr "Spiele weiter, um diesen geheimen Erfolg freizuschalten" @@ -183,7 +205,7 @@ msgid "Characters" msgstr "Charaktere" #: views/html/charactergroups/group.tpl:38 -#: views/html/questgroups/questgroup.tpl:43 views/html/quests/create.tpl:7 +#: views/html/questgroups/questgroup.tpl:53 views/html/quests/create.tpl:7 #: views/html/quests/index.tpl:7 msgid "Quests" msgstr "Quests" @@ -281,17 +303,15 @@ msgstr "Kursfelder" msgid "create" msgstr "erstellen" -#: views/html/introduction/index.tpl:1 -msgid "Introduction" -msgstr "Einführung" - -#: views/html/introduction/index.tpl:5 views/html/introduction/index.tpl:13 +#: views/html/error/index.tpl:5 views/html/error/index.tpl:14 +#: views/html/introduction/index.tpl:3 views/html/introduction/index.tpl:11 #: views/html/menu/index.tpl:6 views/html/users/login.tpl:3 -#: views/html/users/login.tpl:14 +#: views/html/users/login.tpl:17 msgid "Login" msgstr "Login" -#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 +#: views/html/error/index.tpl:8 views/html/error/index.tpl:9 +#: views/html/introduction/index.tpl:6 views/html/introduction/index.tpl:7 #: 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:9 views/html/users/login.tpl:10 @@ -299,7 +319,8 @@ msgstr "Login" msgid "Username" msgstr "Benutzername" -#: views/html/introduction/index.tpl:10 views/html/introduction/index.tpl:11 +#: views/html/error/index.tpl:10 views/html/error/index.tpl:11 +#: views/html/introduction/index.tpl:8 views/html/introduction/index.tpl:9 #: views/html/users/create.tpl:14 views/html/users/create.tpl:15 #: views/html/users/edit.tpl:14 views/html/users/edit.tpl:15 #: views/html/users/login.tpl:11 views/html/users/login.tpl:12 @@ -307,14 +328,18 @@ msgstr "Benutzername" msgid "Password" msgstr "Passwort" -#: views/html/introduction/index.tpl:14 +#: views/html/error/index.tpl:15 views/html/introduction/index.tpl:12 msgid "or" msgstr "oder" -#: views/html/introduction/index.tpl:14 +#: views/html/error/index.tpl:15 views/html/introduction/index.tpl:12 msgid "register yourself" msgstr "registriere dich" +#: views/html/introduction/index.tpl:1 +msgid "Introduction" +msgstr "Einführung" + #: views/html/library/index.tpl:9 views/html/library/topic.tpl:8 msgid "Questtopics" msgstr "Themen" @@ -370,7 +395,7 @@ msgstr "Titel" msgid "Name" msgstr "Name" -#: views/html/quests/create.tpl:14 views/html/quests/index.tpl:15 +#: views/html/quests/create.tpl:14 views/html/quests/index.tpl:12 msgid "Questgroup" msgstr "Questgruppe" @@ -386,23 +411,19 @@ msgstr "Einstiegstext" msgid "Wrong text" msgstr "Text für falsche Antwort" -#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:55 +#: views/html/quests/create.tpl:37 views/html/quests/quest.tpl:56 msgid "Task" msgstr "Aufgabe" -#: views/html/quests/index.tpl:21 -msgid "Questname" -msgstr "Questname" - -#: views/html/quests/index.tpl:24 +#: views/html/quests/index.tpl:19 msgid "Questtype" msgstr "Questtyp" -#: views/html/quests/index.tpl:47 +#: views/html/quests/index.tpl:27 msgid "Apply filters" msgstr "Filter anwenden" -#: views/html/quests/index.tpl:48 +#: views/html/quests/index.tpl:28 msgid "Reset filters" msgstr "Filter zurücksetzen" @@ -411,27 +432,27 @@ msgstr "Filter zurücksetzen" msgid "Quest completed. You have earned %d XPs." msgstr "Quest abgeschlossen. Du hast %d XPs erhalten." -#: views/html/quests/quest.tpl:60 +#: views/html/quests/quest.tpl:62 msgid "Task already successfully solved" msgstr "Du hast die Aufgabe bereits erfolgreich gelöst" -#: views/html/quests/quest.tpl:63 +#: views/html/quests/quest.tpl:65 msgid "Show answer" msgstr "Lösung anzeigen" -#: views/html/quests/quest.tpl:64 +#: views/html/quests/quest.tpl:66 msgid "Skip task" msgstr "Aufgabe überspringen" -#: views/html/quests/quest.tpl:69 +#: views/html/quests/quest.tpl:72 msgid "continue" msgstr "fortfahren" -#: views/html/quests/quest.tpl:76 +#: views/html/quests/quest.tpl:79 msgid "Continuation" msgstr "Fortsetzung" -#: views/html/quests/quest.tpl:82 views/html/quests/quest.tpl:95 +#: views/html/quests/quest.tpl:85 views/html/quests/quest.tpl:98 msgid "Quest" msgstr "Quest" @@ -440,7 +461,7 @@ msgstr "Quest" msgid "Submission of %s" msgstr "Lösung von %s" -#: views/html/quests/submissions.tpl:15 +#: views/html/quests/submissions.tpl:11 msgid "submitted" msgstr "eingereicht" @@ -651,6 +672,9 @@ msgstr "Registrieren" msgid "Roles" msgstr "Rollen" +#~ msgid "Questname" +#~ msgstr "Questname" + #, fuzzy #~ msgid "achieved at %s" #~ msgstr "erhalten am: %s" @@ -700,6 +724,3 @@ msgstr "Rollen" #~ 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" From 4bc2920d02cc6f25c110eabaee5cb1bfdb0b009c Mon Sep 17 00:00:00 2001 From: coderkun Date: Wed, 23 Apr 2014 16:50:57 +0200 Subject: [PATCH 320/340] use cumulated data also for total Seminary XPs --- models/QuestgroupsModel.inc | 8 ++++---- models/SeminariesModel.inc | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/models/QuestgroupsModel.inc b/models/QuestgroupsModel.inc index eaf11751..f3cd0949 100644 --- a/models/QuestgroupsModel.inc +++ b/models/QuestgroupsModel.inc @@ -372,7 +372,7 @@ * @param array $calculatedQuests IDs of already calculated Quests * @return array Cumulated data for Questgroup */ - public function getCumulatedDataForQuestgroup($questgroupId, $characterId, &$calculatedQuests=array()) + public function getCumulatedDataForQuestgroup($questgroupId, $characterId=null, &$calculatedQuests=array()) { // Cumulated data $data = array( @@ -424,12 +424,12 @@ * @param array $calculatedQuests IDs of already calculated Quests * @return array Cumulated data for Quest */ - public function getCumulatedDataForQuest($quest, $characterId, &$calculatedQuests=array()) + public function getCumulatedDataForQuest($quest, $characterId=null, &$calculatedQuests=array()) { // Cumulated data $data = array( 'xps' => $quest['xps'], - 'character_xps' => ($this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) ? $quest['xps'] : 0 + 'character_xps' => (!is_null($characterId) && $this->Quests->hasCharacterSolvedQuest($quest['id'], $characterId)) ? $quest['xps'] : 0 ); // Related Questgroups @@ -466,7 +466,7 @@ } - /** + /** * Summarize XPs of all Quests for a Questgroup and its * sub-Questgroups solved by a Character. * diff --git a/models/SeminariesModel.inc b/models/SeminariesModel.inc index 554f3d10..207411df 100644 --- a/models/SeminariesModel.inc +++ b/models/SeminariesModel.inc @@ -122,8 +122,10 @@ { // Get Questgroups $questgroups = $this->Questgroups->getQuestgroupsForHierarchy($hierarchy['id']); - foreach($questgroups as &$questgroup) { - $xps += $this->Questgroups->getAchievableXPsForQuestgroup($questgroup['id']); + foreach($questgroups as &$questgroup) + { + $data = $this->Questgroups->getCumulatedDataForQuestgroup($questgroup['id']); + $xps += $data['xps']; } } From 048f8c7128d13a4a0f40452b72fef66a8c628629 Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 23 Apr 2014 17:29:40 +0200 Subject: [PATCH 321/340] responsive lightbox for quest images --- views/html/html.tpl | 7 +++++++ views/html/quests/quest.tpl | 4 ++-- www/css/desktop.css | 5 +++++ www/js/imagelightbox.min.js | 6 ++++++ 4 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 www/js/imagelightbox.min.js diff --git a/views/html/html.tpl b/views/html/html.tpl index a62f4e11..f60d4d71 100644 --- a/views/html/html.tpl +++ b/views/html/html.tpl @@ -16,6 +16,13 @@ $("#qtextbox").niceScroll({autohidemode:false,cursorcolor:"#c2beb9"}); } ); + + +