Merge branch 'master' of github.com:volkszaehler/volkszaehler.org

This commit is contained in:
Justin Otherguy 2011-01-16 23:16:42 +01:00
commit 3070c5337c
26 changed files with 238 additions and 99 deletions

3
.gitmodules vendored
View file

@ -1,3 +1,6 @@
[submodule "misc/controller/ethersex"]
path = misc/controller/ethersex
url = git://github.com/ethersex/ethersex.git
[submodule "misc/website"]
path = misc/website
url = git@github.com:volkszaehler/website.git

3
README
View file

@ -18,7 +18,8 @@ volkszaehler.org/
| \_ demo/ Demo Daten zum Import in die Datenbank
|
|_ tools/ Verschiedene Scripts zum Importieren von Daten, Installation, Helper etc.
\_ tests/ Einfache Testcases für einige Klasses des Backends
|_ tests/ Einfache Testcases für einige Klasses des Backends
\_ website/ Die Website unter http://volkszaehler.org
Wiki: http://wiki.volkszaehler.org
Other: http://volkszaehler.org

BIN
htdocs/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -3,6 +3,9 @@
<head>
<title>volkszaehler.org - web frontend</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="shortcut icon" type="image/x-icon" href="../favicon.ico">
<link rel="apple-touch-icon" href="../favicon.ico">
<!-- jQuery -->
<script type="text/javascript" src="javascripts/jquery/jquery-1.4.2.min.js"></script>
@ -130,7 +133,10 @@
<!--</ul>-->
<div id="entity-subscribe">
<p>Hier k&ouml;nnen Sie einen existierenden Kanal über seine UUID hinzuf&uuml;gen</p>
<p><label for="uuid">UUID: </label><input id="uuid" type="text" size="36" maxlength="36" /></p>
<table>
<tr><td><label for="uuid">UUID:</label></td><td><input id="uuid" type="text" size="36" maxlength="36" /></td></tr>
<tr><td><label for="remember">Cookie:</label></td><td><input id="remember" type="checkbox" /></td></tr>
</table>
<input type="button" value="abonnieren" />
</div>
<!--div id="entity-create"> -->

View file

