diff --git a/backend/bin/cli-config.php b/backend/bin/cli-config.php deleted file mode 100644 index b6ba7ed..0000000 --- a/backend/bin/cli-config.php +++ /dev/null @@ -1,6 +0,0 @@ -setAttribute('em', $entityManager); - -?> \ No newline at end of file diff --git a/backend/bin/doctrine b/backend/bin/doctrine deleted file mode 120000 index e49d329..0000000 --- a/backend/bin/doctrine +++ /dev/null @@ -1 +0,0 @@ -/home/steffen/workspace/doctrine2/bin/doctrine \ No newline at end of file diff --git a/backend/bin/doctrine b/backend/bin/doctrine new file mode 100755 index 0000000..92f323f --- /dev/null +++ b/backend/bin/doctrine @@ -0,0 +1,4 @@ +#!/usr/bin/env php +register(); // register on SPL autoload stack +} + +// load configuration into registry +if (!file_exists(BACKEND_DIR . '/volkszaehler.conf.php')) { + throw new Exception('No configuration available! Use volkszaehler.conf.default.php as an template'); +} +include BACKEND_DIR . '/volkszaehler.conf.php'; + +$em = Volkszaehler\Dispatcher::createEntityManager(); + +$helperSet = new \Symfony\Components\Console\Helper\HelperSet(array('em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em))); + +$cli = new \Symfony\Components\Console\Application('Doctrine Command Line Interface', Doctrine\ORM\Version::VERSION); +$cli->setCatchExceptions(true); +$cli->setHelperSet($helperSet); +$cli->addCommands(array( + // DBAL Commands + new \Doctrine\DBAL\Tools\Console\Command\RunSqlCommand(), + new \Doctrine\DBAL\Tools\Console\Command\ImportCommand(), + + // ORM Commands + new \Doctrine\ORM\Tools\Console\Command\ClearCache\MetadataCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\ResultCommand(), + new \Doctrine\ORM\Tools\Console\Command\ClearCache\QueryCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand(), + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\DropCommand(), + new \Doctrine\ORM\Tools\Console\Command\EnsureProductionSettingsCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertDoctrine1SchemaCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateRepositoriesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateEntitiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\GenerateProxiesCommand(), + new \Doctrine\ORM\Tools\Console\Command\ConvertMappingCommand(), + new \Doctrine\ORM\Tools\Console\Command\RunDqlCommand(), + new \Doctrine\ORM\Tools\Console\Command\ValidateSchemaCommand(), +)); +$cli->run(); diff --git a/backend/index.php b/backend/index.php index e78bc1b..6f8b4af 100644 --- a/backend/index.php +++ b/backend/index.php @@ -19,29 +19,39 @@ * http://www.gnu.org/copyleft/gpl.html */ +namespace Volkszaehler; + +use Volkszaehler\Util; +use Volkszaehler\Controller; + +// TODO replace by state class +const VERSION = 1.1; +const BACKEND_DIR = '/home/steffen/workspace/volkszaehler.org/backend'; // TODO realpath(__DIR__) +const DEV_ENV = true; + // class autoloading -require 'lib/vendor/doctrine/Common/ClassLoader.php'; +require BACKEND_DIR . '/lib/Util/ClassLoader.php'; -$doctrineLoader = new \Doctrine\Common\ClassLoader('Doctrine', 'lib/vendor/doctrine'); -$doctrineLoader->register(); // register on SPL autoload stack +$classLoaders = array(); +$classLoaders[] = new Util\ClassLoader('Doctrine', BACKEND_DIR . '/lib/vendor/Doctrine'); +$classLoaders[] = new Util\ClassLoader('Symfony', BACKEND_DIR . '/lib/vendor/Symfony'); +$classLoaders[] = new Util\ClassLoader('Volkszaehler', BACKEND_DIR . '/lib'); -$vzLoader = new \Doctrine\Common\ClassLoader('Volkszaehler', 'lib'); -$vzLoader->register(); // register on SPL autoload stack - -// API version -define('VERSION', '0.2'); +foreach ($classLoaders as $loader) { + $loader->register(); // register on SPL autoload stack +} // enable strict error reporting error_reporting(E_ALL); // load configuration into registry -if (!file_exists(__DIR__ . '/volkszaehler.conf.php')) { +if (!file_exists(BACKEND_DIR . '/volkszaehler.conf.php')) { throw new Exception('No configuration available! Use volkszaehler.conf.default.php as an template'); } -include __DIR__ . '/volkszaehler.conf.php'; +include BACKEND_DIR . '/volkszaehler.conf.php'; -$fc = new FrontController(); // spawn frontcontroller -$fc->run(); // execute controller and sends output +$fc = new Dispatcher; // spawn frontcontroller / dispatcher +$fc->run(); // execute controller and sends output ?> diff --git a/backend/lib/Controller/ChannelController.php b/backend/lib/Controller/Channel.php similarity index 54% rename from backend/lib/Controller/ChannelController.php rename to backend/lib/Controller/Channel.php index 7d10cf6..a08267d 100644 --- a/backend/lib/Controller/ChannelController.php +++ b/backend/lib/Controller/Channel.php @@ -19,40 +19,42 @@ * http://www.gnu.org/copyleft/gpl.html */ -class ChannelController extends Controller { +namespace Volkszaehler\Controller; + +use \Volkszaehler\Model; + +class Channel extends Controller { public function get() { - // TODO get channels from entity manager + // TODO filter by uuid, type etc... + $channels = $this->em->getRepository('Volkszaehler\Model\Channel\Channel')->findAll(); foreach ($channels as $channel) { - $this->view->addChannel($channel); + $this->view->add($channel); } } public function add() { - $channel = new Channel(); + // TODO validate input + $channel = new Model\Channel\Meter('power'); - // TODO how do differ the 1-wire sensors? - /*if (substr($channel->uuid, 0, 19) == OneWireSensor::$uuidPrefix) { - $channel->type = 'OneWireSensor'; - $channel->description = OneWireSensor::getFamilyDescription($channel); - } - else { - $channel->type = 'Channel'; - }*/ + $channel->setName($this->view->request->getParameter('name')); + $channel->setResolution($this->view->request->getParameter('resolution')); + $channel->setDescription($this->view->request->getParameter('description')); + $channel->setCost($this->view->request->getParameter('cost')); - // TODO adapt to doctrine orm - $channel->persist(); - $channel->save(); + $this->em->persist($channel); + $this->em->flush(); - $this->view->addChannel($channel); + $this->view->add($channel); } // TODO check for valid user identity public function delete() { - $channel = Channel::getByUuid($this->view->request->get['ucid']); + $ucid = $this->view->request->getParameter('ucid'); + $channel = $this->em->getRepository('Volkszaehler\Model\Channel\Channel')->findOneBy(array('uuid' => $ucid)); - // TODO adapt to doctrine orm - $channel->delete(); + $this->em->remove($channel); + $this->em->flush(); } public function edit() { diff --git a/backend/lib/Controller/Controller.php b/backend/lib/Controller/Controller.php index d9510f6..ca159e0 100644 --- a/backend/lib/Controller/Controller.php +++ b/backend/lib/Controller/Controller.php @@ -19,20 +19,44 @@ * http://www.gnu.org/copyleft/gpl.html */ -class ControllerException extends Exception {}; +namespace Volkszaehler\Controller; abstract class Controller { protected $view; + protected $em; - public function __construct(View $view) { + /* + * constructor + */ + public function __construct(\Volkszaehler\View\View $view, \Doctrine\ORM\EntityManager $em) { $this->view = $view; + $this->em = $em; } /* - * catches unknown actions + * creates new view instance depending on the requested format */ - public function __call($method, $param) { - throw new ControllerException('Undefined controller action!'); + public static function factory(\Volkszaehler\View\View $view, \Doctrine\ORM\EntityManager $em) { + $controller = ucfirst(strtolower($view->request->getParameter('controller'))); + + $controllerClassName = 'Volkszaehler\Controller\\' . $controller; + if (!(\Volkszaehler\Util\ClassLoader::classExists($controllerClassName)) || !is_subclass_of($controllerClassName, '\Volkszaehler\Controller\Controller')) { + throw new \InvalidArgumentException('\'' . $controllerClassName . '\' is not a valid controller'); + } + return new $controllerClassName($view, $em); + } + + /** + * run controller actions + * + * @param string $action runs the action if class method is available + */ + public function run($action) { + if (!method_exists($this, $action)) { + throw new \InvalidArgumentException('\'' . $action . '\' is not a valid controller action'); + } + + $this->$action(); } } diff --git a/backend/lib/Controller/Data.php b/backend/lib/Controller/Data.php new file mode 100644 index 0000000..0388bc5 --- /dev/null +++ b/backend/lib/Controller/Data.php @@ -0,0 +1,77 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\Controller; + +class Data extends Controller { + public function get() { + // TODO why not ucids? + $ids = explode(',', trim($this->view->request->getParameter('ids'))); + + $q = $this->em->createQuery('SELECT c FROM Volkszaehler\Model\Channel\Channel c WHERE c.id IN (' . implode(', ', $ids) . ')'); + $channels = $q->execute(); + + $from = ($this->view->request->getParameter('from')) ? (int) $this->view->request->getParameter('from') : NULL; + $to = ($this->view->request->getParameter('to')) ? (int) $this->view->request->getParameter('to') : NULL; + $groupBy = ($this->view->request->getParameter('groupBy')) ? $this->view->request->getParameter('groupBy') : NULL; // get all readings by default + + foreach ($channels as $channel) { + $interpreter = $channel->getInterpreter($this->em); + $this->view->add($interpreter->getValues($from, $to, $groupBy)); + } + } + + public function add() { + $ucid = $this->view->request->getParameter('ucid'); + $channel = $this->em->getRepository('Volkszaehler\Model\Channel\Channel')->findOneBy(array('uuid' => $ucid)); + + $value = (float) $this->view->request->getParameter('value'); + $ts = (int) $this->view->request->getParameter('timestamp'); + if ($ts == 0) { + $ts = microtime(true) * 1000; + } + + $data = new \Volkszaehler\Model\Data($channel, $value, $ts); + + $channel->addData($data); + + $this->em->persist($data); + $this->em->flush(); + } + + /* + * prune all data from database + */ + public function delete() { // TODO add user authentification + $dql = 'DELETE FROM \Volkszaehler\Model\Data WHERE channel_id = ' . $this->id; + + if ($this->view->request->getParameter('from')) { + $dql .= ' && timestamp > ' . (int) $this->view->request->getParameter('from'); + } + + if ($this->view->request->getParameter('to')) { + $dql .= ' && timestamp < ' . $this->view->request->getParameter('to'); + } + + $q = $em->createQuery($dql); + $q->execute(); + } +} \ No newline at end of file diff --git a/backend/lib/Controller/DataController.php b/backend/lib/Controller/DataController.php deleted file mode 100644 index 1fdcdc9..0000000 --- a/backend/lib/Controller/DataController.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License (either version 2 or - * version 3) as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * For more information on the GPL, please go to: - * http://www.gnu.org/copyleft/gpl.html - */ - -class DataController extends Controller { - public function get() { - $ids = explode(',', trim($this->view->request->get['ids'])); - $channels = Channel::getByFilter(array('id' => $ids), true, false); // get all channels with id in $ids as an array - - $from = (isset($this->view->request->get['from'])) ? (int) $this->view->request->get['from'] : NULL; - $to = (isset($this->view->request->get['to'])) ? (int) $this->view->request->get['to'] : NULL; - $groupBy = (isset($this->view->request->get['groupBy'])) ? $this->view->request->get['groupBy'] : NULL; // get all readings by default - - foreach ($channels as $channel) { - // TODO change to Channel::getValues() - $this->view->addChannel($channel, $channel->getPulses($from, $to, $groupBy)); - } - } - - public function add() { - $ucid = $this->view->request->get['ucid']; - $channel = Channel::getByUuid($ucid); - $channel->addData($this->view->request->get); // array(timestamp, value, count) - } -} \ No newline at end of file diff --git a/backend/lib/Controller/FrontController.php b/backend/lib/Controller/FrontController.php deleted file mode 100644 index 8030a19..0000000 --- a/backend/lib/Controller/FrontController.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License (either version 2 or - * version 3) as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * For more information on the GPL, please go to: - * http://www.gnu.org/copyleft/gpl.html - */ - -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\Configuration; - -final class FrontController { - // MVC - private $em = NULL; // Model (Doctrine Entitymanager) - private $view = NULL; // View - private $controller = NULL; // Controller - - public function __construct() { - // create view instance - $view = $request->get['format'] . 'View'; - if (!class_exists($view) || !is_subclass_of($view, 'View')) { - throw new InvalidArgumentException('\'' . $view . '\' is not a valid View'); - } - $this->view = new $view; - - $this->em = self::createEntityManager(); - } - - public static function createEntityManager() { - $config = Registry::get('config'); - - // Doctrine - $doctConfig = new Configuration; - - //$cache = new \Doctrine\Common\Cache\ApcCache; - //$config->setMetadataCacheImpl($cache); - - $driverImpl = $doctConfig->newDefaultAnnotationDriver('lib/Model'); - $doctConfig->setMetadataDriverImpl($driverImpl); - - //$config->setQueryCacheImpl($cache); - - $doctConfig->setProxyDir('lib/Model/Proxies'); - $doctConfig->setProxyNamespace('Volkszaehler\Model\Proxies'); - - return EntityManager::create($config['db'], $doctConfig); - } - - public function run() { - // create controller instance - $controller = $request->get['controller'] . 'Controller'; - if (!class_exists($controller) || !is_subclass_of($controller, 'Controller')) { - throw new ControllerException('\'' . $controller . '\' is not a valid controller'); - } - $controller = new $controller($this->view); - - $action = $this->view->request->get['action']; - - $controller->$action(); // run controllers actions (usually CRUD: http://de.wikipedia.org/wiki/CRUD) - } - - public function __destruct() { - $this->view->render(); // render view & send http response - } -} - -?> \ No newline at end of file diff --git a/backend/lib/Controller/GroupController.php b/backend/lib/Controller/Group.php similarity index 74% rename from backend/lib/Controller/GroupController.php rename to backend/lib/Controller/Group.php index 3b4a8f0..bd65b21 100644 --- a/backend/lib/Controller/GroupController.php +++ b/backend/lib/Controller/Group.php @@ -19,7 +19,9 @@ channname = $this->view->request->get['name']; - $group->description = $this->view->request->get['description']; + $group->name = $this->view->request->getParameter('name'); + $group->description = $this->view->request->getParameter('description'); - // TODO adapt to doctrine orm - $group->save(); + $this->em->persist($group); + $this->em->flush(); - $this->view->addGroup($group); + $this->view->add($group); } // TODO check for valid user identity public function delete() { - $group = Group::getByUuid($this->view->request->get['ugid']); + $group = Group::getByUuid($this->view->request->getParameter('ugid')); - // TODO adapt to doctrine orm - $group->delete(); + $this->em->remove($group); + $this->em->flush(); } public function edit() { diff --git a/backend/lib/Controller/UserController.php b/backend/lib/Controller/User.php similarity index 79% rename from backend/lib/Controller/UserController.php rename to backend/lib/Controller/User.php index 743ce3a..a1a6478 100644 --- a/backend/lib/Controller/UserController.php +++ b/backend/lib/Controller/User.php @@ -19,7 +19,9 @@ * http://www.gnu.org/copyleft/gpl.html */ -class UserController extends Controller { +namespace Volkszaehler\Controller; + +class User extends Controller { // TODO do we need this? public function get() { @@ -27,20 +29,20 @@ class UserController extends Controller { public function add() { $user = new User(); - $user->password = $this->view->request->get['password']; + $user->setPassword($this->view->request->getParameter('password')); - // TODO adapt to doctrine orm - $user->save(); + $this->em->persist($user); + $this->em->flush(); - $this->view->addUser($user); + $this->view->add($user); } // TODO check for valid user identity public function delete() { - $user = User::getByUuid($this->view->request->get['uuid']); + $user = User::getByUuid($this->view->request->getParameter('uuid')); - // TODO adapt to doctrine orm - $user->delete(); + $this->em->remove($user); + $this->em->flush(); } public function edit() { diff --git a/backend/lib/Dispatcher.php b/backend/lib/Dispatcher.php new file mode 100644 index 0000000..8badede --- /dev/null +++ b/backend/lib/Dispatcher.php @@ -0,0 +1,94 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler; + +use Volkszaehler\View; +use Volkszaehler\Controller; +use Volkszaehler\Util; + +/* + * frontcontroller / dispatcher + */ +final class Dispatcher { + // MVC + private $em = NULL; // Model (Doctrine EntityManager) + private $view = NULL; // View + private $controller = NULL; // Controller + + /* + * constructor + */ + public function __construct() { + $request = new View\Http\Request(); + $response = new View\Http\Response(); + + $format = $request->getParameter('format'); + $controller = $request->getParameter('controller'); + + $this->em = Dispatcher::createEntityManager(); + $this->view = View\View::factory($request, $response); + $this->controller = Controller\Controller::factory($this->view, $this->em); + } + + /** + * execute application + */ + public function run() { + $action = (is_null($this->view->request->getParameter('action'))) ? 'get' : $this->view->request->getParameter('action'); // default action + + $this->controller->run($action); // run controllers actions (usually CRUD: http://de.wikipedia.org/wiki/CRUD) + $this->view->render(); // render view & send http response + } + + /** + * factory for doctrines entitymanager + * + * @todo create extra singleton class or registry? + */ + public static function createEntityManager() { + $vzConfig = Util\Registry::get('config'); + + // Doctrine + $dcConfig = new \Doctrine\ORM\Configuration; + + if (extension_loaded('apc')) { + $cache = new \Doctrine\Common\Cache\ApcCache; + $dcConfig->setMetadataCacheImpl($cache); + $dcConfig->setQueryCacheImpl($cache); + } + + $driverImpl = $dcConfig->newDefaultAnnotationDriver(BACKEND_DIR . '/lib/Model'); + $dcConfig->setMetadataDriverImpl($driverImpl); + + $dcConfig->setProxyDir(BACKEND_DIR . '/lib/Model/Proxies'); + $dcConfig->setProxyNamespace('Volkszaehler\Model\Proxies'); + $dcConfig->setAutoGenerateProxyClasses(DEV_ENV == true); + + $dcConfig->setSQLLogger(Util\Debug::getSQLLogger()); + + $em = \Doctrine\ORM\EntityManager::create($vzConfig['db'], $dcConfig); + + return $em; + } +} + +?> diff --git a/backend/lib/Model/Channel.php b/backend/lib/Interpreter/Interpreter.php similarity index 60% rename from backend/lib/Model/Channel.php rename to backend/lib/Interpreter/Interpreter.php index 0666ccc..2c515f4 100644 --- a/backend/lib/Model/Channel.php +++ b/backend/lib/Interpreter/Interpreter.php @@ -19,67 +19,28 @@ * http://www.gnu.org/copyleft/gpl.html */ -interface ChannelInterface { - // data management - public function addData($data); - public function getData($from = NULL, $to = NULL, $groupBy = NULL); - public function reset(); +namespace Volkszaehler\Interpreter; - // some statistical functions +interface InterpreterInterface { + public function getValues($from = NULL, $to = NULL, $groupBy = NULL); public function getMin($from = NULL, $to = NULL); public function getMax($from = NULL, $to = NULL); public function getAverage($from = NULL, $to = NULL); } -/** - * Channel class - * - * @Entity - * @Table(name="channels") - */ -abstract class Channel extends Entity implements ChannelInterface { - /** @Column(type="string") */ - protected $type; - - /** @Column(type="integer") */ - protected $resolution; - - /** @Column(type="integer") */ - protected $cost; - - /** @Column(type="string") */ - protected $name; - - /** @Column(type="string") */ - protected $description; +abstract class Interpreter implements InterpreterInterface { + protected $channel; + protected $em; /* - * prune all data from database - */ - public function reset($from = 0, $to = NULL) { - // TODO add timefilter - $sql = 'DELETE FROM data WHERE channel_id = ' . (int) $this->id . ' && from to'; - - // TODO delelte with doctrine dal - } - - /* - * add a new data to the database + * constructor */ - public function addData($data) { - $sql = 'INSERT INTO data (channel_id, timestamp, value) VALUES(' . $this->dbh->escape($this) . ', ' . $this->dbh->escape($data['timestamp']) . ', ' . $this->dbh->escape($data['value']) . ')'; - // TODO insert with doctrine dal + public function __construct(\Volkszaehler\Model\Channel\Channel $channel, \Doctrine\ORM\EntityManager $em) { + $this->channel = $channel; + $this->em = $em; } - - /* - * retrieve data from the database - * - * If desired it groups it into packages ($groupBy parameter) - * - * @return array() Array with timestamps => value (sorted by timestamp from newest to oldest) - * @param $groupBy determines how readings are grouped. Possible values are: year, month, day, hour, minute or an integer for the desired size of the returned array - */ - public function getData($from = NULL, $to = NULL, $groupBy = NULL) { + + protected function getData($from = NULL, $to = NULL, $groupBy = NULL) { $ts = 'FROM_UNIXTIME(timestamp/1000)'; // just for saving space switch ($groupBy) { case 'year': @@ -107,7 +68,7 @@ abstract class Channel extends Entity implements ChannelInterface { break; default: - if (is_numeric($groupBy)) { + if (is_numeric($groupBy)) { // lets agrregate it with php $groupBy = (int) $groupBy; } $sqlGroupBy = false; @@ -115,18 +76,26 @@ abstract class Channel extends Entity implements ChannelInterface { $sql = 'SELECT'; $sql .= ($sqlGroupBy === false) ? ' timestamp, value' : ' MAX(timestamp) AS timestamp, SUM(value) AS value, COUNT(timestamp) AS count'; - $sql .= ' FROM data WHERE channel_id = ' . (int) $this->id . $this->buildFilterTime($from, $to); + $sql .= ' FROM data WHERE channel_id = ' . (int) $this->channel->getId(); // TODO add time filter if ($sqlGroupBy !== false) { $sql .= ' GROUP BY ' . $sqlGroupBy; } $sql .= ' ORDER BY timestamp DESC'; - - // TODO query with doctrine dal - //$result = $this->dbh->query($sql); - //$totalCount = $result->count(); + $rsm = new \Doctrine\ORM\Query\ResultsetMapping; + $rsm->addScalarResult('timestamp', 'timestamp'); + $rsm->addScalarResult('value', 'value'); + + if ($sqlGroupBy) { + $rsm->addScalarResult('count', 'count'); + } + + $query = $this->em->createNativeQuery($sql, $rsm); + $result = $query->getResult(); + $totalCount = count($result); + if (is_int($groupBy) && $groupBy < $totalCount) { // return $groupBy values $packageSize = floor($totalCount / $groupBy); $packageCount = $groupBy; @@ -137,23 +106,25 @@ abstract class Channel extends Entity implements ChannelInterface { } $packages = array(); - $reading = $result->rewind(); + $reading = reset($result); for ($i = 1; $i <= $packageCount; $i++) { $package = array('timestamp' => $reading['timestamp'], // last timestamp in package 'value' => (float) $reading['value'], // sum of values 'count' => ($sqlGroupBy === false) ? 1 : $reading['count']); // total count of values or pulses in the package while ($package['count'] < $packageSize) { - $reading = $result->next(); + $reading = next($result); $package['value'] += $reading['value']; $package['count']++; } $packages[] = $package; - $reading = $result->next(); + $reading = next($result); } return array_reverse($packages); // start with oldest ts and ends with newest ts (reverse array order due to descending order in sql statement) } -} \ No newline at end of file +} + +?> \ No newline at end of file diff --git a/backend/lib/Interpreter/Meter.php b/backend/lib/Interpreter/Meter.php new file mode 100644 index 0000000..cf6d9dd --- /dev/null +++ b/backend/lib/Interpreter/Meter.php @@ -0,0 +1,92 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\Interpreter; + +class Meter extends Interpreter { + + public function getConsumption($from = NULL, $to = NULL) { // TODO untested + $sql = 'SELECT SUM(value) AS count + FROM data + WHERE + channel_id = ' . (int) $this->id . ' && + ' . $this->getTimeFilter($from, $to) . ' + GROUP BY channel_id'; + + $result = $this->dbh->query($sql)->rewind(); + + return $result['count'] / $this->resolution / 1000; // returns Wh + } + + public function getMin($from = NULL, $to = NULL) { + $data = $this->getData($from, $to); + + $min = current($data); + foreach ($data as $reading) { + if ($reading['value '] < $min['value']) { + $min = $reading; + } + } + return $min; + } + + public function getMax($from = NULL, $to = NULL) { + $data = $this->getData($from, $to); + + $min = current($data); + foreach ($data as $reading) { + if ($reading['value '] > $min['value']) { + $min = $reading; + } + } + return $min; + } + + public function getAverage($from = NULL, $to = NULL) { // TODO calculate timeinterval if no params were given + return $this->getConsumption($from, $to) / ($to - $from) / 1000; // return W + } + + /* + * just a passthru of raw data + */ + public function getPulses($from = NULL, $to = NULL, $groupBy = NULL) { + return parent::getData($from, $to, $groupBy); + } + + /* + * raw pulses to power conversion + */ + public function getValues($from = NULL, $to = NULL, $groupBy = NULL) { + $pulses = parent::getData($from, $to, $groupBy); + $pulseCount = count($pulses); + + for ($i = 1; $i < $pulseCount; $i++) { + $delta = $pulses[$i]['timestamp'] - $pulses[$i-1]['timestamp']; + + $pulses[$i]['timestamp'] -= $delta/2; + $pulses[$i]['value'] *= 3600000/(($this->channel->getResolution() / 1000) * $delta); // TODO untested + } + + return $pulses; // returns W + } +} + +?> \ No newline at end of file diff --git a/backend/lib/Interpreter/Sensor.php b/backend/lib/Interpreter/Sensor.php new file mode 100644 index 0000000..b736b9f --- /dev/null +++ b/backend/lib/Interpreter/Sensor.php @@ -0,0 +1,49 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\Interpreter; + +class Sensor extends Interpreter { + + public function getData($from = NULL, $to = NULL, $groupBy = NULL) { + $data = parent::getData($from, $to, $groupBy); + + array_walk($data, function(&$reading) { + $reading['value'] /= $reading['count']; // calculate average (ungroup the sql sum() function) + }); + + return $data; + } + + public function getMin($from = NULL, $to = NULL) { // TODO untested + return $this->dbh->query('SELECT value, timestamp FROM data WHERE channel_id = ' . (int) $this->id . self::buildFilterTime($from, $to) . ' ORDER BY value ASC', 1)->current(); + } + + public function getMax($from = NULL, $to = NULL) { // TODO untested + return $this->dbh->query('SELECT value, timestamp FROM data WHERE channel_id = ' . (int) $this->id . self::buildFilterTime($from, $to) . ' ORDER BY value DESC', 1)->current(); + } + + public function getAverage($from = NULL, $to = NULL) { // TODO untested + return $this->dbh->query('SELECT AVG(value) AS value FROM data WHERE channel_id = ' . (int) $this->id . self::buildFilterTime($from, $to))->current(); + } +} + +?> \ No newline at end of file diff --git a/backend/lib/Model/Channel/Meter/PowerMeter.php b/backend/lib/Logger/Flukso.php similarity index 92% rename from backend/lib/Model/Channel/Meter/PowerMeter.php rename to backend/lib/Logger/Flukso.php index f68939a..71f3c80 100644 --- a/backend/lib/Model/Channel/Meter/PowerMeter.php +++ b/backend/lib/Logger/Flukso.php @@ -19,8 +19,10 @@ * http://www.gnu.org/copyleft/gpl.html */ -class PowerMeter extends Meter { - const unit = 'kW/h'; +namespace Volkszaehler\Logger; + +class Flukso implements Logger { + } ?> \ No newline at end of file diff --git a/backend/lib/Logger/Logger.php b/backend/lib/Logger/Logger.php new file mode 100644 index 0000000..cae526a --- /dev/null +++ b/backend/lib/Logger/Logger.php @@ -0,0 +1,36 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\Logger; + +/* + * interface for parsing diffrent logging APIs (google, flukso etc..) + */ +interface Logger { + public function __construct(\Volkszaehler\View\Http\Request $request); + + /** + * @return \Volkszaehler\Model\Data $data the parsed data + */ + public function getData(); +} + +?> \ No newline at end of file diff --git a/backend/lib/Model/Channel/Channel.php b/backend/lib/Model/Channel/Channel.php new file mode 100644 index 0000000..627dd68 --- /dev/null +++ b/backend/lib/Model/Channel/Channel.php @@ -0,0 +1,90 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\Model\Channel; + +use Doctrine\Common\Collections\ArrayCollection; + +/** + * Channel class + * + * @Entity + * @Table(name="channels") + * @InheritanceType("SINGLE_TABLE") + * @DiscriminatorColumn(name="type", type="string") + * @DiscriminatorMap({ + * "meter" = "Meter", + * "sensor" = "Sensor" + * }) + */ +abstract class Channel extends \Volkszaehler\Model\Entity { + /** @Column(type="string") */ + protected $name; + + /** @Column(type="string") */ + protected $description; + + /** @Column(type="string") */ + protected $indicator; + + /** + * @OneToMany(targetEntity="Volkszaehler\Model\Data", mappedBy="channel"), cascade={"remove"} + */ + private $data = NULL; + + /* + * constructor + */ + public function __construct($indicator) { + parent::__construct(); + + $this->indicator = $indicator; + $this->data = new ArrayCollection(); + } + + /* + * getter & setter + */ + public function getName() { return $this->name; } + public function setName($name) { $this->name = $name; } + public function getDescription() { return $this->description; } + public function setDescription($description) { $this->description = $description; } + public function getUnit() { return static::$indicators[$this->indicator]; } + public function getIndicator() { return $this->indicator; } + + /* + * add a new data to the database + */ + public function addData(\Volkszaehler\Model\Data $data) { + $this->data->add($data); + } + + /* + * obtain channels data interpreter to calculate statistical information + */ + public function getInterpreter(\Doctrine\ORM\EntityManager $em) { + $interpreterClassName = 'Volkszaehler\Interpreter\\' . substr(strrchr(get_class($this), '\\'), 1); + if (!(\Volkszaehler\Util\ClassLoader::classExists($interpreterClassName)) || !is_subclass_of($interpreterClassName, '\Volkszaehler\Interpreter\Interpreter')) { + throw new \InvalidArgumentException('\'' . $interpreterClassName . '\' is not a valid Interpreter'); + } + return new $interpreterClassName($this, $em); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Channel/Meter.php b/backend/lib/Model/Channel/Meter.php index e8ed187..1608ef5 100644 --- a/backend/lib/Model/Channel/Meter.php +++ b/backend/lib/Model/Channel/Meter.php @@ -19,69 +19,31 @@ * http://www.gnu.org/copyleft/gpl.html */ -abstract class Meter extends Channel { - public function getConsumption($from = NULL, $to = NULL) { // TODO untested - $sql = 'SELECT SUM(value) AS count - FROM data - WHERE - channel_id = ' . (int) $this->id . ' && - ' . $this->getTimeFilter($from, $to) . ' - GROUP BY channel_id'; +namespace Volkszaehler\Model\Channel; - $result = $this->dbh->query($sql)->rewind(); +/** + * Meter class + * + * @Entity + */ +class Meter extends Channel { + /** @Column(type="integer") */ + private $resolution; - return $result['count'] / $this->resolution / 1000; // returns Wh - } - - public function getMin($from = NULL, $to = NULL) { - $data = $this->getData($from, $to); - - $min = current($data); - foreach ($data as $reading) { - if ($reading['value '] < $min['value']) { - $min = $reading; - } - } - return $min; - } - - public function getMax($from = NULL, $to = NULL) { - $data = $this->getData($from, $to); - - $min = current($data); - foreach ($data as $reading) { - if ($reading['value '] > $min['value']) { - $min = $reading; - } - } - return $min; - } - - public function getAverage($from = NULL, $to = NULL) { // TODO calculate timeinterval if no params were given - return $this->getConsumption($from, $to) / ($to - $from) / 1000; // return W - } + /** @Column(type="decimal") */ + private $cost; /* - * just a passthru of raw data + * indicator => unit mapping */ - public function getPulses($from = NULL, $to = NULL, $groupBy = NULL) { - return parent::getData($from, $to, $groupBy); - } + protected static $indicators = array( + 'power' => 'kW/h', + 'gas' => 'qm/h', + 'water' => 'qm/h' + ); - /* - * raw pulses to power conversion - */ - public function getData($from = NULL, $to = NULL, $groupBy = NULL) { - $pulses = parent::getData($from, $to, $groupBy); - $pulseCount = count($pulses); - - for ($i = 1; $i < $pulseCount; $i++) { - $delta = $pulses[$i]['timestamp'] - $pulses[$i-1]['timestamp']; - - $pulses[$i]['timestamp'] -= $delta/2; - $pulses[$i]['value'] *= 3600000/(($this->resolution / 1000) * $delta); // TODO untested - } - - return $pulses; // returns W - } + public function getResolution() { return $this->resolution; } + public function setResolution($resolution) { $this->resolution = $resolution; } + public function getCost() { return $this->cost; } + public function setCost($cost) { $this->cost = $cost; } } \ No newline at end of file diff --git a/backend/lib/Model/Channel/Sensor.php b/backend/lib/Model/Channel/Sensor.php index 5df151f..eb81164 100644 --- a/backend/lib/Model/Channel/Sensor.php +++ b/backend/lib/Model/Channel/Sensor.php @@ -19,26 +19,21 @@ * http://www.gnu.org/copyleft/gpl.html */ -abstract class Sensor extends Channel { - public function getData($from = NULL, $to = NULL, $groupBy = NULL) { - $data = parent::getData($from, $to, $groupBy); - - array_walk($data, function(&$reading) { - $reading['value'] /= $reading['count']; // calculate average (ungroup the sql sum() function) - }); - - return $data; - } +namespace Volkszaehler\Model\Channel; - public function getMin($from = NULL, $to = NULL) { // TODO untested - return $this->dbh->query('SELECT value, timestamp FROM data WHERE channel_id = ' . (int) $this->id . self::buildFilterTime($from, $to) . ' ORDER BY value ASC', 1)->current(); - } +/** + * Channel class + * + * @Entity + */ +class Sensor extends Channel { - public function getMax($from = NULL, $to = NULL) { // TODO untested - return $this->dbh->query('SELECT value, timestamp FROM data WHERE channel_id = ' . (int) $this->id . self::buildFilterTime($from, $to) . ' ORDER BY value DESC', 1)->current(); - } - - public function getAverage($from = NULL, $to = NULL) { // TODO untested - return $this->dbh->query('SELECT AVG(value) AS value FROM data WHERE channel_id = ' . (int) $this->id . self::buildFilterTime($from, $to))->current(); - } + /* + * indicator => unit mapping + */ + protected static $indicators = array( + 'temperature' => '° C', + 'pressure' => 'hPa', + 'humidity' => '%' + ); } \ No newline at end of file diff --git a/backend/lib/Model/Channel/Sensor/OneWireSensor.php b/backend/lib/Model/Channel/Sensor/OneWireSensor.php deleted file mode 100644 index ad6b587..0000000 --- a/backend/lib/Model/Channel/Sensor/OneWireSensor.php +++ /dev/null @@ -1,130 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License (either version 2 or - * version 3) as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * For more information on the GPL, please go to: - * http://www.gnu.org/copyleft/gpl.html - */ - -/* - * Class for Dallas Semiconductor 1-Wire sensors - * http://www.dalsemi.com/ - */ -class OneWireSensor extends Sensor { - const unit = 'todo'; - - /* - * all 1-wire sensor should use a uuid with this prefix followed by their unique rom id - */ - static public $uuidPrefix = '07506920-6e7a-11df-'; - - /* - * the first byte of the rom id contains the family id describing the type of the sensors - * the rom id should be included in the uuid of the sensor. so we can determine the family out of the uuid. - */ - static function getFamilyDescription($channel) { - $family = base_convert(substr($channel->uuid, 19, 2), 16, 10); - - switch($family) { - case 0x01: - return 'DS2401/DS1990A Serial Number iButton'; - case 0x02: - return 'DS1425/DS1991 MultiKey iButton'; - case 0x04: - return 'DS2402/DS1994 4K NVRAM memory, clock, timer'; - case 0x05: - return 'DS2405 Addressable Switch'; - case 0x06: - return 'DS1993 4K NVRAM Memory'; - case 0x08: - return 'DS1992 1K NVRAM Memory'; - case 0x09: - return 'DS2502/DS1982 1Kbit Add only memory'; - case 0x0A: - return 'DS1995 16K NVRAM Memory'; - case 0x0B: - return 'DS2505/DS1985 16K EPROM Memory'; - case 0x0C: - return 'DS1996/x2/x4 64K to 256K NVRAM Memory'; - case 0x0F: - return 'DS2506/DS1986 64K EEPROM Memory'; - case 0x10: - return 'DS1820/DS18S20/DS1920 Temperature Sensor'; - case 0x12: - return 'DS2406/2407 Dual Addressable Switch + 1Kbit memory'; - case 0x14: - return 'DS2430A/DS1971 256bit EEPROM iButton'; - case 0x18: - return 'DS1963S SHA iButton'; - case 0x1A: - return 'DS1963L 4kBit MONETARY iButton'; - case 0x1C: - return 'DS2422 1Kbit RAM + Counter'; - case 0x1D: - return 'DS2423 4Kbit RAM + Counter'; - case 0x1F: - return 'DS2409 MicroLAN Coupler'; - case 0x20: - return 'DS2450 Quad A/D Converter'; - case 0x21: - return 'DS1921/H/Z Thermochron iButton'; - case 0x22: - return 'DS1822 Econo-Temperature Sensor'; - case 0x23: - return 'DS2433/DS1973 4K EEPROM Memory'; - case 0x24: - return 'DS1425/DS1904 Real Time Clock'; - case 0x26: - return 'DS2438 Temperature, A/D Battery Monitor'; - case 0x27: - return 'DS2417 Real Time Clock with Interrupt'; - case 0x28: - return 'DS18B20 Temperature Sensor'; - case 0x29: - return 'DS2408 8-Channel Addressable Switch'; - case 0x2C: - return 'DS2890 Single Channel Digital Potentiometer'; - case 0x30: - return 'DS2760 Temperature, Current, A/D'; - case 0x33: - return 'DS2432/DS1961S 1K EEPROM with SHA-1 Engine'; - case 0x3A: - return 'DS2413 Dual Channel Addressable Switch'; - case 0x41: - return 'DS1923 Hygrochron Temperature/Humidity Logger with 8kB Data Log Memory'; - case 0x42: - return 'DS28EA00 Temperature Sensor with Sequence Detect and PIO'; - case 0x82: - return 'DS1425 Multi iButton'; - case 0x84: - return 'DS1427 TIME iButton'; - case 0x89: - return 'DS2502/1982 1024bit UniqueWare Add Only Memory'; - case 0x8B: - return 'DS2505/1985 16Kbit UniqueWare Add Only Memory'; - case 0x8F: - return 'DS2506/1986 64Kbit UniqueWare Add Only Memory'; - case 0x91: - return 'DS1981 512-bit EEPROM Memory UniqueWare Only'; - case 0x96: - return 'DS1955/DS1957B Java Cryptographic iButton'; - default: - return false; - } - } -} - -?> \ No newline at end of file diff --git a/backend/lib/Model/Data.php b/backend/lib/Model/Data.php new file mode 100644 index 0000000..190ef89 --- /dev/null +++ b/backend/lib/Model/Data.php @@ -0,0 +1,63 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\Model; + +use Doctrine\Common\Collections\ArrayCollection; + +/** + * @Entity + * @Table(name="data") + */ +class Data { + /** + * ending timestamp of period in ms since 1970 + * + * @Id + * @Column(type="bigint") + */ + private $timestamp; + + /** @Column(type="decimal") */ + private $value; + + /** + * @Id + * @ManyToOne(targetEntity="Volkszaehler\Model\Channel\Channel", inversedBy="data") + * @JoinColumn(name="channel_id", referencedColumnName="id") + */ + private $channel; + + public function __construct(Channel\Channel $channel, $value, $timestamp) { + $this->channel = $channel; + $this->value = $value; + $this->timestamp = $timestamp; + } + + /* + * setter & getter + */ + public function getValue() { return $this->value; } + public function getTimestamp() { return $this->timestamp; } + public function getChannel() { return $this->channel; } +} + +?> diff --git a/backend/lib/Model/Entity.php b/backend/lib/Model/Entity.php index 0554362..9057158 100644 --- a/backend/lib/Model/Entity.php +++ b/backend/lib/Model/Entity.php @@ -19,19 +19,33 @@ * http://www.gnu.org/copyleft/gpl.html */ +namespace Volkszaehler\Model; + +use Volkszaehler\Util; + /** * Database Entity * - * @todo doctrine abstract entity? - * @Entity + * @MappedSuperclass */ abstract class Entity { /** * @Id * @Column(type="integer") + * @GeneratedValue(strategy="AUTO") */ protected $id; /** @Column(type="string", length=36) */ protected $uuid; + + public function __construct() { + $this->uuid = Util\Uuid::mint(); + } + + /* + * getter & setter + */ + public function getId() { return $this->id; } // read only + public function getUuid() { return $this->uuid; } // read only } \ No newline at end of file diff --git a/backend/lib/Model/Group.php b/backend/lib/Model/Group.php index cbe41c0..efb1093 100644 --- a/backend/lib/Model/Group.php +++ b/backend/lib/Model/Group.php @@ -19,6 +19,8 @@ * http://www.gnu.org/copyleft/gpl.html */ +namespace Volkszaehler\Model; + use Doctrine\Common\Collections\ArrayCollection; /** @@ -29,21 +31,46 @@ use Doctrine\Common\Collections\ArrayCollection; */ class Group extends Entity { /** @Column(type="string") */ - protected $name; + private $name; /** @Column(type="string") */ - protected $description; + private $description; - // TODO doctrine join - protected $channels = NULL; + /** + * @ManyToMany(targetEntity="Volkszaehler\Model\Channel\Channel") + * @JoinTable(name="groups_channel", + * joinColumns={@JoinColumn(name="group_id", referencedColumnName="id")}, + * inverseJoinColumns={@JoinColumn(name="channel_id", referencedColumnName="id")} + * ) + */ + private $channels = NULL; - // TODO doctrine nested selfjoin - protected $children = NULL; + /** + * @ManyToMany(targetEntity="Group") + * @JoinTable(name="groups_groups", + * joinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")}, + * inverseJoinColumns={@JoinColumn(name="child_id", referencedColumnName="id")} + * ) + */ + private $children = NULL; + /* + * construct + */ public function __construct() { + parent::__construct(); + $this->channels = new ArrayCollection(); $this->children = new ArrayCollection(); } + + /* + * getter & setter + */ + public function getName() { return $this->name; } + public function setName($name) { $this->name = $name; } + public function getDescription() { return $this->description; } + public function setDescription($description) { $this->description = $description; } } ?> \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelChannelChannelProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelChannelChannelProxy.php new file mode 100644 index 0000000..8419aae --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelChannelChannelProxy.php @@ -0,0 +1,93 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getName() + { + $this->_load(); + return parent::getName(); + } + + public function setName($name) + { + $this->_load(); + return parent::setName($name); + } + + public function getDescription() + { + $this->_load(); + return parent::getDescription(); + } + + public function setDescription($description) + { + $this->_load(); + return parent::setDescription($description); + } + + public function getUnit() + { + $this->_load(); + return parent::getUnit(); + } + + public function addData(\Volkszaehler\Model\Data $data) + { + $this->_load(); + return parent::addData($data); + } + + public function getInterpreter(\Doctrine\ORM\EntityManager $em) + { + $this->_load(); + return parent::getInterpreter($em); + } + + public function getId() + { + $this->_load(); + return parent::getId(); + } + + public function getUuid() + { + $this->_load(); + return parent::getUuid(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array('name', 'description', 'indicator', 'data', 'id', 'uuid'); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelChannelMeterProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelChannelMeterProxy.php new file mode 100644 index 0000000..d0eba44 --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelChannelMeterProxy.php @@ -0,0 +1,117 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getResolution() + { + $this->_load(); + return parent::getResolution(); + } + + public function setResolution($resolution) + { + $this->_load(); + return parent::setResolution($resolution); + } + + public function getCost() + { + $this->_load(); + return parent::getCost(); + } + + public function setCost($cost) + { + $this->_load(); + return parent::setCost($cost); + } + + public function getName() + { + $this->_load(); + return parent::getName(); + } + + public function setName($name) + { + $this->_load(); + return parent::setName($name); + } + + public function getDescription() + { + $this->_load(); + return parent::getDescription(); + } + + public function setDescription($description) + { + $this->_load(); + return parent::setDescription($description); + } + + public function getUnit() + { + $this->_load(); + return parent::getUnit(); + } + + public function addData(\Volkszaehler\Model\Data $data) + { + $this->_load(); + return parent::addData($data); + } + + public function getInterpreter(\Doctrine\ORM\EntityManager $em) + { + $this->_load(); + return parent::getInterpreter($em); + } + + public function getId() + { + $this->_load(); + return parent::getId(); + } + + public function getUuid() + { + $this->_load(); + return parent::getUuid(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array('name', 'description', 'indicator', 'data', 'id', 'uuid', 'resolution', 'cost'); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelChannelSensorProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelChannelSensorProxy.php new file mode 100644 index 0000000..996f8ce --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelChannelSensorProxy.php @@ -0,0 +1,93 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getName() + { + $this->_load(); + return parent::getName(); + } + + public function setName($name) + { + $this->_load(); + return parent::setName($name); + } + + public function getDescription() + { + $this->_load(); + return parent::getDescription(); + } + + public function setDescription($description) + { + $this->_load(); + return parent::setDescription($description); + } + + public function getUnit() + { + $this->_load(); + return parent::getUnit(); + } + + public function addData(\Volkszaehler\Model\Data $data) + { + $this->_load(); + return parent::addData($data); + } + + public function getInterpreter(\Doctrine\ORM\EntityManager $em) + { + $this->_load(); + return parent::getInterpreter($em); + } + + public function getId() + { + $this->_load(); + return parent::getId(); + } + + public function getUuid() + { + $this->_load(); + return parent::getUuid(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array('name', 'description', 'indicator', 'data', 'id', 'uuid'); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelDataProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelDataProxy.php new file mode 100644 index 0000000..7a193b5 --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelDataProxy.php @@ -0,0 +1,57 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getValue() + { + $this->_load(); + return parent::getValue(); + } + + public function getTimestamp() + { + $this->_load(); + return parent::getTimestamp(); + } + + public function getChannel() + { + $this->_load(); + return parent::getChannel(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array('timestamp', 'value', 'channel'); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelEntityProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelEntityProxy.php new file mode 100644 index 0000000..4482eea --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelEntityProxy.php @@ -0,0 +1,51 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getId() + { + $this->_load(); + return parent::getId(); + } + + public function getUuid() + { + $this->_load(); + return parent::getUuid(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array(); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelGroupProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelGroupProxy.php new file mode 100644 index 0000000..270c002 --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelGroupProxy.php @@ -0,0 +1,75 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getName() + { + $this->_load(); + return parent::getName(); + } + + public function setName($name) + { + $this->_load(); + return parent::setName($name); + } + + public function getDescription() + { + $this->_load(); + return parent::getDescription(); + } + + public function setDescription($description) + { + $this->_load(); + return parent::setDescription($description); + } + + public function getId() + { + $this->_load(); + return parent::getId(); + } + + public function getUuid() + { + $this->_load(); + return parent::getUuid(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array('name', 'description', 'channels', 'children', 'id', 'uuid'); + } +} \ No newline at end of file diff --git a/backend/lib/Model/Proxies/VolkszaehlerModelUserProxy.php b/backend/lib/Model/Proxies/VolkszaehlerModelUserProxy.php new file mode 100644 index 0000000..1881b19 --- /dev/null +++ b/backend/lib/Model/Proxies/VolkszaehlerModelUserProxy.php @@ -0,0 +1,75 @@ +_entityPersister = $entityPersister; + $this->_identifier = $identifier; + } + private function _load() + { + if (!$this->__isInitialized__ && $this->_entityPersister) { + $this->__isInitialized__ = true; + if ($this->_entityPersister->load($this->_identifier, $this) === null) { + throw new \Doctrine\ORM\EntityNotFoundException(); + } + unset($this->_entityPersister); + unset($this->_identifier); + } + } + + + public function getEmail() + { + $this->_load(); + return parent::getEmail(); + } + + public function setEmail($email) + { + $this->_load(); + return parent::setEmail($email); + } + + public function setPassword($password) + { + $this->_load(); + return parent::setPassword($password); + } + + public function checkPassword($password) + { + $this->_load(); + return parent::checkPassword($password); + } + + public function getId() + { + $this->_load(); + return parent::getId(); + } + + public function getUuid() + { + $this->_load(); + return parent::getUuid(); + } + + + public function __sleep() + { + if (!$this->__isInitialized__) { + throw new \RuntimeException("Not fully loaded proxy can not be serialized."); + } + return array('email', 'password', 'groups', 'id', 'uuid'); + } +} \ No newline at end of file diff --git a/backend/lib/Model/User.php b/backend/lib/Model/User.php index 87ed35d..742a557 100644 --- a/backend/lib/Model/User.php +++ b/backend/lib/Model/User.php @@ -19,6 +19,8 @@ * http://www.gnu.org/copyleft/gpl.html */ +namespace Volkszaehler\Model; + use Doctrine\Common\Collections\ArrayCollection; /** @@ -29,17 +31,42 @@ use Doctrine\Common\Collections\ArrayCollection; */ class User extends Entity { /** @Column(type="string") */ - protected $email; + private $email; /** @Column(type="string") */ - protected $passwords; + private $password; - // TODO doctrine join - protected $groups = NULL; + /** + * @ManyToMany(targetEntity="Group") + * @JoinTable(name="groups_users", + * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, + * inverseJoinColumns={@JoinColumn(name="group_id", referencedColumnName="id")} + * ) + */ + private $groups = NULL; + /* + * constructor + */ public function __construct() { + parent::__construct(); + $this->groups = new ArrayCollection(); } + + /* + * getter & setter + */ + public function getEmail() { return $this->email; } + public function setEmail($email) { $this->email = $email; } + public function setPassword($password) { $this->password = sha1($password); } + + /* + * check hashed password against cleartext + */ + public function checkPassword($password) { + return (sha1($password) === $this->password); + } } ?> \ No newline at end of file diff --git a/backend/lib/Util/ClassLoader.php b/backend/lib/Util/ClassLoader.php new file mode 100644 index 0000000..9d3f5c3 --- /dev/null +++ b/backend/lib/Util/ClassLoader.php @@ -0,0 +1,204 @@ +. + */ + +/* + * slightly modified from doctrine + */ + +namespace Volkszaehler\Util; + +class ClassLoader { + private $fileExtension = '.php'; + private $namespace; + private $includePath; + private $namespaceSeparator = '\\'; + + /** + * Creates a new ClassLoader that loads classes of the + * specified namespace from the specified include path. + * + * If no include path is given, the ClassLoader relies on the PHP include_path. + * If neither a namespace nor an include path is given, the ClassLoader will + * be responsible for loading all classes, thereby relying on the PHP include_path. + * + * @param string $ns The namespace of the classes to load. + * @param string $includePath The base include path to use. + */ + public function __construct($ns = null, $includePath = null) { + $this->namespace = $ns; + $this->includePath = $includePath; + } + + /** + * Sets the base include path for all class files in the namespace of this ClassLoader. + * + * @param string $includePath + */ + public function setIncludePath($includePath) { + $this->includePath = $includePath; + } + + /** + * Gets the base include path for all class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getIncludePath() { + return $this->includePath; + } + + /** + * Sets the file extension of class files in the namespace of this ClassLoader. + * + * @param string $fileExtension + */ + public function setFileExtension($fileExtension) { + $this->fileExtension = $fileExtension; + } + + /** + * Gets the file extension of class files in the namespace of this ClassLoader. + * + * @return string + */ + public function getFileExtension() { + return $this->fileExtension; + } + + /** + * Registers this ClassLoader on the SPL autoload stack. + */ + public function register() { + spl_autoload_register(array($this, 'loadClass')); + } + + /** + * Removes this ClassLoader from the SPL autoload stack. + */ + public function unregister() { + spl_autoload_unregister(array($this, 'loadClass')); + } + + /** + * Loads the given class or interface. + * + * @param string $classname The name of the class to load. + * @return boolean TRUE if the class has been successfully loaded, FALSE otherwise. + */ + public function loadClass($className) { + if ($this->namespace !== null && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { + return false; + } + + $subNamespace = substr($className, strlen($this->namespace)); + $parts = explode($this->namespaceSeparator, $subNamespace); + $path = implode(DIRECTORY_SEPARATOR, $parts); + + require_once ($this->includePath !== null ? $this->includePath : '') . $path . $this->fileExtension; + return true; + } + + /** + * Asks this ClassLoader whether it can potentially load the class (file) with + * the given name. + * + * @param string $className The fully-qualified name of the class. + * @return boolean TRUE if this ClassLoader can load the class, FALSE otherwise. + */ + public function canLoadClass($className) { + if ($this->namespace !== null && strpos($className, $this->namespace . $this->namespaceSeparator) !== 0) { + return false; // TODO handle with exceptions + } + + $subNamespace = substr($className, strlen($this->namespace)); + $parts = explode($this->namespaceSeparator, $subNamespace); + $path = implode(DIRECTORY_SEPARATOR, $parts); + + return file_exists(($this->includePath !== null ? $this->includePath . DIRECTORY_SEPARATOR : '') . $path . $this->fileExtension); + } + + /** + * Checks whether a class with a given name exists. A class "exists" if it is either + * already defined in the current request or if there is an autoloader on the SPL + * autoload stack that is a) responsible for the class in question and b) is able to + * load a class file in which the class definition resides. + * + * If the class is not already defined, each autoloader in the SPL autoload stack + * is asked whether it is able to tell if the class exists. If the autoloader is + * a ClassLoader, {@link canLoadClass} is used, otherwise the autoload + * function of the autoloader is invoked and expected to return a value that + * evaluates to TRUE if the class (file) exists. As soon as one autoloader reports + * that the class exists, TRUE is returned. + * + * Note that, depending on what kinds of autoloaders are installed on the SPL + * autoload stack, the class (file) might already be loaded as a result of checking + * for its existence. This is not the case with a ClassLoader, who separates + * these responsibilities. + * + * @param string $className The fully-qualified name of the class. + * @return boolean TRUE if the class exists as per the definition given above, FALSE otherwise. + */ + public static function classExists($className) { + if (class_exists($className, false)) { + return true; + } + + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader)) { // array(???, ???) + if (is_object($loader[0])) { + if ($loader[0] instanceof ClassLoader) { // array($obj, 'methodName') + if ($loader[0]->canLoadClass($className)) { + return true; + } + } else if ($loader[0]->{$loader[1]}($className)) { + return true; + } + } else if ($loader[0]::$loader[1]($className)) { // array('ClassName', 'methodName') + return true; + } + } else if ($loader instanceof \Closure) { // function($className) {..} + if ($loader($className)) { + return true; + } + } else if (is_string($loader) && $loader($className)) { // "MyClass::loadClass" + return true; + } + } + + return false; + } + + /** + * Gets the ClassLoader from the SPL autoload stack that is responsible + * for (and is able to load) the class with the given name. + * + * @param string $className The name of the class. + * @return The ClassLoader for the class or NULL if no such ClassLoader exists. + */ + public static function getClassLoader($className) { + foreach (spl_autoload_functions() as $loader) { + if (is_array($loader) && $loader[0] instanceof ClassLoader && + $loader[0]->canLoadClass($className)) { + return $loader[0]; + } + } + + return null; + } +} \ No newline at end of file diff --git a/backend/lib/Util/configuration.php b/backend/lib/Util/Debug.php similarity index 66% rename from backend/lib/Util/configuration.php rename to backend/lib/Util/Debug.php index 425db29..f3095d8 100644 --- a/backend/lib/Util/configuration.php +++ b/backend/lib/Util/Debug.php @@ -19,18 +19,16 @@ * http://www.gnu.org/copyleft/gpl.html */ -class Configuration extends Registry implements ArrayAccess { - public function offsetSet($offset, $value) { - $this->registry[$offset] = $value; - } - public function offsetExists($offset) { - return isset($this->registry[$offset]); - } - public function offsetUnset($offset) { - unset($this->registry[$offset]); - } - public function offsetGet($offset) { - return isset($this->registry[$offset]) ? $this->registry[$offset] : null; +namespace Volkszaehler\Util; + +class Debug { + private static $logger = NULL; + + static public function getSQLLogger() { + if (is_null(self::$logger)) { + self::$logger = new \Doctrine\DBAL\Logging\DebugStack(); + } + return self::$logger; } } diff --git a/backend/lib/Util/registry.php b/backend/lib/Util/Registry.php similarity index 95% rename from backend/lib/Util/registry.php rename to backend/lib/Util/Registry.php index aacc9ea..644db1c 100644 --- a/backend/lib/Util/registry.php +++ b/backend/lib/Util/Registry.php @@ -19,6 +19,8 @@ * http://www.gnu.org/copyleft/gpl.html */ +namespace Volkszaehler\Util; + /** * Registry class to pass global variables between classes. */ @@ -43,7 +45,7 @@ abstract class Registry { self::$registry[$key] = $value; return true; } else { - throw new Exception('Unable to set variable `' . $key . '`. It was already set.'); + throw new \Exception('Unable to set variable `' . $key . '`. It was already set.'); } } diff --git a/backend/lib/Util/uuid.php b/backend/lib/Util/Uuid.php similarity index 95% rename from backend/lib/Util/uuid.php rename to backend/lib/Util/Uuid.php index 07024cc..6ab859a 100644 --- a/backend/lib/Util/uuid.php +++ b/backend/lib/Util/Uuid.php @@ -31,6 +31,10 @@ * Last revised 2010-02-15 */ +namespace Volkszaehler\Util; + +class Exception extends \Exception {} + class Uuid { const MD5 = 3; const SHA1 = 5; @@ -69,7 +73,7 @@ class Uuid { return new self(self::mintTime($node)); case 2: // Version 2 is not supported - throw new UUIDException("Version 2 is unsupported."); + throw new Exception("Version 2 is unsupported."); case 3: return new self(self::mintName(self::MD5, $node, $ns)); case 4: @@ -77,7 +81,7 @@ class Uuid { case 5: return new self(self::mintName(self::SHA1, $node, $ns)); default: - throw new UUIDException("Selected version is invalid or unsupported."); + throw new Exception("Selected version is invalid or unsupported."); } } @@ -145,9 +149,12 @@ class Uuid { } protected function __construct($uuid) { - if (strlen($uuid) != 16) - throw new UUIDException("Input must be a 128-bit integer."); + if (strlen($uuid) != 16) { + throw new Exception("Input must be a 128-bit integer."); + } + $this->bytes = $uuid; + // Optimize the most common use $this->string = bin2hex(substr($uuid,0,4))."-". @@ -208,11 +215,11 @@ class Uuid { /* Generates a Version 3 or Version 5 UUID. These are derived from a hash of a name and its namespace, in binary form. */ if (!$node) - throw new UUIDException("A name-string is required for Version 3 or 5 UUIDs."); + throw new Exception("A name-string is required for Version 3 or 5 UUIDs."); // if the namespace UUID isn't binary, make it so $ns = self::makeBin($ns, 16); if (!$ns) - throw new UUIDException("A binary namespace is required for Version 3 or 5 UUIDs."); + throw new Exception("A binary namespace is required for Version 3 or 5 UUIDs."); switch($ver) { case self::MD5: $version = self::version3; @@ -258,7 +265,7 @@ class Uuid { self::$randomSource = new COM('CAPICOM.Utilities.1'); // See http://msdn.microsoft.com/en-us/library/aa388182(VS.85).aspx self::$randomFunc = 'randomCOM'; } - catch(Exception $e) {} + catch(\Exception $e) {} } return self::$randomFunc; } @@ -291,6 +298,3 @@ class Uuid { return base64_decode(self::$randomSource->GetRandom($bytes,0)); // straight binary mysteriously doesn't work, hence the base64 } } - -class UUIDException extends Exception { -} \ No newline at end of file diff --git a/backend/lib/View/Http/Request.php b/backend/lib/View/Http/Request.php new file mode 100644 index 0000000..8e7bfa1 --- /dev/null +++ b/backend/lib/View/Http/Request.php @@ -0,0 +1,61 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\View\Http; + +class Request { + protected $headers; + protected $parameters; + + /** + * HTTP request methods + * + * @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html + */ + public $method; + + /* + * constructor + */ + public function __construct() { + $this->headers = apache_response_headers(); // NOTICE only works for Apache Webservers + + $this->method = $_SERVER['REQUEST_METHOD']; + + $this->parameters= array( + 'get' => $_GET, + 'post' => $_POST, + 'cookies' => $_COOKIE, + 'files' => $_FILES + ); + + unset($_GET, $_POST, $_COOKIE, $_FILES); + } + + /* + * setter & getter + */ + public function getHeader($header) { return $this->headers[$header]; } + + public function getParameter($name, $method = 'get') { + return (isset($this->parameters[$method][$name])) ? $this->parameters[$method][$name] : NULL; + } +} diff --git a/backend/lib/View/Http/HttpHandle.php b/backend/lib/View/Http/Response.php similarity index 65% rename from backend/lib/View/Http/HttpHandle.php rename to backend/lib/View/Http/Response.php index 4b2eefd..8104e11 100644 --- a/backend/lib/View/Http/HttpHandle.php +++ b/backend/lib/View/Http/Response.php @@ -19,9 +19,14 @@ * http://www.gnu.org/copyleft/gpl.html */ -abstract class HttpHandle { - public $code; +namespace Volkszaehler\View\Http; + +/* + * simple class to control the output buffering + */ +class Response { protected $headers = array(); + protected $code = 200; // default code (OK) protected static $codes = array( 100 => 'Continue', @@ -65,6 +70,40 @@ abstract class HttpHandle { 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported' ); + + /* + * constructor + */ + public function __construct() { + $this->headers = apache_response_headers(); + + ob_start(array($this, 'obCallback')); + } + + public function obCallback($output) { + return $output; // simple passthrough + } + + public function send() { + // change returncode + header('HTTP/1.1 ' . $this->code . ' ' . self::getCodeDescription($this->code)); // TODO untested + + // send headers + foreach ($this->headers as $name => $value) { + header($name . ': ' . $value); + } + ob_end_flush(); + } + + /* + * setter & getter + */ + public function setHeader($header, $value) { $this->headers[$header] = $value; } + public function getHeader($header) { return $this->headers[$header]; } + public function getCode() { return $this->code; } + public function setCode($code) { $this->code = $code; } + static public function getCodeDescription($code) { + return (isset(self::$codes[$code])) ? self::$codes[$code] : false; + } } -?> \ No newline at end of file diff --git a/backend/lib/View/Json.php b/backend/lib/View/Json.php new file mode 100644 index 0000000..0d188d6 --- /dev/null +++ b/backend/lib/View/Json.php @@ -0,0 +1,68 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\View; + +use Volkszaehler\Util; + +class Json extends View { + protected $json = array(); + + /* + * constructor + */ + public function __construct(Http\Request $request, Http\Response $response) { + parent::__construct($request, $response); + + $this->json['source'] = 'volkszaehler.org'; + $this->json['version'] = \Volkszaehler\VERSION; + + $this->response->setHeader('Content-type', 'application/json'); + } + + public function render() { + parent::render(); + + echo json_encode($this->json); + } + + protected function addDebug() { + $config = Util\Registry::get('config'); + + $this->json['debug'] = array('time' => $this->getTime(), + 'database' => array('driver' => $config['db']['driver'], + 'queries' => Util\Debug::getSQLLogger()->queries) + ); + + } + + protected function addException(\Exception $exception) { + $this->json['exception'] = array('type' => get_class($exception), + 'message' => $exception->getMessage(), + 'code' => $exception->getCode(), + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'trace' => $exception->getTrace() + ); + } +} + +?> \ No newline at end of file diff --git a/backend/lib/View/Json/Channel.php b/backend/lib/View/Json/Channel.php new file mode 100644 index 0000000..9e4e1b8 --- /dev/null +++ b/backend/lib/View/Json/Channel.php @@ -0,0 +1,51 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\View\Json; + +class Channel extends \Volkszaehler\View\Json { + + public function add(\Volkszaehler\Model\Channel\Channel $obj, $data = NULL) { + $channel['id'] = (int) $obj->getId(); + $channel['uuid'] = (string) $obj->getUuid(); + $channel['type'] = strtolower(substr(strrchr(get_class($obj), '\\'), 1)); + $channel['indicator'] = $obj->getIndicator(); + $channel['unit'] = $obj->getUnit(); + $channel['name'] = $obj->getName(); + $channel['description'] = $obj->getDescription(); + + if (is_subclass_of($obj, '\Volkszaehler\Model\Channel\Meter')) { + $channel['resolution'] = (int) $obj->getResolution(); + $channel['cost'] = (float) $obj->getCost(); + } + + if (!is_null($data) && is_array($data)) { + $channel['data'] = array(); + foreach ($data as $reading) { + $channel['data'][] = array($reading['timestamp'], $reading['value'], $reading['count']); + } + } + + $this->json['channels'][] = $channel; + } +} + +?> \ No newline at end of file diff --git a/backend/lib/View/Json/Data.php b/backend/lib/View/Json/Data.php new file mode 100644 index 0000000..3c761d7 --- /dev/null +++ b/backend/lib/View/Json/Data.php @@ -0,0 +1,30 @@ + + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License (either version 2 or + * version 3) as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * For more information on the GPL, please go to: + * http://www.gnu.org/copyleft/gpl.html + */ + +namespace Volkszaehler\View\Json; + +class Data extends \Volkszaehler\View\Json { + public function add($data) { + $this->json['data'][] = $data; + } +} + +?> \ No newline at end of file diff --git a/backend/lib/View/Http/HttpResponse.php b/backend/lib/View/Json/Group.php similarity index 57% rename from backend/lib/View/Http/HttpResponse.php rename to backend/lib/View/Json/Group.php index a899b3b..5cd6034 100644 --- a/backend/lib/View/Http/HttpResponse.php +++ b/backend/lib/View/Json/Group.php @@ -19,33 +19,24 @@ * http://www.gnu.org/copyleft/gpl.html */ -class HttpResponse extends HttpHandle { +namespace Volkszaehler\View\Json; + +class Group extends \Volkszaehler\View\Json { - public $code = 200; // default code (OK) - - public function __construct() { - $this->headers = apache_response_headers(); + public function add(\Volkszaehler\Model\Group $obj, $recursive = false) { + $group['id'] = (int) $obj->getId(); + $group['uuid'] = (string) $obj->getUuid(); + $group['name'] = $obj->getName(); + $group['description'] = $obj->getDescription(); - ob_start(array($this, 'obCallback')); - } - - public function obCallback($output) { - return $output; - } - - public function send() { - // change returncode - header('HTTP/1.1 ' . $this->code . ' ' . HttpHandle::$codes[$this->code]); // TODO untested - - // send headers - foreach ($this->headers as $name => $value) { - header($name . ': ' . $value); + if ($recursive) { // TODO add really nested sub groups + $children = $obj->getChildren(); + + foreach ($children as $child) { + $this->addGroup($child, $recursive); + } } - ob_end_flush(); - } - - public function setHeader($header, $value) { - $this->headers[$header] = $value; + + $this->json['groups'][] = $group; } } - diff --git a/backend/lib/View/Http/HttpRequest.php b/backend/lib/View/Json/User.php similarity index 61% rename from backend/lib/View/Http/HttpRequest.php rename to backend/lib/View/Json/User.php index 5aff302..083d5b0 100644 --- a/backend/lib/View/Http/HttpRequest.php +++ b/backend/lib/View/Json/User.php @@ -19,32 +19,17 @@ * http://www.gnu.org/copyleft/gpl.html */ -class HttpRequest extends HttpHandle { - public $code; +namespace Volkszaehler\View\Json; + +class User extends \Volkszaehler\View\Json { - public $server; - public $get; - public $post; - public $cookies; - public $files; - - public function __construct() { - $this->headers = apache_response_headers(); - - $this->server = $_SERVER; - $this->get = $_GET; - $this->post = $_POST; - $this->cookies = $_COOKIE; - $this->files = $_FILES; - - unset($_SERVER); - unset($_GET); - unset($_POST); - unset($_COOKIE); - unset($_FILES); + public function add(\Volkszaehler\Model\User $obj) { + $user['id'] = (int) $obj->getId(); + $user['uuid'] = (string) $obj->addUuid(); + $user['email'] = $obj->getEmail(); + + $this->json['users'][] = $user; } - - public function getHeader($header) { - return $this->headers[$header]; - } -} \ No newline at end of file +} + +?> \ No newline at end of file diff --git a/backend/lib/View/JsonView.php b/backend/lib/View/JsonView.php deleted file mode 100644 index 8ac9342..0000000 --- a/backend/lib/View/JsonView.php +++ /dev/null @@ -1,115 +0,0 @@ - - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License (either version 2 or - * version 3) as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * For more information on the GPL, please go to: - * http://www.gnu.org/copyleft/gpl.html - */ - -class JsonView extends View { - public $jsonData = array(); - - public function __construct() { - parent::__construct(); - - $config = Registry::get('config'); - - $this->jsonData['source'] = 'volkszaehler.org'; - $this->jsonData['version'] = VERSION; - $this->jsonData['storage'] = $config['db']['backend']; - $this->jsonData['controller'] = $request->get['controller']; - $this->jsonData['action'] = $request->get['action']; - - $this->response->setHeader('Content-type', 'application/json'); - } - - public function render() { - $this->jsonData['time'] = $this->getTime(); - echo json_encode($this->jsonData); - } - - protected function addException(Exception $exception) { - $this->jsonData['exception'] = array('message' => $exception->getMessage(), - 'code' => $exception->getCode(), - 'file' => $exception->getFile(), - 'line' => $exception->getLine(), - 'trace' => $exception->getTrace() - ); - } - - public function addChannel(Channel $obj, $data = NULL) { - $channel['id'] = (int) $obj->id; - $channel['uuid'] = $obj->uuid; - $channel['type'] = $obj->type; - $channel['unit'] = $obj::unit; - $channel['name'] = $obj->name; - $channel['description'] = $obj->description; - $channel['resolution'] = (int) $obj->resolution; - $channel['cost'] = (float) $obj->cost; - - if (!is_null($data) && is_array($data)) { - $channel['data'] = array(); - foreach ($data as $reading) { - $channel['data'][] = array($reading['timestamp'], $reading['value'], $reading['count']); - } - } - - $this->jsonData['channels'][] = $channel; - } - - public function addUser(User $obj) { - $user['id'] = (int) $obj->id; - $user['uuid'] = $obj->uuid; - - $this->jsonData['users'][] = $user; - } - - public function addGroup(Group $obj, $recursive = false) { // TODO fix this. how do we want to handly nested set structures? - $group['id'] = (int) $obj->id; - $group['uuid'] = $obj->uuid; - $group['name'] = $obj->name; - $group['description'] = $obj->description; - - $backtrace = array(&$group); - - if ($recursive) { - $children = $obj->getChildren(); - - foreach ($children as $child) { - $subGroup['id'] = (int) $child->id; - $subGroup['uuid'] = $child->uuid; - $subGroup['name'] = $child->name; - $subGroup['description'] = $child->description; - - if ($child->level > $lastLevel) { - array_push(end($backtrace), $subGroup); - // array_push($backtrace, &$subgroup); // TODO: Deprecated: Call-time pass-by-reference has been deprecated - } - elseif ($child->level < $lastLevel) { - array_pop($backtrace); - array_push(end($backtrace), $subGroup); - } - elseif ($child->level == $lastLevel) { - array_push(end($backtrace), $subGroup); - } - } - } - - $this->jsonData['groups'][] = $group; - } -} - -?> \ No newline at end of file diff --git a/backend/lib/View/View.php b/backend/lib/View/View.php index c361bca..419f065 100644 --- a/backend/lib/View/View.php +++ b/backend/lib/View/View.php @@ -19,25 +19,19 @@ * http://www.gnu.org/copyleft/gpl.html */ -interface ViewInterface { - public function render(); - public function exceptionHandler(Exception $exception); - public function errorHandler($errno, $errstr, $errfile, $errline); - - public function addChannel(Channel $obj); - public function addUser(User $obj); - public function addGroup(Group $obj); -} +namespace Volkszaehler\View; -abstract class View implements ViewInterface { +abstract class View { public $request; protected $response; + private $created; // holds timestamp of creation, used later to return time of execution - public function __construct() { - $this->request = new HttpRequest(); - $this->response = new HttpResponse(); + public function __construct(Http\Request $request, Http\Response $response) { + $this->request = $request; + $this->response = $response; + // TODO move to Debug or State class $this->created = microtime(true); // error & exception handling by view @@ -45,25 +39,50 @@ abstract class View implements ViewInterface { set_error_handler(array($this, 'errorHandler')); } - final public function errorHandler($errno, $errstr, $errfile, $errline) { - $this->exceptionHandler(new ErrorException($errstr, 0, $errno, $errfile, $errline)); + /* + * creates new view instance depending on the requested format + */ + public static function factory(Http\Request $request, Http\Response $response) { + $format = ucfirst(strtolower($request->getParameter('format'))); + $controller = ucfirst(strtolower($request->getParameter('controller'))); + + $viewClassName = 'Volkszaehler\View\\' . $format . '\\' . $controller; + if (!(\Volkszaehler\Util\ClassLoader::classExists($viewClassName)) || !is_subclass_of($viewClassName, '\Volkszaehler\View\View')) { + throw new \InvalidArgumentException('\'' . $viewClassName . '\' is not a valid View'); + } + + return new $viewClassName($request, $response); } - final public function exceptionHandler(Exception $exception) { + /* + * error & exception handling + */ + final public function errorHandler($errno, $errstr, $errfile, $errline) { + $this->exceptionHandler(new \ErrorException($errstr, 0, $errno, $errfile, $errline)); + } + + final public function exceptionHandler(\Exception $exception) { $this->addException($exception); //$this->status = STATUS_EXCEPTION; // TODO add status reporting to API - $this->code = 400; // TODO add Exception => HTTP code mapping + + $code = ($exception->getCode() == 0 && Http\Response::getCodeDescription($exception->getCode())) ? 400 : $exception->getCode(); + $this->response->setCode($code); $this->render(); die(); } + // TODO move this into Debug or State Class protected function getTime() { return round(microtime(true) - $this->created, 4); } - public function __destruct() { - $this->response->send(); // send response + public function render() { + if (!is_null($this->request->getParameter('debug')) && $this->request->getParameter('debug') > 0) { + $this->addDebug(); + } + + $this->response->send(); } } \ No newline at end of file diff --git a/backend/lib/View/XmlView.php b/backend/lib/View/Xml.php similarity index 93% rename from backend/lib/View/XmlView.php rename to backend/lib/View/Xml.php index 09bada3..0c879ab 100644 --- a/backend/lib/View/XmlView.php +++ b/backend/lib/View/Xml.php @@ -19,7 +19,11 @@ * http://www.gnu.org/copyleft/gpl.html */ -class XmlView extends View { +namespace Volkszaehler\View; + + +// TODO outdated +class Xml extends View { private $xmlDoc; private $xml; private $xmlChannels; @@ -41,13 +45,13 @@ class XmlView extends View { $this->xml->appendChild($this->xmlDoc->createElement('source', 'volkszaehler.org')); $this->xml->appendChild($this->xmlDoc->createElement('storage', $config['db']['backend'])); - $this->xml->appendChild($this->xmlDoc->createElement('controller', $request->get['controller'])); - $this->xml->appendChild($this->xmlDoc->createElement('action', $request->get['action'])); + $this->xml->appendChild($this->xmlDoc->createElement('controller', $request->getParameter('controller'))); + $this->xml->appendChild($this->xmlDoc->createElement('action', $request->getParameter('action'))); $this->response->setHeader('Content-type', 'text/xml'); } - public function addChannel(Channel $obj, $data = NULL) { + public function addChannel(\Volkszaehler\Model\Channel $obj, $data = NULL) { $xmlChannel = $this->xmlDoc->createElement('channel'); $xmlChannel->setAttribute('id', (int) $obj->id); @@ -77,7 +81,7 @@ class XmlView extends View { $this->xmlChannels->appendChild($xmlChannel); } - public function addUser(User $obj) { + public function addUser(\Volkszaehler\Model\User $obj) { $xmlUser = $this->xmlDoc->createElement('user'); $xmlUser->setAttribute('id', (int) $obj->id); $xmlUser->appendChild($this->xmlDoc->createElement('uuid', $obj->uuid)); @@ -85,7 +89,7 @@ class XmlView extends View { $this->xmlUsers->appendChild($xmlUser); } - public function addGroup(Group $obj) { + public function addGroup(\Volkszaehler\Model\Group $obj) { $xmlGroup = $this->xmlDoc->createElement('group'); $xmlGroup->setAttribute('id', (int) $obj->id); $xmlGroup->appendChild($this->xmlDoc->createElement('uuid', $obj->uuid)); @@ -118,7 +122,7 @@ class XmlView extends View { echo $this->xmlDoc->saveXML(); } - protected function addException(Exception $exception) { + protected function addException(\Exception $exception) { $xmlException = $this->xmlDoc->createElement('exception'); $xmlException->setAttribute('code', $exception->getCode()); $xmlException->appendChild($this->xmlDoc->createElement('message', $exception->getMessage()));