From 3fca3cab9b49a2ae4934ddc738839fb8c4534e62 Mon Sep 17 00:00:00 2001 From: coderkun Date: Fri, 17 Jan 2014 02:19:53 +0100 Subject: [PATCH 001/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmca7#4?qEfq{XYfq_AWfq@}}fq{XWfq|h1B*?(Pu!Dhtft7)QVJ8Cv0}lfO!+r(^ z1}+8$hBHv{>kJGG!VC-ycNrKMco`TNJ~A*c%x7d^_|L$=zy@M7LCjmm1Tkk569WSW z$Xq4{22lnEhJ#ED3<3-c4A+?$7#JBC7+4q>7?`1UgD8;QAR6R85X}TNm%+8DD8Goo zGq0p5KczIeBr`vcAviTRGcU0yGqso@v^ce>m?60+HL)Z$MIotDLA6*RKTkomn4u^& zJ+rtZwJ0@3AwQ2HpeR2pHMvCBGetKnwWv5VKhH`bBqLS9CpA4aFGV3gO(BXaz&}LS z8Dvssex9yNVo9o%f{}rtiLQa6uA!xZk&%_DnYMwUfdN;5ziv=!StiI7?CLBa>U67$ka6Vp?5LsAoSk@!{$DXH-;u3WyJzOD!d8tNHvIp^n< zq~?|AhEx`$S}ByIR+Q)$+`n;x8+Sd^R* zkXVwTYhb0Ir>74=Wtpkv#as+-sTnz`MGUT)d1;6DWE7R=r89V!78R#5IHl&5mQFehler +

    :

    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/213] 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/213] 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 zcmca7#4?qEfq_AUfq_AWfq`KU0|SFB0|P@0BS@5i;Q}KA13v=;!$n3025trhhKGy{ z44e!M3~w137+4t?7``(yFbFU(F#Kg?VBle3VBlkdh)XjuFt9N&FeoxHFmNz1Flaz& zLna0WE(Qh$D<%d80|o{LSEzUyR9`(*Un`XEVPaqqW?*2L4COCpVqoB9U|?9s#K170 zk%3_k69a<;$h|BK41x>{45cg(_3bPW`)9L2+_#7Y;{MGn5clne${%H6VBlk5U^v49 zap!HQ{1X-i1}g>zhF2^Qf2p!U#&V#pU@$DGb4>xtV#1MVYC^48a-sr8y}I zB^eNV6dpCqQ7wMdFjqlCwOCW3C^a!Br&1v)RUrlJmK1x2(Bjmhyu{p8Fr%0uIWaFe zHHRS?>~tuVqL5UnpjxbupQoT&%mC5JP?VaUSzMA@l$xTDpT`hTl%JKFT%zllq8pZ4 zRGgWgXQdF5k*eU6nx2}MqL81a5XBYXAEFBmlFa-(U6;g?R4WA|149#C14CUSBLzb< zD$Gh7={{73U~HL)luHM1l&kHIZ9BPSIlk%H1_DuZig zUfSV38AYXe>F8;+G_?qB9|JU{mN0mi78R#51n1}ED5M}MPI!d zW^#r?dMdf)2%{cE-3Xv%uCIK Rrr)B>)S?oF;?&F%1^~m@V7LGP delta 374 zcmey*^@6qjo)F7a1_lNOb_NCp83qQ15C#SYZUzR19*`gd1H%pm1_o9J28Nvs3=BLB z3=I1j7#O%17#Pk##ji6kFbFd+Fx+KeVBlq7VED+uz%ZYYf#E*`0|Og~%>*%T856{u zO-u|793XR<7#Ktu80r}gGBGd+FfcG&XJTMrWME)mVPIfjhFT7yK$e4OkOM(96VzY^ z*P^2QA_mXAlA`>S(&UoN{5*!>)ZEOx#G=g9VusM-)S_aBdb->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/213] 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/213] 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 zcmey*)xlGLPl#nI0|NttI0FNN327#J8XvoJ6SGB7Z_VS#w)9}CD~^$ZL` ztPqWotPr1Ru|j-g#tKrvz+l4)aiB9R0|Ore1A`wc#0RlZb!n^&3|0&b3^`E#a#lzP ztc9xE#R>_j!%+GxD+7Zl0|Uc#kbFG@1H)TZh|9jQGBAKbotFXP5Rhe%NPxH)qzT3c z(d-Ni3}Orn3_=VH3``&a1_lOZ28g3UTu_v)WMB|rU|{&m$iTqEz`(%A1QC~JVqjonU|>*WVqoB4 zU|`UI(uPb73|tHh3|34G3r@YA{K}THnTt+xF0NE&%kh$ zg@J*Ofq~%+3&f?jp$eX`Ffdp#FfhDgf%r_76(VoM3JC#gDDBG1z#z)Nz~B#+Pi2Kz zn8(V%01ZTN2!X

            - + 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/213] 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 zcmeC+>E)}xC&V(9fq{WRnt_2qhJk_M3j+g#Dgy(<8AgyO149B61A{OF14AMc0|P$; z14Aj4Ze?O%;AUW8n99V!z{|kEu#ky?fs=uOVLcNA11kdq!%ij!1_1^JhW$(o3_J`B z3>To{cbOO%*cccX9z(?I8D23lFmNz1Fnov749pA+Tnr2hoXiXi`V0&VBFqd71`G@g zF3b>vOPL|+tD*cBW{81ZPaaM>$w^<>)@%sC&V(9fq{WRoPmKshJk_M3IhX!JOcwm4X*~=0P={hbUmU3gzE}8uSE8zlJ*O z6O_-!3~>N2GXukXMg|5sW(Eck1_p+GEDQ`13=9mHSr`}u85kJeus}TYj|JqgdIkm| zR)|JPR*28ESRp<#V+AQ-V6b6@IMA7ufq{>Kfx(Xz;)7VIx-?b>1}g>zh8!q=IV&Ut z)BtTpY(gfp! zXm$n$1~CQ(1|bFp1}2aI0|Nsy1H{oFE+|S_7$A-ZMGYu4Kv4q<1yIz1qKy?4ryP?b znT%OVi&KjxuVX5l?9W^}`6RO|laaw>0TwkTBZJKrEb)x|PN{jNB~__K3OR?j6(?s* MKEhfxxqxjl0FSOVN&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 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmeC>d%#zJPl#nI0|NttECU0B3tp*4BQM13>TOf7#LDlbp@{d9cW;hL{FEK+LeiO=n%M5YQ7iIJ60-WP_L=!N$PA$H2gl%*MdLQP04@Py|&_4b{-Z#=v06z`)SK#=v03 zz`(EzD*lxX5>mfG8W|WEIM^XUEeNHh*%=r_85kH;*&!im%?@#}3sgRgoq+)qt1Ju* z4B`wBz4c5C3=EQxsAgb>(xCX30>u@S4~jQXT!OeDm$QKw43N+Pr3p}|fGmazGe|Qq zFbF}@6DaOM(F#f%FnJIy#=yY9&cL7m4r)+>0ttc=A&6#h&PXguOfE?+DxRFcw30O_ zKPR<#G6(ZNE+bt7O9ewCD-(;!0xWV!+|3p&hKyWbec2_cMX8fBSdX&><>%z2=1taP GyAJ@RszRXv delta 664 zcmaFB*UMLbPl#nI0|NttGy?;J3M z7%o7??=mqkurV+&JcfwZGrVGAVBlb2VE7KD8JHOuxEL51IGGt3^cff!M3@;E3>X*~ zT$mvSmoh`tS3~(N%n$>+p!7s$h=XT9`D>UV4%y7iz`(-5z;J|_fnh!)1H(yX1_lv^ zdIp9}Rt5$M1_p*YR)|lgvobIUGB7YKWrg^7J1fM%);L8^oa>q4Lb^3=E)H6NkhcI7*oq7#O4&APxZuGc!Ou1ByzJ zSt%evNl-8|Ktcf&m!S9o1r;bhLGmEX&H(YX7>L6F@d+r}Kv5+G6@yWr_y&a_D1Cs$ sCeLPCIoXT(FPEXNk*R`_ft9Jz + * @copyright 2014 Heinrich-Heine-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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmaFBw@aY@o)F7a1_lNOMFs{283qOh6-EXIeFg>w9VU<{1H%g@1_mw$28NeR3=F~y z3=ChG7#R2&7#KL1A+!WD0|Pe$1A_)L0|PGu1A_@O0|O@m1A`MY0|P4q1A`AU1A_nq z149rq0|O5O14A-Yynva3fsKKoo}mmXQOnH0z`?-4&;_L@LliK~fofRH3~|UxW{3l~ zFf%Y1FfcGYVP;@pVPIeoWno|tV_;yAVu4s}$O19X9LjfK0b9u6#=^iL#lXPe3+C4| zFl4Yme3lOuU|?uwfw;Jf1!C}Q76yj-j0_A5SRf8?U}InqVPIhJW@BKGU|?WKV1xL) zl8u2ukb!}roedH)Gua^Ku4IE)yq67P?h!Ufh+So4U|_9hU|@I%Rq&h*V!=B$1_nL` z28N$(5Fc=`Lktjv(h}?tAIh^cFc>m0FsQOKFjz4#Fod%+Ft9T)Fic{HIBXVF-DY-3 z(C=i2n0JgF5=H0P85ruN85kIDLIu9EGcbrUFfjawDiq~__(+ZeqQQWJfq{{MfkB7? zVgV=^LHZ#n1wn%19TeA~^aM&PplAV^3rZ)TWCcoVP}Nyr8kBHAaR^FipdeIWfLJIG zmStd&Wq<@Zl*=H_00~Mq21pP~!Xy|N7?>Ft7~~irAqW!&r7QNyy-eHL0!veiONu8u zGnWJ=7H2brWMmdAfW;Itixu(%)EtK7 z{JfIH%)HFJbhvtm3B{8yGFNaI8YvhVTNzkR4rEb)atsYNSF$iM!R)SLt5$FvJ|@;0JoBN0RR91 delta 718 zcmdlb@PM!Wo)F7a1_lNOSq26M83qOh7DfgJO$G*rH;f=r28Ibt3=CWh3=9*Q7#M^Z z7#QX-F);8mFfeR@(g&Cr7`Pc27%ng|Fz_-kFx+8cVBln6V0gvEz`)AD!0?5MfkA+Q zf#DYu0|O5O0|PfRL|lTIfq{*Ip`JmG86u&|%)r3Gz`$SxrLCa~U6>&b@M30Q&}U#^ z2xVqqFkoO{XobpegR0*H zdTfvov1fz$)Eg=v$ObV#f{lTJkAZ<9nT>&gqn?3*p$Mv=8mgg*je)_Cfq|ieje)_6 zfq`KcRQxL&B&2?WG%_$SaIiyyS`bQ0vokP=GB7ZxvO_}BnjPX`7pQy~I|BnKR#_Mr z7{nPMdh3}O7#JiWQO&>%r9ts61&S*u9~5t(xCC)QE@uNV7$BhoN)w<^0a*+cW{_rJ zU=V_)Cs5pjq7{@jVDcbZjDdlH9i$8t)Sv_f5(FhehRIKvwohKnTs+x`MP_m)OBsiO jrGlZ6m5IgV=PU|v&SrjACZ@>}>{XLDv#U-1%I*pPFoZe> 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 delta 1299 zcmdlba9z0mo)F7a1_lNOEd~Y#83qQ197YBPYX$~}Jxm}`28I>P3=9Gc3=Auo85qPF z7#MajGca&5Ffd$VW?&FzU|_h*%)r3Uz`*bkN;9%BFmN+4FbG0vSr!HcUIqpRO%?_Q zP6h@Ba~1{$Rt5$JM;5TT^$hMT3=BLB3=9!aIu)v*h=qZHje&uof(2q>JygDng@J*C zfq`KuxwTa1}O#xh9W4xmlYBsQ=t46tY9B9 ztcRL=fR%w^J|hFeQK)z-I|GAwJp%(n89T&>ee4VjA`A=+Guas!Bp4VN*0Do^?l?OG zgCGL~!&P=jNIhqV1o;6@VBiD!gahL9MySFrC_NEM&wv^YR+U?_9a>jPN{jNB~?(T!#E7?spa{3c`!XC42e0#lh3i|005txxR?L{ delta 867 zcmcaEyi1_|o)F7a1_lNOMFs{283qOh6-EXIeFg>w9VU<{1H%g@1_mw$28NeR3=F~y z3=ChG7#R2&7#KL1A+!WD0|Pe$1A_)L0|PGu1A_@O0|O@m1A`MY0|P4q1A`AU1A_nq z149rq0|O5O14A-Yynva3fsKKoo}mmXQOnH0z`?-4&;_L@LliK~fofRH3~|UxW{3l~ zFf%Y1FfcGYVP;@pVPIeoWno|tV_;yAVu4s}$O19X9LjfK0b9u6#=^iL#lXPe3+C4| zFl4Yme3lOuU|?uwfw;Jf1!C}Q76yj-j0_A5SRf8?U}InqVPIhJW@BKGU|?WKV1xL) zl8u2ukb!}roedH)Gua^Ku4IE)yq67P?h!Ufh+So4U|_9hU|@I%Rq&h*V!=B$1_nL` z28N$(5Fc=`Lktjv(h}?tAIh^cFc>m0FsQOKFjz4#Fod%+Ft9T)Fic{HIBXVF-DY-3 z(C=i2n0JgF5=H0P85ruN85kIDLIu9EGcbrUFfjawDiq~__(+ZeqQQWJfq{{MfkB7? zVgV=^LHZ#n1wn%19TeA~^aM&PplAV^3rZ)TWCcoVP}R9$8kBHAaR^FipdeIWfLJIG zmStd&Wq<@Zl*=H_00~Mq21pP~!Xy|N7?>Ft7~~irAqW!&r7KX#OuoqMGg*^GU&1r5 zq$oe7G`S=*KaasDKRpvn=9f Date: Mon, 17 Feb 2014 02:54:49 +0100 Subject: [PATCH 058/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmcaEJX56po)F7a1_lNO9R>yl83qQ18b$^NYX$~}J4_%^28I*N3=9Gc3=Aik85qPF z7#MCbGca&5Ffe>#W?&FzU|{&m%)r3Uz`!8L0-+UI7#O%27#Iwpv@Ht*11|#ugC`3E z11AFmLpTcq11kdqLm~^<+v z!+EH=51`^tq2|9~Wnh?J&&a^=6{>J5J0ysgu|s^kkDY-*1Qhh_3=9$s3=H?!Awl__ zoq<7+fq{XQ0}^uL9FU+_;ea^6gahJ04-SX}{5c@{5;!1Hlg9z^a1{pwLp>;n8=wZX zb3iPf&cVRI$H2g_hyxN58=(g7g3g?0u5vIiSTQg#uyZmnurn|) zIB`N8?!^hQFqso#UM45R;YFMb@!+_u=Y;s6gOh=Q85HN7kRYGK$-p4Zz`(E!%0I-( zz#z)Nz;Fg?&?8PrNW6y1|Aku2&IQrW&oy}$Qy8PpWI<+cM(xe9%o$Al#rZj9sVNMl sc@W0rBdiV_MrH~|2397flX=(_;GE4SY>SzN(o=H|Z!0cgD9wYh0Ely1HUIzs delta 756 zcmbO!a$UIoo)F7a1_lNOEd~Y#83qQ197YBPYX$~}Jxm}`28I>P3=9Gc3=Auo85qPF z7#MajGca&5Ffd$VW?&FzU|_h*%)r3Uz`*bkN;9%BFmN+4FbG0vSr!HcUIqpRO%?_Q zP6h@Ba~1{$Rt5$JM;5TT^$hMT3=BLB3=9!aIu)v*h=qZHje&uof(2q>JygDng@J*C zfq`KuxwTa1}O#xh9W4xmlYBsQ=t46tY9B9 ztcRL=fR%w^J|hFeQK)z-I|GAwJp%(n89T&>ee4VjA`A=+Guas!Bp4VN*0Do^?l?OG zgCGL~!&P=jNIhqV1o;6@VBiD!gahL9MySFrC_NEM&wv^ Date: Fri, 28 Feb 2014 20:53:48 +0100 Subject: [PATCH 066/213] 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/213] 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/213] 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/213] 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/213] 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 zcmbO!GGDa*o)F7a1_lNOeFg>w83qQ1DU1vZ&I}9;Jj@_b1_lWh1_l8J1_ntM1_p5k z1_mt_1_mw$1_mb<1_ogU1_oai1_pix28Ki^UC6?~z|FwG&Go`Ip5m4ShUfq`KOD+7ZV z0|UcKsD=Yj@ncX6PD917utI!tid}PrJjL-!5J#y!3Ob(KN|zXd`1R_2&lpz?2w@6lXoh06PIGB6l2Ffash zGB8*%Ffg=mGSoA$GcYjh=Y;s=I8@;SsKL)TAwGEz<^SP?_=uH@fdQ00MY$kRA<4zS zAkDzQpbX{Pb1^W8GB7ZBazPvz52Z7>AR$x;RoBc_&j5BnFH~SE7Xt$$B;kWC02vC( z6(Av15WxWPAt;zZTm=RO20c(zfH({c4BAi{l)^zwQ1pP(lp+HIgCaXlP|>IoXCy4$3vMGBDVj!uE$z*dsHwNFhBnqeLOK jSfM;Mvm~`>ay&=E=A9fyj2!8y#RaKFMJ1D8aq0sAsG4Cu delta 1038 zcmbO)I#Zyl83qQ18b$^NYX$~}J4_%^28I*N3=9Gc3=Aik85qPF z7#MCbGca&5Ffe>#W?&FzU|{&m%)r3Uz`!8L0-+UI7#O%27#Iwpv@Ht*11|#ugC`3E z11AFmLpTcq11kdqLm~^<+v z!+EH=51`^tq2|9~Wnh?J&&a^=6{>J5J0ysgu|s^kkDY-*1Qhh_3=9$s3=H?!Awl__ zoq<7+fq{XQ0}^uL9FU+_;ea^6gahJ04-SX}{5c@{5;!1Hlg9z^a1{pwLp>;n8=wZX zb3iPf&cVRI$H2g_hyxN58=(g7g3g?0u5vIiSTQg#uyZmnurn|) zIB`N8?!^hQFqso#UM45R;YFMb_29Uy=Y;s6gOh=Q85HN7kRYGK$-p4Zz`(E!%0I-( zz#z)Nz;Fg?&?8PrNW6y1|Aku2&IQrW&&9yN$N-5ZP@)ATX;9(=C0vj>450W2`BV`k z!@$6x$^Z#+P>KZ!ff9-)h`|5}I(aA?lv|V-7#QRj7#JiO7#K95;-F{(P!3^$I0}?_p~@K;d>9xQv_Ywofq_ARfq_Ab0g}j+86ZIf3PzY>F&G0%3o$@K zON{{%dg_x!S+-0*&l0*>i - + From 5706d6fe14b6e01828c2dc5e199dfa81d25324b8 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 4 Mar 2014 12:04:56 +0100 Subject: [PATCH 071/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmca7#4?qEfq_Avfq_AWfq`KPBLjmo0|NsOGf0$yL4t*WL4bjQL6U`mL7ahsL5qcf zfs28G!HI=|L70Jo!Iy=BfuDhaArVR!vM?}kGcYhTLg`)>1_oXR28Nj|3=EtM3=GRz z7#LU?7#KD}&D{>=pI~8N;9+23I0vO~L&aaPFfgz&FfhD>+WQqM4>E*U|)SQ1%`}sH- z82CWxffM3h4Ni!9Lr#eK=1|%WO1nbkeK;8y3>g?0f;br%tQZ&=S~wXP*cliY_H#n~ zaU81d0o2@QoDhG!hw}e$Lj1$Z#lQecnxb5gc#z~`V31~DU{Hqg?YS5jL>U+uJh>q5 zi-*z~T#)c7gsN-ig4o{+1PL%e(g!H-Xo2z5Ea!N&RIr)TCd_~fVO zmzFU2{bv5Y)L?3adCNmQ3?atj}Wq$At*m5wU{BO6hsC?{Fj+p%n+QB zUz(GmP?CZ0mBOQjIjY5v8s;h#r6%U&R4OE;Dx`oto??%z9%LCpt%ho`CN>QriN)Cr zAsLy)5H~Ai7Axd~11T|wAtbXTCzT--6tW;9FEJOwD`p7K&x5FENGxVZPRvVA&0$E+ z&nrpH%*)J6hnoOVQ_PSIjyouoqL5UnpjxbupQoT&%m8sYLr#8jc4`VkQEGZ-aY<@X zY6?`kII%31p*TOMEH#CpG!M!MD9X=DO)k;(OwkQXEh^5;&$Chp$w*c3Nlj19OHs&A zQ;6aU@DI@iCz;IrJYAQ>l2j`NBLhPdT?1oX0}};9Ln~8DZ39CC1Fisn-JsO6Opqy9 z)EQYB7(mqdBo>$Gh7={{73UY3@8a7E}sxVlb>#i<2}B^g!< zdRzfHrA3K3x^DSJxy4orc?Do@v8|D{0)%O+k*A<&t6->UjnE&QUs{x$3d)zosU=nl z5O={%4^B-iO3nyKEXmL{uu{;|(}$q4%+&H?E=X?9E=es)FDfl4NX>%?fbu(3Ko^|! zq1hXxFqOeCwKO$P!Mn7mSRu8jxFj_vCp8Z$=#-jQT2hr-gd_o0mveYqadJj#9s^tf zl1OoKMp0^JQfXc~16XlVYGP4RYGw(tawIXg)Qp@|l&lZR`Kb&ZnW;qz>8Tkd3aP~k zK&>ifK%@gGt3;s;TxcjH=7Ne2 vh*wkd7}8UV3sQ@UN}$C@QD$mU2~ + * @copyright 2014 Heinrich-Heine-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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmbO)I#F~33uCcIv$u|X{IWMg2M&&a?K1XcKnoq<7|fq{XUgMopIfq_AqgMmSWfq_AdgMmSU zfq}uAgMopUfq@}}gMmSifq@~3gMopEfq|ifgMopcfq|is1LBa091w>r<$yR~4ODzD zM?J)ViyRCL+zbp1_cp2-1*cliYPC*r3f*O2-6XKJHQ2skkh>yN= zGB7YRFfg!lL85}2i-AF!fq_93$~WXUpnu2OELr7wA_T>GnH#iI} y6pW0m49q6aXH$T2EH@u#`@tmPb9h^EXQtIUUoCg3iTDS7&sUh7*0dQudp&O za56A3++t;5U}9ikc*4rSz{bG9@SK%_L5hKa;T@FE$;QAS%)r1Pzy^_5fzsM+3=9Gc z3=BqW5c8ec80x_Wc(6e%@@HdUn9s<-5CK*AgPnmvoPmLXlY@bQi-Cbbk%NIjgn@xU zhl7DZf`Ng-k%NJOmw|yHii3eckb!|AgM)#Ahk=2iii3fHpMim)ode>K=^PM;tmc3? zU=vjQFh@Pafa@F#4BQM13{N>AA@C8Z;XBlze^3YTaWXLQF)%PlazY%e!3j}s$O*B) z97@|kX;-Mc4<`eIAp-+L5GMnJ6$1l93nv2uI|Bp5{(4S`PmV(sK7bnhj1%IM_fY;H zPKb|KxfmFj85kHuxgb#?$;H4R&A`B*4CUK%F))ZSFfe#>K^zzlr8Br7Ayf!e*USZR z0B0{$VCrN}=5j{)$&JkNj0}?(GUqeOZ5Cvi%qSg_Se(rel95@g5LlX8T%wR!tdL(& zl9`{Em@|1N>kSSA69q#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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmca7#4?qEfq_Avfq_AWfq`KPBLjmo0|NsOGf0$yL4t*WL4bjQL6U`mL7ahsL5qcf zfs28G!HI=|L70Jo!Iy=BfuDhaArVR!vM?}kGcYhTLg`)>1_oXR28Nj|3=EtM3=GRz z7#LU?7#KD}&D{>=pI~8N;9+23I0vO~L&aaPFfgz&FfhD>+WQqM4>E*L$t)HoOzBp4VNtT`az8^XcBAjrVL zki-EA#}W=myfkt^+%b^@;*O;p5c}6a#rHz>U*v$q+kFm5IKGCe{|Ghb2h@HJP6h@( z1_lOUPKbMzI3en_IU(j7LTL*qZ4Z@q<78kkWME+M;bdU2VqjpX3=9m)Ack{BVo_ppNotXTdr^LAK`{fmfI?ttYHQEFmI zszP3Bxk7PjZf0I$Q6;iiX>n>1gG*{oYDp?W9?FNx6(<*E7L;V>=P|gZWR@UQfq7sx zu0=)pMGUTw8YcQCX6EQRrlb_578f(P=PTsrF@T+_;FFq|l3K*znO9PjpHi9(HqIwM zJu{ENCqF&Ew1mMYzqkbAd8W`sARk0=1^9>Pf)h(-ex9yNVo9o%f{}rtiLQaMuAzm3k+GG5nYMwUfdN;5ziv=! zStiI7?CLBb>U67$ka6Vp?5LsAoSk@!{$DXH-;u3WyJ zzOD!d8tNHvIp^n{G!}qD}}rQFt^y&$XWrywAIK{P_$Jr)U-zE56&+wN=^mk$l}xz zD+P$V;HC$sCKe@U1SFPZ=o(ll=;`T0P+4Ybc`+9xA7_`O7Nr-J78IoBK?FcK9V(y; zPV~@>4N{oO;FnsOny27hT2!o%T2x$;nv;{72NiTm%_}XbN-aW?0ISP6ysbDnBQ=i! zt^i4-I60#zH8ZI+FP#CbI4L!;C@D3w1X($fm|JQ_PAW>K2j%%x29M0tB8BwSj1q;^ zVuf;${vrl{z~uwX z8V2{&^8CC!g!dD3iWw4 + * @copyright 2014 Heinrich-Heine-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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmca7dRL_Wo)F7a1_lNOT?PgQ83qQ17DfgJTLuP(H%uT=28IXB3=9Gc3=9vM85qPF z7#Kb=Gca&5FfeejFfa%+FffR+Ffi~lFfeFBX-gIc25trh22Us*%EG|F%fP^p$il$D z$-uyn&%(gK%D})-$pSXFo}nHpFoA`Efro*CVH%Xb7)q~XVPIfmU|?9!0-3o8SI0Yg0lg9$6dC#kGp7co?_ zGBB_(FfeqnGB8LmFfjD9LR`Fp6=LB!D1SRE#7BEsAr3qarO!dly9(vMgqr&SYAz!i zL_HfD0|Q4r0|Nsu8w109Mg|5kHi!eZu|tCTI6K70_t_a3L>L$tUa>{3~C&Zkh9=`M1?B{#6zJR5Q{Q7Am$c8#Tz*wQ8blSO>~hh0@v}+BqY!C^5MtwMfCeD8ICzm;oX%*@0!==GQDTjFWlT>?Q}YrE(Y> jC>WSn8JJDp$EE=1Y<|Wjz_gi#)01&>7+2ur4P1HvPu^bp delta 1169 zcmcaBa!<7Wo)F7a1_lNO0|o{L83qQ1IgAVp&LB23NR)v=gN1=XfPsNQlZAmnoPmMC ziiLrJi-Cb5h=qYcn1O*ImW6?VpMim)5K1?)Ffed4FfdGn(sNlD7g?0$~hSr ztU!r`lYv1J6x5uMDEP<8z`)MHz`)PNz@QEa0WOGx?71KoxpF~#=m+ITb3q)I!Uc(n z5-45A#lRrSz`)P}RW}bxFXyU<1l0zp!b4mT3(r9LSGX7$7$F&f0hA#?IYbu92jv(L z6O<^0KnzeKWB?^91_oUyUx$H#L5cz5Xpj)dbD&(L!oa|w1rh+ozcd3R7_}K7z5$6V zF)%QQffx*sI0j`QP@ICIOO=6vK>;cU3KdZHgL1*X1hF+3AaM<1gR&in24Ou0NXUV* zC@67*9jeIy@wFTS0|OHS1A{yRBte1V6r>4+6&V;9)F$6$-pS)$lwVq);FFq|l3KKR zDvK=RgetQuestgroupById($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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmcaB@=>(@o)F7a1_lNO0|o{L83qQ1IgAVp&LB23NR)v=gN1>Chk=1XlZAmnfPsO* zjD>+goPmMCorQsci-Cb5o`r!yn1O*Ii-m!KpMim)4odg2Ffed4Ffh!6(yLe)7s4jLVSFUm4ShUfq~&UD+7ZB0|Ubwr~#~O5C?FxK`a(ygIFlf2JxXP8^oddP}+&)T!mV6AIg8u#lXM_ z$q3*yAPb5HC?Aw#Kul2L1myq_TZI9VmUKZJ1_lNp1_lOc21saugrpcCQKSk=s2~AQ z{DZO`D68l&K;jJ~uFL?5V-OqUVo*UfCN2*V{%4j zYFTP-YFZ}?#pGBv6%fO6b1R!6 zqkwBsQE6U!YF=@2Mq*k?YTo3n?4P)tGLv#L^Gh;Pvo|l}uwZ0QFDfl4NX?skm9ql? Dzk6tj delta 1073 zcmew;dRL_Wo)F7a1_lNOT?PgQ83qQ17DfgJTLuP(H%uT=28IXB3=9Gc3=9vM85qPF z7#Kb=Gca&5FfeejFfa%+FffR+Ffi~lFfeFBX-gIc25trh22Us*%EG|F%fP^p$il$D z$-uyn&%(gK%D})-$pSXFo}nHpFoA`Efro*CVH%Xb7)q~XVPIfmU|?9!0-3o8SI0Yg0lg9$6dC#kGp7co?_ zGBB_(FfeqnGB8LmFfjD9LR`Fp6=LB!D1SRE#7BEsAr3qarO!dly9(vMgqr&SYAz!i zL_HfD0|Q4r0|Nsu8w109Mg|5kHi!eZu|tCTI6K70_t_a3L>L$tUa>{3~C&Zkh9=`M1?B{#6zJR5Q{Q7Am$c8#Tz*wQ8blSO>~hh0@v}+BqY!C^5MtwMfCeD8ICzm;oX%*_UP0W_DH%#>rA_%UMjU49q4U bWm5q&Hos*vWSp$e@o}>@r#a)~BCdJ>KHpr! 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/213] 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/213] 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/213] 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 zcmew;+9OtfPl#nI0|NttAp--03U~)m7+4q>7($@pkx)7wY)(A`Ln5r!va=_%P+D*e0&#bz&ln31_cHNhR;v~MA#S@_!t-%q}U)1(qw};#Dopv zU|TkbPo3Ey4)=x9A#9Kkiw5!QK|#m{v8ap<;?j1I1Oo#@57fZPYzz$Z85tPnK-JlB zK-BwkK%yd!1LA-(4h9Ah1_p*k4h9AZ1_p*{9FQPi&%wYT$iTp`mje=F=Q$uzb%%q2 zp&k@>Z#W<>1Vs!ej@daOAt1pC@rep2!~!iS-;@)g-kK9)kP}osh!YYbv7C^Q$bqUa zg{rTH@|&Ri4o-%8aGdr-C8lvg65a}Y=hlxT%O3s1A`I+ z1B1q7Rh9#jU$Z1IC+1~O_GGP_e4bT_Ju#;!H8G`f@&{IFzL5Nq#2kf!qWtut)Z*gF zTx=VSOmqzl6%387OpLV+3=Isp0{nG@Qp+-nGxPIwT@p)DtrUz53{7+m5b8F+W#eU< y%*UC<8jzD%QZ>1pvuQFDmmPajYEf!tN%3TRE@^JJ{GyWL~s z4BJ>37&sXi7>=+oFt9Q()H9re8hiyJ!0;4m;cF=W2MYrOI|Bp5KPaD#m4Shcfq{XC z72*J4R)_=TSQ!{N7#J9|p|lAr*a8MysCs88-wR3yfc4iiFodx(Fz7QdFeI`-cA3+U%36=Q8 z0f{?4PKb}BI3X4(K>6C75T6-xLJYKk%6o7!Fz_)jFa&c#LL>>QJ{zjO5K31->3U9v zdQc)@XyasHFl1n0=;35wuwr0f*vHAhAj!bM@RJkbV__~x8c^Y4U{GgZV9?=$I4pz< z;(#bFh!3;4AQqQH#T&RFQ858Z&*EZW5M^LsSOQhIi>n?IM2DdYu0k!k59L4SVqjo| zWCU;;kOf5pln=@=ASNhrf^q>L_k@Zm4Sf)>`*NRNU+H>Fff3!y#fOy_(A!Z38agGfkBCZfkAz;Da!#? z_k4x?yvbi!5+_Ho*03k%7Zs%@mrTCOsyvBJYO*HV1|wr#V*>>PQ!4`tZ39CC1Fisn z-JsO6%;L=aJYAQ>l2j`NBLhPdT?4o}%gx;EyiCF#nW;qz>8Tkd3aP~k<*At^sYR2; hIkP8sa5i!VWhQ5oWTvMoOy=aWo9xdeHMx{)IsjU^YtaAz 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 d0e103fb..6401b58d 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 05:08+0100\n" -"PO-Revision-Date: 2014-03-30 05:09+0100\n" +"POT-Creation-Date: 2014-04-01 13:43+0100\n" +"PO-Revision-Date: 2014-04-01 13:43+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de_DE\n" @@ -20,33 +20,33 @@ msgstr "" msgid "Error" msgstr "Fehler" -#: ../../../views/html/charactergroups/group.tpl:3 -#: ../../../views/html/charactergroups/groupsgroup.tpl:3 -#: ../../../views/html/charactergroups/index.tpl:3 -#: ../../../views/html/characters/character.tpl:50 +#: ../../../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:25 +#: ../../../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:63 +#: ../../../views/html/charactergroups/group.tpl:60 #: ../../../views/html/questgroups/questgroup.tpl:43 msgid "Quests" msgstr "Quests" -#: ../../../views/html/charactergroups/groupsgroup.tpl:13 +#: ../../../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/seminaries/seminary.tpl:8 msgid "Description" msgstr "Beschreibung" @@ -62,29 +62,41 @@ msgstr "Gewonnene Quest" msgid "Lost Quest" msgstr "Verlorene Quest" -#: ../../../views/html/characters/character.tpl:14 +#: ../../../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 -#: ../../../views/html/menu/index.tpl:2 msgid "Introduction" msgstr "Einführung" -#: ../../../views/html/menu/index.tpl:4 ../../../views/html/users/create.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/user.tpl:1 msgid "Users" msgstr "Benutzer" -#: ../../../views/html/menu/index.tpl:5 +#: ../../../views/html/menu/index.tpl:4 msgid "Usergroups" msgstr "Benutzergruppen" -#: ../../../views/html/menu/index.tpl:6 +#: ../../../views/html/menu/index.tpl:5 #: ../../../views/html/seminaries/create.tpl:1 #: ../../../views/html/seminaries/delete.tpl:1 #: ../../../views/html/seminaries/edit.tpl:1 @@ -92,47 +104,43 @@ msgstr "Benutzergruppen" msgid "Seminaries" msgstr "Kurse" -#: ../../../views/html/menu/index.tpl:8 ../../../views/html/users/login.tpl:2 +#: ../../../views/html/menu/index.tpl:7 ../../../views/html/users/login.tpl:2 #: ../../../views/html/users/login.tpl:11 msgid "Login" msgstr "Login" -#: ../../../views/html/menu/index.tpl:10 +#: ../../../views/html/menu/index.tpl:9 msgid "Logout" msgstr "Logout" -#: ../../../views/html/quests/quest.tpl:15 -#: ../../../views/html/quests/submissions.tpl:22 +#: ../../../views/html/quests/quest.tpl:40 +#: ../../../views/html/quests/submissions.tpl:24 msgid "solved" msgstr "gelöst" -#: ../../../views/html/quests/quest.tpl:17 -#: ../../../views/html/quests/submissions.tpl:13 +#: ../../../views/html/quests/quest.tpl:42 +#: ../../../views/html/quests/submissions.tpl:15 msgid "unsolved" msgstr "ungelöst" -#: ../../../views/html/quests/quest.tpl:52 +#: ../../../views/html/quests/quest.tpl:50 msgid "Task" msgstr "Aufgabe" -#: ../../../views/html/quests/quest.tpl:60 -msgid "Task successfully solved" -msgstr "Du hast die Aufgabe erfolgreich gelöst" +#: ../../../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:60 -msgid "Show correct answer" -msgstr "Richtige Lösung anzeigen" +#: ../../../views/html/quests/quest.tpl:57 +msgid "Show answer" +msgstr "Lösung anzeigen" -#: ../../../views/html/quests/quest.tpl:61 -msgid "Go on" -msgstr "Hier geht es weiter" - -#: ../../../views/html/quests/quest.tpl:67 -#: ../../../views/html/quests/quest.tpl:76 +#: ../../../views/html/quests/quest.tpl:71 +#: ../../../views/html/quests/quest.tpl:80 msgid "Quest" msgstr "Quest" -#: ../../../views/html/quests/submission.tpl:10 +#: ../../../views/html/quests/submission.tpl:13 #, php-format msgid "Submission of %s" msgstr "Lösungen von %s" @@ -154,7 +162,7 @@ msgid "create" msgstr "erstellen" #: ../../../views/html/seminaries/delete.tpl:2 -#: ../../../views/html/seminaries/seminary.tpl:40 +#: ../../../views/html/seminaries/seminary.tpl:39 msgid "Delete seminary" msgstr "Kurs löschen" @@ -174,7 +182,7 @@ msgid "cancel" msgstr "abbrechen" #: ../../../views/html/seminaries/edit.tpl:2 -#: ../../../views/html/seminaries/seminary.tpl:39 +#: ../../../views/html/seminaries/seminary.tpl:38 msgid "Edit seminary" msgstr "Kurs bearbeiten" @@ -188,15 +196,11 @@ msgid "Create new seminary" msgstr "Neuen Kurs erstellen" #: ../../../views/html/seminaries/index.tpl:10 -#: ../../../views/html/seminaries/seminary.tpl:43 +#: ../../../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:5 -msgid "Achievements" -msgstr "Errungenschaften" - #: ../../../views/html/seminarymenu/index.tpl:6 msgid "Library" msgstr "Bibliothek" @@ -251,6 +255,9 @@ msgstr "registriert am %s" msgid "Roles" msgstr "Rollen" +#~ msgid "Go on" +#~ msgstr "Hier geht es weiter" + #, fuzzy #~ msgid "Character groups" #~ msgstr "Charaktergruppen" From 8c0168cc0d60475fdaa0827bbc8a7b33455db545 Mon Sep 17 00:00:00 2001 From: coderkun Date: Tue, 1 Apr 2014 13:47:00 +0200 Subject: [PATCH 155/213] 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/213] 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/213] 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 zcmeAXJ1J3rPl#nI0|NttIRgWO3da11AFmgAXeM11keVJwq5P#NZfKh();&35F_G1_pKp28ISGzk`*5fsKKI zp^p_}@l>e#1*{AV91IK$YoYWusQUe^3=Dz{3=Bu1`mVDwFt9K%Fx-R6KL*qF3=A)! z2EJ!yV9;k^VEDz#z+k|@z@Wtj@j)~j#DVE-5CbaM7#I{77#M2Vz&>D@$Hu_G$H2g_ zgbm`b&1?_{?Ssl6XM_0oEL8t>sQ4W=1_riz1_p*lAO-^i!%H?uP=0{&S=b>KbF)J% zmSKljsK^enP@NqTB4+Fi43Z2C47ThLi^JI&7)~=XFeE_r>u^HMvE_tBogXK}p=q29 z4D})m3=D;w3=9$s3=D0YkT{yp2?@HjoRBEl&k2dsGn^0y-rt14@g^w014{Qp#iwyG zFc>m0FwEg%V6b9fU^vGG@i8YigqGrlq$O=`NE91$LmZmST@P`2CO5=orQ8r7w{k-q zGzls`n;Q}ZtDy#M;D*HM4k&$yn}I=;fq~%+RNo_RNC>`$%KzntgaA7a#C$#;h=Zi- zc^DX^85kIpc_0Sb^DrMnDi&j4U@(EQ zH5eEev_ZLo0TOgf43M+{Dg!{-5~K{23sgZ21_lOW28a(qp`yqD@gztJREB_PP!1Ad z0Q&*Ng(N@_OAT5Wf!LZ13=Hxh1_J|w6axc;5(5K+5Ca2)&Sp~9VgtNKX4>0OaKFwaB8KE1HpPG_cq8pr= zSd^R*kXVwTYiOmQr>74=g&@01DhpCKTXHxt^1Br!rl*1&Sq8EsWpfp$AtO(Ceo+ZS z_~C6uC88VAjnaLR?3W>QO=az!W(2OF6 I(ma?d0FYa&>;M1& delta 1170 zcmX>p(IZxWPl#nI0|NttAp--03U~)m7+4q>7($@pkx)7wY)(A`Ln5r!va=_%P+D*e0&#bz&ln31_cHNhR;v~MA#S@_!t-%q}U)1(qw};#Dopv zU|TkbPo3Ey4)=x9A#9Kkiw5!QK|#m{v8ap<;?j1I1Oo#@57fZPYzz$Z85tPnK-JlB zK-BwkK%yd!1LA-(4h9Ah1_p*k4h9AZ1_p*{9FQPi&%wYT$iTp`mje=F=Q$uzb%%q2 zp&k@>Z#W<>1Vs!ej@daOAt1pC@rep2!~!iS-;@)g-kK9)kP}osh!YYbv7C^Q$bqUa zg{rTH@|&Ri4o-%8aGdr-C8lvg65a}Y=hlxT%O3s1A`I+ z1B1rqwJfrXlkc$pp8SZ-g)wDv7`qp{p@N~Ym5K4> Date: Fri, 4 Apr 2014 00:57:05 +0200 Subject: [PATCH 158/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmX>pF;Tkyo)F7a1_lNOYX$}e83qQ1FN_Qf{tOHZXP7~v3=ABs3=BLB3=EvC3=BdH z3=Cqd3=9Gc3=EpA3=HB73=CGR3=CWh3=Bc63=F~y3=FZX3=I4X3=D-(x{;NEft!JW zVIq{C%gVsO%fP^}l9hphlYyb0VLK}W11kdq!$DSv1CB#2c*4rSAj!bM@D(b~&jxXb z4jV+hB^v_+I|BoQJ(Tax#=yYFz`)?k260dbRDC=f0|N&G149m!E`_SEV`E?tWTT+F!oa{V7ph1gFXWT!%j8^1_K5LhL>y*pGmPpLPU)nqTigI zfkA74GGc-Q1vsR>gGfBt%r(lgYx%4>BHRh5DQOnGcXu} zT*%G9V8y_|z{CRy30o-b#{)@h@jQ?;kj?|qH=PIK@VPt?hpps+gv3rBh=Yzn#n1CV zqWC_Pe#rv~@z3>8g`B*Q#3RHD2~u%hhz3nwh|i6AAqHFVLLB1G3kgzxD4h*e-_Fax zzzB*;21pceLfIe+l#P`bAm;0U1Q-|?>OpxL6t|$P2FlMG3=9ma3=H6C0i|+T28a*M zp&CGxHi%$=1U)FPgF?)d0g@&_QlO#(lulF_7#J)WAR(s40Lc{~d3g}Qz`&pk%KuO{ zm;x0fQVfu&0Oe(nASjQ^K?^7l-vSgpAO-^@2tiCxwgu6;3=9lr3=9lL3=9m43=9k= zAVE-}1ufjP7$Bhr5;kT4MWG=BgC+w5gB}AUzCqc8!7(`_Gqo%=H#M)Mn87h8C%-&3 zMIkLSCsm=OvLJQx9+rj4MMe2V3Mr*UnR)3DnbLxs{KOP11=V5(*GCN#eG@ZtbRAPt zic*V<8QefxQ!A2FQ&Wl+iZiQH6><|RGIL9FAu<_>#R}y``FZJJ;}sq?%uy|V)G&8* z4r?~!Z6@2Wr?8tS7#drdT1;Nft_WpYZa&YR$0+Jrl#^JRRFbOT zl30?OnU-Ian^=-M*@km>y<2KVPHK@tQfg){L=wAWL2gZDaLI(4ppaQyqEJ<;kY1F3 zcs_$mY7sbo(v2OJ!Rp!@*kv#>)f=4OXj zEW-}5P>~&Cp*lMxM9kP37$g}O7;M=g7KgJlFq~#&U`T-K*WrYiW6KGNIzLW`L(@1J z80tkB7#IpU85krO7#P|(A#pUH6B2Z5IU!N9pA!^;X2rBQy#ZV6pI$th`%VVG#QlSd7p&F~9;!RL~2bAuIicjNW zU@&B0V3@Ha8>+RznTizzvDh9Z>oZHv@wx0|UbusJ=(skPv(gmH*2P2?2HDOagM>iY z4Mc-N0+b#=Y!C)zS0fOE0g|vm*-x2)fkBr6l>ZnQ7(iJal=sycAi*OCRV>E9z+eJp zYcMb{XoGSE10?8}7$9i@R0e>uB}f@47pQ_53=9m$3=kiJLPe1Q;z^Jcs0;zopd2K^ zz~BmEgE1%pf*5Mh!U)9HWME*B2Qe5J7^D~&7?c#FBsb#6Tsd*)n z)mazvKWdoho0yrS>zI;Klv-T8S(`1Jkt;mED1`w;6i=SYt~L1xyY*yFjwp6x1p`AX zL$k?Q9EvdZ=1ClRjFZK
                                - 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/213] 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/213] 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/213] 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/213] 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 zcmca7#4?qEfq@}_fq_AWfq`KT69Ypwh|LNTWnehL#=yYCz`$^lje$Xkfq~%~8v}y? z0|UcLHUwkWU@0ba56A3l(RE1fUIg{huGiF4l#cTI|G9R0|UcWb_NDX1_p+6Q27_^5cjZh zK=cW5Ffgz)Ffd3!`3f8mc{L6O1~vu;24fD0`>deit{e;u91IK$K~OrH17cqa2Lpo& z0|P@T2LppD0|Ub>sJRC?7#Nfp7#L1)K>YOv%KrmZufPd$hbt!ogCGL~gEuF{-dIis z1{MYeh7?YSeVLpPcNIa^*F))6PKf)uIT;xA85kI*aWXI%FfcG2Tj zL)>u~s{Rtx+?(8xcz(^Ug_vG*oa{DA<(-_Haf;qe(N&nO5{#~}#O&o9WpV93D0AS%ef zV8y_|;4TOWhbAaJSrFphC4vkL8Vn2!YXu?hz9tB9_eUr#BLs1`yAUKj`wKzLuNQ)Z zTdNSnofCy1?pi3sz@Wpxz_3n;fkBRef#J3g1A`u@T!GS>!Vr5SgdzG8g(2y%5K31I zL()YnRNZW0NcvkM42k!ZP<6Y7A^tro46*l&FvOiVg(30&5K8}rs+SaDU|?imVDMmI zU{D05D+UGze@KytLW0T)Q27Tc`#|9b%5xwEvJ8;$2Bj%8kN^V%gCCT(WPqeyPL!fdUBm@e7P#Oo7>!7#=iGj*`XHY%B00~Do1_lOQkOTuHK7AM< z=@FDCLFpDG1uDNlGzf$8q6>(@0Es^k6I4!vXb`q%U|_IdU|=w1U|{fMU|=u-34+RT z1_lOK21p!&%3V;I#l!$fJD_xF#K6G7%m68)Y@vFzL9}CXMrLYRYHn&?NilHyO(|9=&a6sR$W5%s%q`7@$YdlIE0h=I=cR+gMB!1x9M$4S4RaYh^Gb^HQ%aM; z4)sYbOU+^M$xJE&dDkaDJu{C1Oe&-$X6B@(fH?W3B@90K#U+q1WAH-`AP@)avw+0n z;`02W6b2Xr?o5P0u|i3HzCsSjNgBC{6?zJ)DGJFMiABYlNV*`3iZk+yN)$A5GxJbX z6{Y4S=B6?PKxl>J{JfIH%)DZS%$%Im^u!#nY2bJ-hO2>v7EDhj!~*QDfSLVm!uX!8AXZUzz)jKNiAjwDg}|jkR+Cw zTFelfkzbmVqEM27NLcXLQYcDI%*m-#NJ>>m0jIqbdt~*Xphu|HP%YNPp`lzMF|W8h zwTL0OG$}W;xESPekh6;!f=i3QVG5=3hAB)9EKK2iJYkBa278!>Bo=2gfCz=eoTAjk zluCu-(&XgS;^MSYknfB0bIMXv7(y~ja#9&W@=FqP6bg#+)4|yzv^cdW9g@jGOt4>I z47{F!D}wn3A%Mp_=!&rW2c)W)Av`}1nrD&|^O93@7?Qz743tVyNUBs&Emi=fp<)I| z>@t9|1w#=u^`xdiC5scwQW?O303ji!f(jb2Uh&q_@$(e+Hx4NENoB?T*mkc?CXpVai!ycC7}G=(Ux0RIqOaDkYapQr1RSdwa` zU}RuuqHAEHYhbEiXl!L{plx7iV89jNuN#zFmI*QiuR5Q^;u77EqQt!7oWzp+A}a+h zpTxZM(!}&s-H_D8TqM4gLP~19iz}C}r>`r*frfepT+aDEOx*?SXsa6UlsTC#q z1v!bCdDh^3Uz}QE8ye!KYk^P)vMw#PNY^zlIX@*cFWpMPA}OX;EU1u3LUlZn2d@UICa}Y-?n# z0Abo{??Of4@) z(u$BYv{EPp)mPxkjSEzNl;)+U<`pMrB&L<5<}tVyEh;Wa&B;m4g9Rs#GDnw5SSE#ln?_39owtauQ3bV0CV0YEfz)5(gA}LHXbW9h91$nv(}k zk*N&9`8hcXDXEZXKqQ0m%%bd^%;XG(^i)WCE>S2?ElNqvvq$KF#TG&rxC+Lj)#vcG zVo(Z6%&SVxOi#^YfC{AMDS*9F%;1-poB^)HK;aML5R3r0Ld+OIk-{GZ=t^=CxgIj)438+}e zEGdB(8PEa=&W8s8EQCP<1Oov@F=jwuNaGI-Y|3#22uvx1dun-pULGQUCnhBorGj!G z1EMsAvPz&4u8^1uYW73YSZW?}3!o@7wWtItUt9nR%G9Df29V0sJZKhxav*86gdsIE eFC7#CpwbI$u0koubcN82BD`{->I7s|2?GG_89bE$ delta 1422 zcmZ3iF;Tkyo)F7a1_lNOYX$}e83qQ1FN_Qf{tOHZXP7~v3=ABs3=BLB3=EvC3=BdH z3=Cqd3=9Gc3=EpA3=HB73=CGR3=CWh3=Bc63=F~y3=FZX3=I4X3=D-(x{;NEft!JW zVIq{C%gVsO%fP^}l9hphlYyb0VLK}W11kdq!$DSv1CB#2c*4rSAj!bM@D(b~&jxXb z4jV+hB^v_+I|BoQJ(Tax#=yYFz`)?k260dbRDC=f0|N&G149m!E`_SEV`E?tWTT+F!oa{V7ph1gFXWT!%j8^1_K5LhL>y*pGmPpLPU)nqTigI zfkA74GGc-Q1vsR>gGfBt%r(lgYx%4>BHRh5DQOnGcXu} zT*%G9V8y_|z{CRy30o-b#{)@h@jQ?;kj?|qH=PIK@VPt?hpps+gv3rBh=Yzn#n1CV zqWC_Pe#rv~@z3>8g`B*Q#3RHD2~u%hhz3nwh|i6AAqHFVLLB1G3kgzxD4h*e-_Fax zzzB*;21pceLfIe+l#P`bAm;0U1Q-|?>OpxL6t|$P2FlMG3=9ma3=H6C0i|+T28a*M zp&CGxHi%$=1U)FPgF?)d0g@&_QlO#(lulF_7#J)WAR(s40Lc{~d3g}Qz`&pk%KuO{ zm;x0fQVfu&0Oe(nASjQ^K?^7l-vSgpAO-^@2tiCxwgu6;3=9lr3=9lL3=9m43=9k= zAVE-}1ufjP7$Bhr5;kT4MIk=}gC+w5gB}AUzBwizWqZuyo0yrS>zI;Klv-Ro`7C?P zWHS!E$*!ELlN&gvOrFUVJUN&9CcBA(p|O>z#bjR|MJU^Haw*S&$u_)6oO*it5Hxun muPlPMc{i^d Date: Sat, 5 Apr 2014 13:31:55 +0200 Subject: [PATCH 174/213] 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/213] 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 zcmZ3i@lB)to)F7a1_lO(AO;2o83qQ1HB1Z)xeN>pHmo2~28Iu83=BLB3=ALH7#M^Y z7#RMsF)#=)Ffa(RGcbrVFfb^wGca&5FfdrLGcX7i%Fe*R%fP@ek)45olYyb0VLm$p1IVtG><|a6XNOpDhMj>yf`NhIE;|E*Bm)D( zH>kV-2gE_T91wk091INX3=9koP`(FL-j9QUfsKKIA({i?z$B=6E(Zex2Ll5`6_~DP zU})xmSlGkCz@Wmwz%ZACfkBmlf#DF;;1?VW49W})3?DckJ`>@D@Krb=>OD9i4$0+Y zU=UX(D)dIkoDwVV(aZsufQ&}U#^*vHAh zV8Fn@@QxD_bn;vf2WoRcd}_l5aexaK#6cNQegzlAL36ku79ZqdU{GLSU^vDFao}?< z1_nL`28Op>5c~dcF)-AFf`XkJqEUbwVv#A7@5T)YiEM6&0Ta2wamFy68{)%-Ph74miclz`(@7z;K=$5;B*d=0AbTf8}PV2gm&{Zivf3DGd~7{5%kg#CRZa zufPKdLMt8yh69WY49+|dhqCZ7Fo-fRFo^O&_}YAsL}|kZ@nHxb1A_r_1_o&c28O3l3mEws7?c_K^gMoqJl_137!a@)S>p|&AA&A3UgdmB$M+oeYdWJnhkf1vx1PPLJLJ*hT z6JlV{VPIf*EyTbe$H2fKA1V~rwxU>T?EkFbVBwGETY*0xFDqCzBAa+=S@;|6}bz^||hLZsjpN>!sAj$?rFhD{9 zl$t?YI|hiay+Ir(c42^IH%|sg(1X$gR0>?M1u`%&I7021p_XRZ!qG!T?ERJ`4;D?hFhJ#tfjiW?%>aDTZP*5be166x&@Ek^CZtqSW-v z;*!)Nh06TWqTW?h DkSUnN delta 1777 zcmeySu~?)2o)F7a1_lO(00ssI83qQ1IZO-;*&sG6NR)x$1RDbb4+8_kNj3%sAqED9 zYitY*0t^fcFWDFv#2FYEez7qya4|422(dFT2s1D+$g(pq@G~$l7(!`Bb_ND+1_p*e zC>_hrz`)DEz>vw#z`)7CP|r}#&cFb&tC1bzfOd9>1xwf&7$g`N7`C!AFi0{mFr0(R zzhH+rh?N7PPl$toft`VYK?2HG;DE@haWF8jF)%O~b3hzu1r>MYU|`^2U|(G#6dD#5C=Ix`2k!I50r30ES|~5z@Wguz%Y*s;=uh}3=Dh> z3=BuPAogA1VqmBT1;u@+#ur==i+H&q2FY|9K%k66a%J5Mf|oP~wB69dkZNVhiAdL`@tYBnk@nAR$-F zR}XP{FCWB53;7rrq!}0(R`5YAIKs!kpv1tyaFvgNL5zWc;RhcBgE|8P13y0`YRvf| zA>+&saeyb3AHvVTAkV$V>Az47k|GQYj0_A69t;c&ilD^Bz`)=SDI8HqP?-TL2|=YG zCpd#6t0TLRH43J=U zVt}~b0jka&lo+9G5M=`*7$6}BVyZDPFqnfF3=9l*43H25B}{LqSiKhmBq4#yc#s$< z3P33xRK|ni7bFHM_nkp?0s|x{-53}cbU_jfkT~^WfFw##)&wP9kQAsO1JNK1%91W1 z1_LAtK}=AQ4Jw5}YzBJ<1_lcT1_o0G1_n^0s

                                    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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 zcmca7#4?qEfq@~2fq_AWfq`KS69Ypo0|SE%D@c@q;R7230}lfO!$&p-1|bFphJS1f z3<3-c41(+o4B`w73`*<_3|tHh3|8z648jZy46f`94EziX43SVelbwNqn}LC$5=ytS zGcfQnFfdGHXJFuDU|^Wf&cFb&Y9%|w{`Kq-^Uts|Fi0>kFx+KlV31^BVE6`=7vO-n zN0$Sl&x(VAft`VY!2!zmfXe%EFfgz&Ffc@OK-`xE70=~hVBlb2V5ox9%^VQ>dN>#u zR2Uc-=5jDFs4_4x9DdN?8WP2_~Qdlppvawxr)6XL$joD2;53=9nWI2jlW7#JAdaYDjPo(tkW zZ7zsEZMY!zyKq6=lL6&da6#NNhYMowK`sUc1qKF&V_XpTJ?COz;A3E5c*_N`?++Km zUF_TteFEGNdrYBxH*QEcWOGCGPvnNg*K}@({}w{&72J?`Uk~LU=7!jRikpFfiGhLP zJU1jo(@I%6>fgfUj z8DZs&*Cw-2iB96!YWH~As{e8JDaAO*^A{0t0Q3=9lB0ub6< z022R60uXz%1R&uvNq~Vt6O?bE^fdv9J3xsMln>biA@+(1Ld0bSA^uhtgoK9yRNf9s zyF%6Z2r@7jGB7X%2{JHPF)%Q+2tvYnKa{>82yyR2K?Vj51_p*#f)IBL3qjng2c;v0 zAntAvf~4;rA&5Kn2tmT_kPsw1&Iv)>bx(+aL5G2X;k6J0gB$|`gM=^xgB}9|gE^E= z6NcD3RT!dgo-ic6tb@`!gdyqT5LDd_VMuy>2&JD1L;Uet7?KYDK=tv7K*C8v1mX^P z5s15WL?G$F1WE^pKSN2GRZukaVaB z;(*FuP}$GGz+l3_z#z-Oz~IFINe`ep#U2!o43KyS)ia><1S+#ZTD2J<;S0)Bpz_iP zB**|MyFg4(8Z-sva|Q+mH3mo?1&MnwK+-R$P6DS721xqyVPIfzXJBA3W`M+V07x+u zn}KM@+N@TIp;?yDrm(-lpl2n8|ln<3FPAKYhj~XWCCT8X+ zB&MVkr50lfC}if9CFW$NFt~yIn_7{anwnCqP@Gwns*sylk(pbX3z5l4ELJEl%Fjy& zhl#?YhB>Omj~eDOc;=N9<)@S;gB|LVT9%r_;FFnD1oEy=etKpe1DI4uOU%qkO#yN8 zOG_Ah@{3C#VaDKx96%rr*k=KW#l_|MMJWt02HcqlfntS{{CtHRkdriW6D#x-R8tg^ zGZKr6HIZ~d6cuOW7nLYz8T>&*0ss_6wz$u3TOlIcif&7r4rl6Vv5h-Q}O3ce<2ue-QEG|hc zf-;H{!GRr=pOae55L5~xgCR*QGqso@I3vF_Cq6oz6@RF{;bBC!+_OW;;MYFMAaP?`r-98i>>m6}|l>zSe(mRbZ# zA65z>8L0|Bsp+YCDGK>%3Q=4E{vo>HQZh3?PuC@}B-Kj6$iUD<*T6*Az*ND|*vi;k z+rZGkfGfaXHz>6%6J!c@b(RoyK8eL8x*8ZLQsfoEr zd@F^N)OZ(HE?-YySA+u%^$fV2^Ycnl^Gb9>DhpDr6iQMnO7shI5;OCx!G%F_YKd)V zh?}kjLLJDuwA3P9*SzHXl+3(zD+Py%iWT986~0Xd~bi8;D%`9-fC^;2WM-``*SSdi<2e&0SHL)l;BOtLPL)XAcp)51CyckI;98WESejIlssM_q%(VQX+{BVpNX?jC0?IR`1qG>j5CKqC znO+1I&;^%l(3%mXFqOeCwKO$P!Mn7mSRu8jxFj_vCp8Z$=#-jQT2hr-gd_o0mveYq zadJj#9s^tfl1OoKMp0^JQfXc~16XlVYGP4RY9`3haOFs1ZmAhLsYMJBN+BsVGZ*3| zv`W)AF*8Tk5nN}cGPq=>Du8NIWI=_@;u3|@y!69+a!NAO!AihE0~W6;RY)(&KRlno zCAA1*d`V@2!lQ-Haz=?lDkNALTr>014)4h*0{H?`i#lZ{Ut)3wxMl-|Ka4{#0^kZUV*o`8e-xlA#SsUNrD^GjNvRAjr3x8|#U%bmqSVY1Q0bADpOan$N`KH0ECE&OsW}X8`9&q5Vj;7n1YTr73nVxn9ssZq z1_=-h1Qf-X0f8ZnKQOQ<#}Obfr3~(=<@tGei2R+HlvI=o%7F}s(iF-nfkwDOVlJpJ z07+x1dC+D-N-C^jPy|ZH3MrY%8PIkCNT39&tGECZz^O%f3?TifdC-gjzy2}5dT eUOFgRKxG=(Hic4 + * @copyright 2014 Heinrich-Heine-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/213] 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/213] 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/213] 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 zcmca7#4?qEfq@~2fq_AWfq`KS69Ypo0|SE%D@c@q;R7230}lfO!$&p-1|bFphJS1f z3<3-c41(+o4B`w73`*<_3|tHh3|8z648jZy46f`94EziX43SVelbwNqn}LC$5=ytS zGcfQnFfdGHXJFuDU|^Wf&cFb&Y9%|w{`Kq-^Uts|Fi0>kFx+KlV31^BVE6`=7vO-n zN0$Sl&x(VAft`VY!2!zmfXe%EFfgz&Ffc@OK-`xE70=~hVBlb2V5ox9%^VQ>dN>#u zR2Uc-=5jDFs4_4x9DdN?8WP2_~Qdlppvawxr)6XL$joD2;53=9nWI2jlW7#JAdaYDjPo(tkW zZ7zsEZMY!zyKq6=lL6&da6#NNhYMowK`sUc1qKF&V_XpTJ?COz;A3E5c*_N`?++Km zUF_TteFEGNdrYBxH*QEcWOGCGPvnNg*K}@({}w{&72J?`Uk~LU=7!jRikpFfiGhLP zJU1jo(@I%6>fgfUj z8DZs&*Cw-2iB96!YWH~As{e8JDaAO*^A{0t0Q3=9lB0ub6< z022R60uXz%1R&uvNq~Vt6O?bE^fdv9J3xsMln>biA@+(1Ld0bSA^uhtgoK9yRNf9s zyF%6Z2r@7jGB7X%2{JHPF)%Q+2tvYnKa{>82yyR2K?Vj51_p*#f)IBL3qjng2c;v0 zAntAvf~4;rA&5Kn2tmT_kPsw1&Iv)>bx(+aL5G2X;k6J0gB$|`gM=^xgB}9|gE^E= z6NcD3RT!dgo-ic6tb@`!gdyqT5LDd_VMuy>2&JD1L;Uet7?KYDK=tv7K*C8v1mX^P z5s15WL?G$F1WE^pKSN2GRZukaVaB z;(*FuP}$GGz+l3_z#z-Oz~IFINe`ep#U2!o43KyS)ia><1S+#ZTD2J<;S0)Bpz_iP zB**|MyFg4(8Z-sva|Q+mH3mo?1&MnwK+-R$P6DS721xqyVPIfzXJBA3W`M+V07x+u zn}KM@+N@TIp;?yDrm(-lpl2n8|ln<3FPAKYhj~XWCCT8X+ zB&MVkr50lfC}if9CFW$NFt~yIn_7{anwnCqP@Gwns*sylk(pbX3z5l4ELJEl%Fjy& zhl#?YhB>Omj~eDOc;=N9<)@S;gB|LVT9%r_;FFnD1oEy=etKpe1DI4uOU%qkO#yN8 zOG_Ah@{3C#VaDKx96%rr*k=KW#l_|MMJWt02HcqlfntS{{CtHRkdriW6D#x-R8tg^ zGZKr6HIZ~d6cuOW7nLYz8T>&*0ss_6wz$u3TOlIcif&7r4rl6Vv5h-Q}O3ce<2ue-QEG|hc zf-;H{!GRr=pOae55L5~xgCR*QGqso@I3vF_Cq6oz6@RF{;bBC!+_OW;;MYFMAaP?`r-98i>>m6}|l>zSe(mRbZ# zA65z>8L0|Bsp+YCDGK>%3Q=4E{vo>HQZh3?PuC@}B-Kj6$iUD<*T6*Az*ND|*vi;k z+rZGkfGfaXHz>6%6J!c@b(RoyK8eL8x*8ZLQsfoEr zd@F^N)OZ(HE?-YySA+u%^$fV2^Ycnl^Gb9>DhpDr6iQMnO7shI5;OCx!G%F_YKd)V zh?}kjLLJDuwA3P9*SzHXl+3(zD+Py%iWT986~0Xd~bi8;D%`9-fC^;2WM-``*SSdi<2e&0SHL)l;BOtLPL)XAcp)51CyckI;98WESejIlssM_q%(VQX+{BVpNX?jC0?IR`1qG>j5CKqC znO+1I&;^%l(3%mXFqOeCwKO$P!Mn7mSRu8jxFj_vCp8Z$=#-jQT2hr-gd_o0mveYq zadJj#9s^tfl1OoKMp0^JQfXc~16XlVYGP4RY9`3haOFs1ZmAhLsYMJBN+BsVGZ*3| zv`W)AF*8Tk5nN}cGPq=>Du8NIWI=_@;u3|@y!69+a!NAO!AihE0~W6;RY)(&KRlno zCAA1*d`V@2!lQ-Haz=?lDkNALTr>014)4h*0{H?`i#lZ{Ut)3wxMl-|Ka4{#0^kZUV*o`8e-xlA#SsUNrD^GjNvRAjr3x8|#U%bmqSVY1Q0bADpOan$N`KH0ECE&OsW}X8`9&q5Vj;7n1YTr73nVxn9ssZq z1_=-h1Qf-X0f8ZnKQOQ<#}Obfr3~(=<@tGei2R+HlvI=o%7F}s(iF-nfkwDOVlJpJ z07+x1dC+D-N-C^jPy|ZH3MrY%8PIkCNT39&tGECZz^O%f3?TifdC-gjzy2}5dT eUOFgRKxG=(Hic4 + * @copyright 2014 Heinrich-Heine-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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 delta 2796 zcmeySaoVu{o)F7a1_lO(LAjo`47>~s3@14m7&sXi7_M_NFt9Q(Fg)ahc;q?MTox_{1_=fR22m~s21y16 z20bo_yayM^WAzLSsa#-}Fcfh?45;N|U|?rpU}%Q&yTJ+>7$$NtFn|JO9#s8OD1QS~ z{dTCj{ag$T91IK$XQ1>|E{F&3aWOEcFfcH@=VD+`Wnf?s;bvf9uV-Lju;ON5P-b9Y zaN>seI1S1#YM+^*apz41@ z>Hpji53=$=%opHcV9;k^U{K&;V5m1>U|0hVtwAAP#EdV_;xnU|{IxgM?Tg zAH-t|pz<5}AP(HdR}XRF9;gAQp%z}^gCv$ad=M9Z<6~erz{tSxA8OD60R{$91_p+U z0uY0r3qW$i4*^J`6cA)!&}Cp?kP~EJkY`|E@DYRrc@C7GBM8YQXX*tZarRgc5=0*a zAwK#o2uZd4LXd2uAq3&u3PCIg6N30GMF^t5Ob8OU%|a0UlZ7B5wOojSL7IVqVVw}f zp{Ilx7?c@>o3s zgR3Y5gCQsvh%zu(fl4w_NKoyC(&t1WKD#fHu}sA2&zJwOBl14A4r z+87|d@n?VpH>gekmG_`Z2c*mvl(<0*1_lOG1_lOZP(cOdD>2kFFgQXvAPSUcJsBYJ z3##QnrJ55+f`NfSmVtr6g@J*=mjM!wiVTo=c3^;n5-9CBL)C(6I#9_9s#QU`3sghe zFfcHfK;=P{E2uUF#XqPT4Q60q2w{Mvcu+0o2U5Ynz!1y8z!1*Bz+ew5M4)1zA{N91 zRZ^f*E{1`D!5SpM0Eur)skcbeP?85$?GqJctA+R*HxMZ>=hY6o=YHm_$5d(xO-kie`%*dOXo0*qb zRH=}bnVOTbc@L)!qe@1qLNJQ3!lQ;cs>P2Q<|=jnPrJ3sVNGTsgvh(KjttrR4_ENGBlgq#iIb{Y~IAPno$rGU<@v) zP(!mzQYRM(sHuZ}nO%}v1d>+BEG|(f%}YPLC#NJc9a-^YM`2BNsM^T|!m80wc4|>- zW_oH~adJjtT1jdiBve7hl~fiWIMCn)i7TWQ6_=#ue%|e_`LulJuO+)D#97BeiJrS^ic=F7MK!;clfr8nL$}Wk;3NSH+M-B5pfjkG*hm&^;eKvGS%|x;sp|ZTRC?!=PFF!d$AulsI TqeLMwyCkzLGqq^)8sSp_-Y}Z> delta 1884 zcmX?Y_)Vk!o)F7a1_lO(AO;2o83qQ1HB1Z)xeN>pHmo2~28Iu83=BLB3=ALH7#M^Y z7#RMsF)#=)Ffa(RGcbrVFfb^wGca&5FfdrLGcX7i%Fe*R%fP@ek)45olYyb0VLm$p1IVtG><|a6XNOpDhMj>yf`NhIE;|E*Bm)D( zH>kV-2gE_T91wk091INX3=9koP`(FL-j9QUfsKKIA({i?z$B=6E(Zex2Ll5`6_~DP zU})xmSlGkCz@Wmwz%ZACfkBmlf#DF;;1?VW49W})3?DckJ`>@D@Krb=>OD9i4$0+Y zU=UX(D)dIkoDwVV(aZsufQ&}U#^*vHAh zV8Fn@@QxD_bn;vf2WoRcd}_l5aexaK#6cNQegzlAL36ku79ZqdU{GLSU^vDFao}?< z1_nL`28Op>5c~dcF)-AFf`XkJqEUbwVv#A7@5T)YiEM6&0Ta2wamFy68{)%-Ph74miclz`(@7z;K=$5;B*d=0AbTf8}PV2gm&{Zivf3DGd~7{5%kg#CRZa zufPKdLMt8yh69WY49+|dhqCZ7Fo-fRFo^O&_}YAsL}|kZ@nHxb1A_r_1_o&c28O3l3mEws7?c_K^gMoqJl_137!a@)S>p|&AA&A3UgdmB$M+oeYdWJnhkf1vx1PPLJLJ*hT z6JlV{VPIf*EyTbe$H2fKA1V~rwxU>T?EkFbVBwGETY*0xFDqCzBAa+=S@;|6}bz^||hLZsjpN>!sAj$?rFhD{9 zl$t?YI|hiay+Ir(c42^IH%|sg(1X$gR0>?M1u`%&I7021p_XRZ!qG!T?ERJ`4;D?hFhJ#tfjiW?%>aDTZP*5bZeGiX)J*cybNr_sMl! zMw|C@1udWaJ+V)dnI%pD0J+|qs{jB1 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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/213] 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 b1ffcc0c5580e76cd5091435d053c3bf0bd006c6 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 14 Apr 2014 16:44:42 +0200 Subject: [PATCH 212/213] 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 213/213] 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){