@ -37,19 +37,31 @@ vz.wui.init = function() {
// buttons
$('button, input[type=button],[type=image]').button();
$('button[name=options-save]').click(function() { vz.options.save(); });
$('#permalink').click(function() { // TODO add uuids
var u = window.location.protocol + '//' +
$('#permalink').click(function() {
var uuids = [];
var url = window.location.protocol + '//' +
window.location.host +
window.location.pathname +
'?from=' + vz.options.plot.xaxis.min +
'&to=' + vz.options.plot.xaxis.max;
window.location = u;
vz.entities.each(function(entity, parent) {
if (entity.active) {
uuids.push(entity.uuid);
}
});
uuids.unique().each(function(key, value) {
url += '&uuid=' + value;
});
window.location = url;
});
$('button[name=entity-add]').click(function() { $('#entity-add').dialog('open'); });
$('#entity-subscribe input[type=button]').click(function() {
try {
vz.uuids.add($('#entity-subscribe input[type=text]').val());
vz.uuids.save();
$('#entity-subscribe input[type=text]').val('');
$('#entity-add').dialog('close');
vz.entities.loadDetails();
@ -80,16 +92,16 @@ vz.wui.init = function() {
// auto refresh
if (vz.options.refresh) {
$('#refresh').attr('checked', true);
vz.wui.interval = window.setInterval(vz.wui.refresh, vz.options.refreshInterval);
vz.wui.timeout = window.setTimeout(vz.wui.refresh, 3000);
}
$('#refresh').change(function() {
if ($(this).attr('checked')) {
vz.options.refresh = true;
vz.wui.interval = window.setInterval(vz.wui.refresh, vz.options.refreshInterval);
vz.wui.timeout = window.setTimeout(vz.wui.refresh, 3000);
}
else {
vz.options.refresh = false;
window.clearInterval(vz.wui.interval);
window.clearTimeout(vz.wui.timeout);
}
});
@ -155,9 +167,12 @@ vz.wui.initEvents = function() {
*/
vz.wui.refresh = function() {
var delta = vz.options.plot.xaxis.max - vz.options.plot.xaxis.min;
vz.options.plot.xaxis.max = new Date().getTime(); // move plot
vz.options.plot.xaxis.min = vz.options.plot.xaxis.max - delta; // move plot
vz.entities.loadData();
vz.wui.timeout = window.setTimeout(vz.wui.refresh, (delta / 100 < 3000) ? 3000 : delta / 100); // TODO update timeout after zooming
};
/**
@ -272,9 +287,9 @@ vz.entities.show = function() {
event.data.active = state;
if (entity.type == 'group') {
entity.children.each(function(entity) {
$('#entity-' + entity.uuid + ' input[type=checkbox]').attr('checked', state);
entity.active = state;
entity.children.each(function(child) {
$('#entity-' + child.uuid + '.child-of-entity-' + entity.uuid + ' input[type=checkbox]').attr('checked', state);
child.active = state;
});
}
@ -312,6 +327,7 @@ vz.entities.show = function() {
.attr('alt', 'delete')
.bind('click', entity, function(event) {
vz.uuids.remove(event.data.uuid);
vz.uuids.save();
vz.entities.loadDetails();
})
);
@ -435,24 +451,26 @@ vz.load = function(context, identifier, data, success) {
/**
* Parse URL GET parameters
*/
vz.parseUrlVars = function() {
var vars = $.getUrlVars();
vz.parseUrlParams = function() {
var vars = $.getUrlParams();
for (var key in vars) {
if (vars.hasOwnProperty(key)) {
switch (key) {
case 'uuid': // add optional uuid from url
try {
vz.uuids.add(vars[key]);
} catch (exception) {
vz.wui.dialogs.exception(exception);
}
var uuids = (typeof vars[key] == 'string') ? [vars[key]] : vars[key]; // handle multiple uuids
uuids.each(function(index, uuid) {
try { vz.uuids.add(uuid); } catch (exception) { /* ignore exception */ }
});
break;
case 'from':
vz.options.plot.xaxis.min = parseInt(vars[key]);
break;
case 'to':
vz.options.plot.xaxis.max = parseInt(vars[key]);
break;
case 'debug':
$.getScript('javascripts/firebug-lite.js');
break;
@ -512,7 +530,7 @@ vz.wui.dialogs.error = function(error, description, code) {
modal: true,
buttons: {
Ok: function() {
$( this ).dialog( "close" );
$(this).dialog('close');
}
}
});

View file

@ -55,8 +55,9 @@ var Exception = function(type, message, code) {
* according to js language specification ECMA 1.6
*/
Array.prototype.indexOf = function(n) {
for (var i = 0, l = this.length; i < l; i++)
for (var i = 0, l = this.length; i < l; i++) {
if (n == this[i]) return i;
}
};
Array.prototype.remove = function(n) {
@ -64,8 +65,9 @@ Array.prototype.remove = function(n) {
};
Array.prototype.each = function(cb) {
for (var i = 0, l = this.length; i < l; i++)
for (var i = 0, l = this.length; i < l; i++) {
cb(i, this[i]);
}
};
Array.prototype.contains = function(n) {
@ -75,3 +77,13 @@ Array.prototype.contains = function(n) {
Array.prototype.clear = function() {
this.length = 0;
}
Array.prototype.unique = function () {
var r = new Array();
this.each(function(key, value) {
if (!r.contains(value)) {
r.push(value);
}
});
return r;
}

View file

@ -63,7 +63,7 @@ $(document).ready(function() {
vz.definitions.load();
vz.uuids.load();
vz.options.load();
vz.parseUrlVars();
vz.parseUrlParams();
// initialize user interface
vz.wui.init();

View file

@ -29,18 +29,28 @@
* Get URL parameters
*/
$.extend( {
getUrlVars : function() {
var vars = [], hash;
getUrlParams : function() {
var vars = {}, hash;
var hashes = window.location.href.slice(
window.location.href.indexOf('?') + 1).split('&');
for (var i = 0; i < hashes.length; i++) {
hash = hashes[i].split('=');
vars[hash[0]] = hash[1];
switch (typeof vars[hash[0]]) {
case 'undefined':
vars[hash[0]] = hash[1];
break;
case 'string':
vars[hash[0]] = Array(vars[hash[0]]);
case 'object':
vars[hash[0]].push(hash[1]);
}
}
return vars;
},
getUrlVar : function(name) {
return $.getUrlVars()[name];
getUrlParam : function(name) {
return $.getUrlParams()[name];
}
});

View file

@ -32,9 +32,8 @@ vz.options = {
rounding: 1,
render: 'lines',
refresh: false,
refreshInterval: 5*1000, // 5 secs
defaultInterval: 24*60*60*1000, // 1 day
timezoneOffset: -(new Date().getTimezoneOffset() * 60*1000) // TODO add option with timezone dropdown
timezoneOffset: -(new Date().getTimezoneOffset() * 60000) // TODO add option with timezone dropdown
};
vz.options.plot = {

View file

@ -31,7 +31,6 @@ vz.uuids.add = function(uuid) {
if (this.validate(uuid)) {
if (!this.contains(uuid)) {
this.push(uuid);
this.save();
}
else {
throw new Exception('UUIDException', 'UUID already added: ' + uuid);
@ -48,7 +47,6 @@ vz.uuids.add = function(uuid) {
vz.uuids.remove = function(uuid) {
if (this.contains(uuid)) {
this.splice(this.indexOf(uuid), 1); // remove uuid from array
this.save();
}
else {
throw new Exception('UUIDException', 'UUID unkown: ' + uuid);
@ -66,7 +64,8 @@ vz.uuids.validate = function(uuid) {
* Save uuids as cookie
*/
vz.uuids.save = function() {
$.setCookie('vz_uuids', this.join(';'));
var expires = new Date(new Date().getTime() + 31536e6); // expires in a year
$.setCookie('vz_uuids', this.join(';'), {expires: expires});
};
/**

View file

@ -37,7 +37,7 @@ class AggregatorController extends EntityController {
/**
* Get aggregator
*/
public function get($identifier) {
public function get($identifier = NULL) {
$aggregator = parent::get($identifier);
if ($aggregator instanceof Model\Aggregator) {

View file

@ -37,7 +37,7 @@ class ChannelController extends EntityController {
/**
* Get channel
*/
public function get($identifier) {
public function get($identifier = NULL) {
$channel = parent::get($identifier);
if ($channel instanceof Model\Channel) {

View file

@ -70,18 +70,9 @@ class DataController extends Controller {
public function run($operation, array $identifiers = array()) {
$ec = new EntityController($this->view, $this->em);
$entity = $ec->get($identifiers[0]);
if (count($identifiers) == 2) { // prototype: backend/data/uuid/port.json
$identifiers[0] = $ec->filter(array(
'cuuid' => $identifiers[0],
'port' => $identifiers[1]
));
}
elseif (count($identifiers) == 1) { // assume UUID
$identifiers[0] = $ec->get($identifiers[0]);
}
return parent::run($operation, $identifiers);
return $this->{$operation}($entity);
}
}

View file

@ -34,28 +34,34 @@ use Volkszaehler\Model;
* @package default
*/
class EntityController extends Controller {
/**
* Get entity
*
* @param string $identifier
*/
public function get($uuid) {
if (!Util\UUID::validate($uuid)) {
throw new \Exception('Invalid UUID: ' . $uuid);
public function get($uuid = NULL) {
if (isset($uuid)) {
if (!Util\UUID::validate($uuid)) {
throw new \Exception('Invalid UUID: ' . $uuid);
}
$dql = 'SELECT a, p
FROM Volkszaehler\Model\Entity a
LEFT JOIN a.properties p
WHERE a.uuid = :uuid';
$q = $this->em->createQuery($dql);
$q->setParameter('uuid', $uuid);
try {
return $q->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
throw new \Exception('No entity found with UUID: ' . $uuid, 404);
}
}
$dql = 'SELECT a, p
FROM Volkszaehler\Model\Entity a
LEFT JOIN a.properties p
WHERE a.uuid = ?1';
$q = $this->em->createQuery($dql);
$q->setParameter(1, $uuid);
try {
return $q->getSingleResult();
} catch (\Doctrine\ORM\NoResultException $e) {
throw new \Exception('No entity found with UUID: ' . $uuid, 404);
else { // get public entities
return $this->filter(array('public' => TRUE));
}
}
@ -82,49 +88,40 @@ class EntityController extends Controller {
/**
* Adds an entity to the uuids cookie
*
* @todo add to Model\Entity?
* @param Model\Entity $entity
*/
protected function setCookie(Model\Entity $entity) {
if ($uuids = $this->view->request->getParameter('uuids', 'cookies')) {
$uuids = Util\JSON::decode($uuids);
}
else {
$uuids = new Util\JSON();
}
$uuids = ($uuids = $this->view->request->getParameter('vz_uuids', 'cookies')) ? explode(';', $uuids) : array();
// add new UUID
$uuids->append($entity->getUuid());
// remove duplicates
$uuids->exchangeArray(array_unique($uuids->getArrayCopy()));
$uuids[] = $entity->getUuid();
// send new cookie to browser
setcookie('uuids', $uuids->encode(), 0, '/'); // TODO correct path
setcookie('vz_uuids', implode(';', array_unique($uuids)), 0, '/'); // TODO correct path
}
/**
* Removes an entity from the uuids cookie
*
* @param Model\Entity $entity
* @todo add to Model\Entity?
*/
protected function unsetCookie(Model\Entity $entity) {
if ($uuids = $this->view->request->getParameter('uuids', 'cookies')) {
$uuids = Util\JSON::decode($uuids);
}
else {
$uuids = new Util\JSON();
}
$uuids = ($uuids = $this->view->request->getParameter('vz_uuids', 'cookies')) ? explode(';', $uuids) : array();
// remove old UUID
$uuids->exchangeArray(array_filter($uuids->getArrayCopy, function($uuid) use ($entity) {
$uuids = array_filter($uuids, function($uuid) use ($entity) {
return $uuid != $entity->getUuid();
}));
});
// send new cookie to browser
setcookie('uuids', $uuids->encode(), 0, '/'); // TODO correct path
setcookie('vz_uuids', implode(';', array_unique($uuids)), 0, '/'); // TODO correct path
}
/**
* Update/set/delete properties of properties
* Update/set/delete properties of entities
*/
protected function setProperties(Model\Entity $entity, $parameters) {
foreach ($parameters as $parameter => $value) {
@ -141,14 +138,19 @@ class EntityController extends Controller {
/**
* Filter entites by properties
*
* @todo improve performance
* @param array of property => value filters
* @return array of entities
*/
public function filter(array $properties) {
$dql = 'SELECT a, p
FROM Volkszaehler\Model\Entity a
LEFT JOIN a.properties p';
$sqlWhere = array();
$i = 0;
$sqlWhere = array();
$sqlParams = array();
foreach ($properties as $property => $value) {
switch (Definition\PropertyDefinition::get($property)->getType()) {
case 'string':
@ -158,9 +160,13 @@ class EntityController extends Controller {
break;
case 'boolean':
$value = ($value) ? 1 : 0;
$value = (int) $value;
}
$sqlWhere[] = 'EXISTS (SELECT p' . $i . ' FROM \Volkszaehler\Model\Property p' . $i . ' WHERE p' . $i . '.name = \'' . $property . '\' AND p' . $i . '.value = ' . $value . ' AND p' . $i . '.entity = a)';
$sqlWhere[] = 'EXISTS (SELECT p' . $i . ' FROM \Volkszaehler\Model\Property p' . $i . ' WHERE p' . $i . '.key = :key' . $i . ' AND p' . $i . '.value = :value' . $i . ' AND p' . $i . '.entity = a)';
$sqlParams += array(
'key' . $i => $property,
'value' . $i => $value
);
$i++;
}
@ -169,7 +175,7 @@ class EntityController extends Controller {
}
$q = $this->em->createQuery($dql);
return $q->getSingleResult();
return $q->execute($sqlParams);
}
}

View file

@ -56,7 +56,7 @@ abstract class Interpreter implements InterpreterInterface {
*/
public function __construct(Model\Channel $channel, ORM\EntityManager $em, $from, $to) {
$this->channel = $channel;
// get dbal connection from EntityManager
$this->conn = $em->getConnection();

View file

@ -23,12 +23,10 @@
namespace Volkszaehler\Model;
use Volkszaehler\Definition;
use Doctrine\ORM;
use Doctrine\Common\Collections;
use Volkszaehler\Util;
use Volkszaehler\Definition;
/**
* Entity superclass for all objects referenced by a UUID
@ -124,12 +122,17 @@ abstract class Entity {
* @return array
*/
public function getProperties($prefix = NULL) {
/*$this->properties->filter(function($property) {
return substr($property->getKey(), 0, strlen($prefix)) == $prefix;
})->toArray();*/
$properties = array();
foreach ($this->properties as $property) {
if (substr($property->getKey(), 0, strlen($prefix)) == $prefix) {
$properties[$property->getKey()] = $property->getValue();
}
}
return $properties;
}
@ -175,8 +178,9 @@ abstract class Entity {
}
/*
* Setter & Getter
* Setter & getter
*/
public function getId() { return $this->id; } // read only
public function getUuid() { return $this->uuid; } // read only
public function getType() { return $this->type; } // read only

View file

@ -24,7 +24,6 @@
namespace Volkszaehler\Model;
use Volkszaehler\Definition;
use Volkszaehler\Util;
use Volkszaehler\Model;
@ -73,7 +72,6 @@ class Property {
*/
public function __construct(Model\Entity $entity, $key, $value) {
$this->entity = $entity;
$this->key = $key;
$this->value = $value;
}
@ -118,6 +116,7 @@ class Property {
/**
* @PreRemove
* @todo blocks removal of entity
*/
public function checkRemove() {
if (in_array($this->key, $this->entity->getDefinition()->getRequiredProperties())) {
@ -135,8 +134,9 @@ class Property {
}
/*
* Setter & Getter
* Setter & getter
*/
public function getKey() { return $this->key; }
public function getValue() { return $this->value; }
public function getDefinition() { return Definition\PropertyDefinition::get($this->key); }

View file

@ -137,7 +137,7 @@ class Router {
/**
* Processes the request
*
* Example: http://sub.domain.local/vz/backend/channel/550e8400-e29b-11d4-a716-446655440000/data.json?operation=edit&title=New Title
* Example: http://sub.domain.local/backend.php/channel/550e8400-e29b-11d4-a716-446655440000/data.json?operation=edit&title=New Title
*/
public function run() {
$operation = self::getOperation($this->view->request);

View file

@ -85,10 +85,10 @@ class Debug {
self::$instance->messages[] = array(
'message' => $message,
//'file' => $info['file'],
//'line' => $info['line'],
'file' => $info['file'],
'line' => $info['line'],
//'time' => date('r'),
'args' => array_slice($info['args'], 1),
'args' => array_slice($info['args'], 1)
//'trace' => array_slice($trace, 1)
);
}

@ -1 +1 @@
Subproject commit 822c9dce3a2c13de28af0080aa39e166831e19b1
Subproject commit 64a3546f9ba8a0a4070f8b6ecc182643d5539699

BIN
misc/graphics/backend.dia Normal file

Binary file not shown.

BIN
misc/graphics/backend.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

BIN
misc/graphics/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
misc/graphics/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

89
misc/graphics/favicon.svg Normal file
View file

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="280"
height="280"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="favicon.svg"
inkscape:export-filename="/home/stv0g/favicon.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4">
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective2826"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.5821429"
inkscape:cx="0.26700777"
inkscape:cy="176.44608"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="2048"
inkscape:window-height="1129"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Ebene 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-151.42857,-200.93361)">
<rect
style="fill:#3368ff;fill-opacity:1;stroke:none"
id="rect2816"
width="280"
height="280"
x="151.42857"
y="200.93361"
ry="29.074488" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 290.4375,242.75 c -36.95557,-0.68348 -75.23817,14.70066 -96.84375,45.40625 -17.14705,23.23044 -26.28879,51.79911 -27.75,80.5625 8.20833,0 16.41667,0 24.625,0 1.99005,-36.1935 18.54527,-74.63265 52,-92 27.9397,-14.23833 62.37048,-14.48911 91.3125,-3.6875 27.59623,11.24907 46.45641,37.74868 53.71875,66.03125 2.83083,9.9557 4.33419,20.20713 4.8125,30.5625 8.25,0 16.5,0 24.75,0 -2.07392,-40.73674 -19.6734,-82.8808 -53.75,-106.5625 -21.28456,-14.36211 -47.2826,-20.77635 -72.875,-20.3125 z"
id="path2832" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 338.5,308.125 c -29.65605,39.0314 -59.31369,78.0616 -88.96875,117.09375 8.91331,6.0521 17.79771,12.14684 26.71875,18.1875 26.20253,-42.13644 52.34184,-84.31248 78.5625,-126.4375 -5.04701,-3.45533 -10.10161,-6.89959 -15.15625,-10.34375 l -0.61051,0.79201 L 338.5,308.125 z"
id="path2834" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

1
misc/website Submodule

@ -0,0 +1 @@
Subproject commit d542e6a6817f90f01bffb50f481c308d9d0753da