diff --git a/web/drupal/modules/logger/README.txt b/web/drupal/modules/logger/README.txt
new file mode 100644
index 0000000..e3577a1
--- /dev/null
+++ b/web/drupal/modules/logger/README.txt
@@ -0,0 +1,11 @@
+// $Id$
+
+INSTALLATION
+------------
+1. install this module in your contributed modules directory
+2. add a symlink in the module's root dir to the rrdtool executable
+ e.g. ln -s /usr/bin/rrdtool rrdtool
+3. add following directory structure in this module's root dir
+ mkdir -p data/base data/night graphs/hour graphs/day graphs/week graphs/month graphs/year graphs/night
+
+enjoy!
diff --git a/web/drupal/modules/logger/logger.info b/web/drupal/modules/logger/logger.info
new file mode 100644
index 0000000..fcc8b00
--- /dev/null
+++ b/web/drupal/modules/logger/logger.info
@@ -0,0 +1,8 @@
+; $Id$
+name = Logger
+description = Logs metering values reported through XML-RPC in an RRD database and renders them in different charts.
+package = Metering
+core = 6.x
+version = 6.x-0.2
+dependencies[] = autotimezone
+
diff --git a/web/drupal/modules/logger/logger.install b/web/drupal/modules/logger/logger.install
new file mode 100644
index 0000000..3192a85
--- /dev/null
+++ b/web/drupal/modules/logger/logger.install
@@ -0,0 +1,238 @@
+ t("Contains the Fluksometer device ID's."),
+ 'fields' => array(
+ 'device' => array(
+ 'type' => 'varchar',
+ 'length' => '32',
+ 'not null' => TRUE,
+ ),
+ 'uid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'sha' => array(
+ 'type' => 'varchar',
+ 'length' => '32',
+ 'not null' => FALSE,
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'access' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'version' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'upgrade' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'resets' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'uptime' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'memtotal' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'memfree' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'memcached' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'membuffers' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ ),
+ 'uart_oe' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '5',
+ )
+ ),
+ 'primary key' => array('device'),
+ );
+
+ $schema['logger_meters'] = array(
+ 'description' => t("Contains the Fluksometer meter ID's."),
+ 'fields' => array(
+ 'meter' => array(
+ 'type' => 'varchar',
+ 'length' => '32',
+ 'not null' => TRUE,
+ ),
+ 'uid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'device' => array(
+ 'type' => 'varchar',
+ 'length' => '32',
+ 'not null' => TRUE,
+ 'default' => '0',
+ ),
+ 'created' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'access' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'night' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'type' => array(
+ 'type' => 'varchar',
+ 'length' => '16',
+ 'not null' => TRUE,
+ ),
+ 'function' => array(
+ 'type' => 'varchar',
+ 'length' => '16',
+ 'not null' => TRUE,
+ ),
+ 'value' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'disp-width' => '10',
+ ),
+ 'factor' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 1,
+ 'disp-width' => '10',
+ ),
+ 'unit' => array(
+ 'type' => 'varchar',
+ 'length' => '16',
+ 'not null' => TRUE,
+ ),
+ ),
+ 'primary key' => array('meter'),
+ );
+
+ return $schema;
+}
+
+function logger_install() {
+ drupal_install_schema('logger');
+ drupal_set_message(t('Created logger module tables {logger_devices} and {logger_meters}.'));
+}
+
+function logger_uninstall() {
+ drupal_uninstall_schema('logger');
+ drupal_set_message(t('Deleted logger module tables {logger_devices} and {logger_meters}.'));
+}
+
+/**
+ * R5.x update
+ */
+function logger_update_1() {
+ $items = array();
+ switch ($GLOBALS['db_type']) {
+ case 'mysql':
+ case 'mysqli':
+ $items[] = update_sql("ALTER TABLE {logger_meters} ADD COLUMN night int unsigned NOT NULL default '0' AFTER access");
+ break;
+ case 'pgsql':
+ break;
+ }
+ return $items;
+}
+
+/**
+ * update to run with logger module's 6.x code
+ */
+function logger_update_6000() {
+ $items = array();
+ switch ($GLOBALS['db_type']) {
+ case 'mysql':
+ case 'mysqli':
+ $items[] = update_sql("ALTER TABLE {logger_devices} ADD COLUMN sha varchar(32) AFTER uid");
+ $items[] = update_sql("ALTER TABLE {logger_devices} ADD COLUMN memtotal smallint unsigned NOT NULL default '0'");
+ $items[] = update_sql("ALTER TABLE {logger_devices} ADD COLUMN memfree smallint unsigned NOT NULL default '0'");
+ $items[] = update_sql("ALTER TABLE {logger_devices} ADD COLUMN memcached smallint unsigned NOT NULL default '0'");
+ $items[] = update_sql("ALTER TABLE {logger_devices} ADD COLUMN membuffers smallint unsigned NOT NULL default '0'");
+ $items[] = update_sql("ALTER TABLE {logger_devices} ADD COLUMN uart_oe smallint unsigned NOT NULL default '0'");
+ break;
+ case 'pgsql':
+ break;
+ }
+ return $items;
+}
diff --git a/web/drupal/modules/logger/logger.module b/web/drupal/modules/logger/logger.module
new file mode 100644
index 0000000..b4e8e7b
--- /dev/null
+++ b/web/drupal/modules/logger/logger.module
@@ -0,0 +1,448 @@
+ 'Logger settings',
+ 'description' => 'Configure settings for logging metering values.',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('_logger_admin_settings'),
+ 'access arguments' => array('administer site configuration'),
+ );
+ $items['logger'] = array(
+ 'title' => 'your dashboard', // isn't printed as title on the page, therefore resort to drupal_set_title (t('your ecological dashboard')) in ecology_dashboard;
+ 'description' => 'Configure settings for logging metering values.',
+ 'page callback' => '_logger_dashboard', //takes the callback from the MENU_DEFAULT_LOCAL_TASK -> lightest level-two menu
+ 'page arguments' => array('electricity', 'main', 'hour'),
+ 'access callback' => TRUE,
+ 'type' => MENU_CALLBACK,
+ );
+ $items['logger/add'] = array(
+ 'title' => 'add this user to the chart',
+ 'page callback' => '_logger_add',
+ 'access arguments' => array('logger'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['logger/remove'] = array(
+ 'title' => 'remove this user from the chart',
+ 'page callback' => '_logger_remove',
+ 'access arguments' => array('logger'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['logger/unit'] = array(
+ 'title' => 'change the unit',
+ 'page callback' => '_logger_unit',
+ 'access arguments' => array('logger'),
+ 'type' => MENU_CALLBACK,
+ );
+ $items['logger/electricity'] = array(
+ 'title' => 'electricity',
+// 'page callback' => '_logger_dashboard',
+// 'page arguments' => array('electricity', 'main', 'hour'),
+ 'access callback' => TRUE,
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+
+/**
+ $items['logger/water'] = array(
+ 'title' => 'water',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('water', 'main', 'hour'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ );
+ $items['logger/gas'] = array(
+ 'title' => 'gas',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('gas', 'main', 'hour'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ );
+**/
+
+ $items['logger/electricity/hour'] = array(
+ 'title' => 'hour',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('electricity', 'main', 'hour'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 0,
+ );
+ $items['logger/electricity/day'] = array(
+ 'title' => 'day',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('electricity', 'main', 'day'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 1,
+ );
+ $items['logger/electricity/month'] = array(
+ 'title' => 'month',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('electricity', 'main', 'month'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 2,
+ );
+ $items['logger/electricity/year'] = array(
+ 'title' => 'year',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('electricity', 'main', 'year'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 3,
+ );
+ $items['logger/electricity/night'] = array(
+ 'title' => 'night',
+ 'page callback' => '_logger_dashboard',
+ 'page arguments' => array('electricity', 'main', 'night'),
+ 'access callback' => TRUE,
+ 'type' => MENU_LOCAL_TASK,
+ 'weight' => 4,
+ );
+ return $items;
+}
+
+/**
+ * Callback functions registered in the logger_menu section
+ */
+function _logger_dashboard($type, $function, $interval) {
+ watchdog('dashboard', 'arguments: %type, %function, %interval', array('%type' => $type, '%function' => $function, '%interval' => $interval), WATCHDOG_DEBUG);
+
+ if (user_access('logger')) {
+ drupal_set_title(t('your dashboard'));
+ global $user;
+ }
+ else { //show users who don't have 'logger' permissions icrarus'es chart
+ drupal_set_title(t("a Fluksonian's dashboard"));
+ $user = new stdClass();
+ $user->uid = 1;
+ $user->name = 'icarus75';
+ $user->timezone = '3600';
+ }
+
+ $root_path = drupal_get_path('module', 'logger');
+ $graph_path = $root_path .'/graphs/'. $interval .'/';
+ $pngid = md5(uniqid()); //generate random numbers for the png chart so that the browser doesn't use the cached one, use cron to clean up the dir hourly
+ switch ($interval) {
+ case 'hour':
+ $data_path = $root_path .'/data/base/';
+ $start = 'end-1h';
+ break;
+ case 'day':
+ $data_path = $root_path .'/data/base/';
+ $start = 'end-1d';
+ break;
+ case 'month':
+ $data_path = $root_path .'/data/base/';
+ $start = 'end-60d';
+ break;
+ case 'year':
+ $data_path = $root_path .'/data/base/';
+ $start = 'end-1y';
+ break;
+ case 'night':
+ $data_path = $root_path .'/data/night/';
+ $start = 'end-60d';
+ break;
+ }
+
+ $meter = db_fetch_object(db_query("SELECT meter, unit FROM {logger_meters} WHERE uid = %d AND type = '%s' AND function = '%s'", $user->uid, $type, $function));
+ switch ($type) {
+ case 'electricity':
+ switch ($meter->unit) {
+ case 'watt':
+ $meter->factor = 3600; // 1Wh/s = 3600 W
+ break;
+ case 'kwh':
+ $meter->unit = 'kWh/year';
+ $meter->factor = 31536;
+ break;
+ case 'eur':
+ $meter->unit = 'euro/year';
+ $meter->factor = 5361.12;
+ break;
+ }
+ }
+
+ $color = array(RED, BLUE, GREEN, YELLOW, PURPLE);
+ $string->def = ' DEF:data0='. $data_path . $meter->meter .'.rrd:meter:AVERAGE CDEF:meter0=data0,'. $meter->factor .',* VDEF:min0=meter0,MINIMUM VDEF:max0=meter0,MAXIMUM VDEF:avg0=meter0,AVERAGE VDEF:last0=meter0,LAST';
+ $string->line = ' COMMENT:"\s" LINE1:meter0#'. $color[0] .':'.'"'. substr($user->name.' ', 0, 15) .'"'.' GPRINT:min0:"min\:%5.0lf" GPRINT:max0:"\tmax\:%5.0lf" GPRINT:avg0:"\tavg\:%5.0lf" GPRINT:last0:"\tlast\:%5.0lf\l"';
+
+ if (user_access('logger') || user_access('staff')) { //allow Veerle to watch the graphs
+ $result = db_query("SELECT u.name, lm.meter FROM (({users} u INNER JOIN {user_relationships} ur ON u.uid = ur.requestee_id) INNER JOIN {user_relationship_types} urt ON ur.rtid = urt.rtid) INNER JOIN {logger_meters} lm ON u.uid = lm.uid WHERE ur.requester_id = %d AND urt.name = '%s' AND type = '%s' AND function = '%s' ORDER BY ur.rid", $user->uid, 'subscription', $type, $function);
+ $i = 0;
+ while ($subscription = db_fetch_object($result)) {
+ $i += 1;
+ // print_r($subscription);
+ $string->def .= ' DEF:data'. $i .'='. $data_path . $subscription->meter .'.rrd:meter:AVERAGE CDEF:meter'. $i .'=data'. $i .','. $meter->factor .',* VDEF:min'. $i .'=meter'. $i .',MINIMUM VDEF:max'. $i .'=meter'. $i .',MAXIMUM VDEF:avg'. $i .'=meter'. $i .',AVERAGE VDEF:last'. $i .'=meter'. $i .',LAST';
+ $string->line .= ' LINE1:meter'. $i .'#'. $color[$i] .':'.'"'. substr($subscription->name.' ', 0, 15) .'"'.' GPRINT:min'. $i .':"min\:%5.0lf" GPRINT:max'. $i .':"\tmax\:%5.0lf" GPRINT:avg'. $i .':"\tavg\:%5.0lf" GPRINT:last'. $i .':"\tlast\:%5.0lf\l"';
+ }
+ }
+
+ //construct the TZ=GMT-02:00 format from the $user->timezone object updated by the autotimezone module
+ if ($user->timezone >= 0)
+ $TZ = 'TZ="GMT-';
+ else
+ $TZ = 'TZ="GMT+';
+ $TZ .= gmdate('h:i', abs($user->timezone)) .'" ';
+ //insert the TZ prior to launching rrdtool to obtain a proper time conversion
+ $command = $TZ . $root_path .'/rrdtool graph '. $graph_path . $pngid .'.png -s '. $start .' --vertical-label '. $meter->unit .' --lower-limit 0 -w 500 -h 350 -E -X 0 --font LEGEND:8:';
+ $command .= $string->def;
+ $command .= $string->line;
+ exec($command, $output, $return_var);
+ watchdog('dashboard', 'arguments: %command ++ %output ++ %return_var', array('%command' => $command, '%output' => serialize($output), '%return_var' => $return_var), WATCHDOG_DEBUG);
+ return theme('chart', $graph_path . $pngid .'.png');
+}
+
+function _logger_add($uid) {
+ // TODO : include security checks
+ global $user;
+ $rtid = db_result(db_query("SELECT rtid FROM {user_relationship_types} where name = '%s'", 'subscription'));
+ user_relationships_request_relationship($user->uid, $uid, $rtid, TRUE);
+ $destination = drupal_get_destination();
+ drupal_goto($destination);
+}
+
+function _logger_remove($rid) {
+ // TODO : include security checks
+ db_query("DELETE FROM {user_relationships} WHERE rid = %d", $rid);
+ $destination = drupal_get_destination();
+ drupal_goto($destination);
+}
+
+function _logger_unit($unit) {
+ // TODO : include security checks
+ global $user;
+ // hardcoded type and function
+ db_query("UPDATE {logger_meters} SET unit = '%s' WHERE uid = %d AND type = '%s' AND function = '%s'", $unit, $user->uid, 'electricity', 'main');
+ $destination = drupal_get_destination();
+ drupal_goto($destination);
+}
+
+/**
+ * Implementation of hook_theme() for logger
+ */
+function logger_theme() {
+ return array(
+ 'chart' => array(
+ 'arguments' => array('chart' => NULL),
+ ),
+ 'logger_item_list' => array(
+ 'arguments' => array('items' => NULL, 'title' => NULL),
+ ),
+ );
+}
+
+/**
+ * Theming the chart
+ */
+function theme_chart($chart) {
+ $output .= '
';
+ return $output;
+}
+
+/**
+ * Implementation of hook_block() for logger
+ * Adds two blocks to the logger pages for (de-)selecting users and
+ * another one for selecting the desired unit
+ */
+function logger_block($op = 'list', $delta = 0, $edit = array()) {
+ global $user;
+
+ switch ($op) {
+ case 'list':
+ $blocks['subscriptions']['info'] = t('Subscriptions');
+ $blocks['subscriptions']['status'] = TRUE;
+ $blocks['subscriptions']['region'] = 'right';
+ $blocks['subscriptions']['weight'] = 0;
+ $blocks['subscriptions']['pages'] = 'logger
logger/*';
+
+ $blocks['fluksonians']['info'] = t('Fluksonians');
+ $blocks['fluksonians']['status'] = TRUE;
+ $blocks['fluksonians']['region'] = 'right';
+ $blocks['fluksonians']['weight'] = 1;
+ $blocks['fluksonians']['pages'] = 'logger
logger/*';
+
+ $blocks['unit']['info'] = t('Unit');
+ $blocks['unit']['status'] = TRUE;
+ $blocks['unit']['region'] = 'right';
+ $blocks['unit']['weight'] = 2;
+ $blocks['unit']['pages'] = 'logger
logger/*';
+
+ $blocks['publish']['info'] = t('Publish');
+ $blocks['publish']['status'] = TRUE;
+ $blocks['publish']['region'] = 'content';
+ $blocks['publish']['weight'] = 3;
+ $blocks['publish']['pages'] = 'logger
logger/*';
+
+ return $blocks;
+
+ case 'view':
+ //pass along our current destination in the query string so that logger_add and logger_remove can return after processing their task
+ $destination = drupal_get_destination();
+
+ if ($delta == 'subscriptions' && user_access('logger')) {
+ $result = db_query("SELECT u.uid, u.name, ur.rid FROM ({users} u INNER JOIN {user_relationships} ur ON u.uid = ur.requestee_id) INNER JOIN {user_relationship_types} urt ON ur.rtid = urt.rtid WHERE ur.requester_id = %d AND urt.name = '%s' ORDER BY ur.rid", $user->uid, 'subscription');
+ $items = array();
+ while ($subscription = db_fetch_object($result)) {
+ $items[] = l('[x]', 'logger/remove/'. $subscription->rid, array('attributes' => array('title' => "unsubscribe from ". $subscription->name ."'s stream"), 'query' => $destination)) .' '. l($subscription->name, 'user/'. $subscription->uid, array());
+ }
+ $block['subject'] = t('Subscriptions');
+ $block['content'] = theme('logger_item_list', $items);
+ }
+
+ elseif ($delta == 'fluksonians' && user_access('logger')) {
+ // list all users having the fluksionian role for now
+ // to be replaced by a real buddylist later on
+ $result = db_query("SELECT u.uid, u.name FROM ({users} u INNER JOIN {users_roles} ur ON u.uid = ur.uid) INNER JOIN {role} r ON ur.rid = r.rid WHERE r.name = '%s' AND NOT u.uid = %d ORDER BY u.name", 'fluksonian', $user->uid);
+ $items = array();
+ while ($fluksonian = db_fetch_object($result)) {
+ $items[] = l('[+]', 'logger/add/'. $fluksonian->uid, array('attributes' => array('title' => "subscribe to ". $fluksonian->name ."'s stream"), 'query' => $destination)) .' '. l($fluksonian->name, 'user/'. $fluksonian->uid, array());
+ }
+ $block['subject'] = t('Fluksonians');
+ $block['content'] = theme('logger_item_list', $items);
+ }
+
+ elseif ($delta == 'unit' && user_access('logger')) {
+ //hardcoded the type and function parameters for now
+ $unit = db_result(db_query("SELECT unit FROM {logger_meters} WHERE uid = %d AND type = '%s' AND function = '%s'", $user->uid, 'electricity', 'main'));
+ $items = array();
+ switch ($unit) {
+ case 'watt':
+ $items[] = 'watt';
+ $items[] = l('kWh/year', 'logger/unit/kwh', array('attributes' => array('title' => "switch to kWh/year"), 'query' => $destination));
+ $items[] = l('euro/year', 'logger/unit/eur', array('attributes' => array('title' => "switch to euro/year"), 'query' => $destination));
+ break;
+ case 'kwh':
+ $items[] = l('watt', 'logger/unit/watt', array('attributes' => array('title' => "switch to watt"), 'query' => $destination));
+ $items[] = 'kWh/year';
+ $items[] = l('euro/year', 'logger/unit/eur', array('attributes' => array('title' => "switch to euro/year"), 'query' => $destination));
+ break;
+ case 'eur':
+ $items[] = l('watt', 'logger/unit/watt', array('attributes' => array('title' => "switch to watt"), 'query' => $destination));
+ $items[] = l('kWh/year', 'logger/unit/kwh', array('attributes' => array('title' => "switch to kWh/year"), 'query' => $destination));
+ $items[] = 'euro/year';
+ break;
+ }
+ $block['subject'] = t('Unit');
+ $block['content'] = theme('logger_item_list', $items);
+ }
+
+ elseif ($delta == 'publish' && user_access('logger')) {
+ // $block['subject'] = t('Publish');
+ $block['content'] = drupal_get_form('_logger_publish_form');
+ }
+
+ return $block;
+ }
+}
+
+/**
+ * Implementing a simple non-bulleted list for the logger_block
+ */
+function theme_logger_item_list($items, $title = NULL) {
+ $output = '';
+ foreach ($items as $item) {
+ $output .= $item .'
';
+ }
+ return $output;
+}
+
+/**
+ * Generates the publish block form.
+ */
+function _logger_publish_form() {
+ $form['publish'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Publish'),
+ '#description' => t('Publish the chart.'),
+ '#collapsible' => TRUE,
+ '#collapsed' => TRUE
+ );
+ $form['publish']['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Title'),
+ '#description' => t('Please enter the title of your post.')
+ );
+ $form['publish']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Publish')
+ );
+ return $form;
+}
+
+/**
+ * Process publish form submissions.
+ */
+function _logger_publish_form_submit($form, &$form_state) {
+ $form_state['redirect'] = 'node/add'; //placeholder; check whether we can automatically fill in the new content type
+}
+
+/**
+ * Define the administration settings form for the logger module
+ */
+function _logger_admin_settings() {
+//TODO
+}
+
+/**
+ * Implementation of hook_cron().
+ * Cron will call this hook periodically [e.g. 1 hour interval] to perform housekeeping on the png's.
+ */
+function logger_cron() {
+ exec('rm sites/all/modules/custom/logger/graphs/hour/*');
+ exec('rm sites/all/modules/custom/logger/graphs/day/*');
+ exec('rm sites/all/modules/custom/logger/graphs/month/*');
+ exec('rm sites/all/modules/custom/logger/graphs/year/*');
+ exec('rm sites/all/modules/custom/logger/graphs/night/*');
+}
diff --git a/web/drupal/modules/logger/patches/flukso.core.patch b/web/drupal/modules/logger/patches/flukso.core.patch
new file mode 100644
index 0000000..f53a9bc
--- /dev/null
+++ b/web/drupal/modules/logger/patches/flukso.core.patch
@@ -0,0 +1,126 @@
+Index: .htaccess
+===================================================================
+RCS file: /cvs/drupal/drupal/.htaccess,v
+retrieving revision 1.90.2.3
+diff -u -p -r1.90.2.3 .htaccess
+--- .htaccess 10 Dec 2008 20:04:08 -0000 1.90.2.3
++++ .htaccess 3 Aug 2009 22:38:00 -0000
+@@ -100,10 +100,13 @@ DirectoryIndex index.php
+ # uncomment the following line:
+ # RewriteBase /
+
++ RewriteRule ^xmlrpc/([0-9]+)/?$ xmlrpc.php?version=$1 [L]
++
+ # Rewrite URLs of the form 'x' to the form 'index.php?q=x'.
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_URI} !=/favicon.ico
++ RewriteCond %{REQUEST_URI} !=/xmlrpc(.*)
+ RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]
+
+
+Index: xmlrpc.php
+===================================================================
+RCS file: /cvs/drupal/drupal/xmlrpc.php,v
+retrieving revision 1.15
+diff -u -p -r1.15 xmlrpc.php
+--- xmlrpc.php 10 Dec 2005 19:26:47 -0000 1.15
++++ xmlrpc.php 3 Aug 2009 22:38:00 -0000
+@@ -6,9 +6,77 @@
+ * PHP page for handling incoming XML-RPC requests from clients.
+ */
+
++// define xmlrpc method location
++define('XMLRPC_PATH', 'sites/all/modules');
++define('XMLRPC_MODULE', 'logger');
++define('XMLRPC_FILE', 'xmlrpc');
++
++// defined xmlrpc endpoints
++$xmlrpc_versions = array('', 1);
++
++// any common.inc or other core functions that xmlrpc processing relies upon
++function t($string, $args = array(), $langcode = NULL) {
++ if (empty($args)) {
++ return $string;
++ }
++ else {
++ // Transform arguments before inserting them.
++ foreach ($args as $key => $value) {
++ switch ($key[0]) {
++ case '@':
++ case '%':
++ default:
++ // Escaped only.
++ $args[$key] = check_plain($value);
++ break;
++
++ case '!':
++ // Pass-through.
++ }
++ }
++ return strtr($string, $args);
++ }
++}
++
++function watchdog_xmlrpc($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
++ global $base_root;
++
++ $current_db = db_set_active();
++
++ db_query("INSERT INTO {watchdog}
++ (type, message, variables, severity, link, location, referer, hostname, timestamp)
++ VALUES
++ ('%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', %d)",
++ $type,
++ $message,
++ serialize($variables),
++ $severity,
++ $link,
++ $base_root . request_uri(),
++ referer_uri(),
++ ip_address(),
++ time());
++
++ if ($current_db) {
++ db_set_active($current_db);
++ }
++}
++
++
+ include_once './includes/bootstrap.inc';
+-drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
++drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
+ include_once './includes/xmlrpc.inc';
+ include_once './includes/xmlrpcs.inc';
+
+-xmlrpc_server(module_invoke_all('xmlrpc'));
++if (in_array($_REQUEST['version'], $xmlrpc_versions)) {
++ require_once XMLRPC_PATH . '/' . XMLRPC_MODULE . '/' . XMLRPC_FILE . $_REQUEST['version'] . '.inc';
++
++ //debugging watchdog_xmlrpc('xmlrpc', 'xmlrpc api called with version %version', array('%version' => $_REQUEST['version']), WATCHDOG_DEBUG);
++
++ $function = XMLRPC_MODULE . '_xmlrpc';
++ $callbacks = $function();
++ xmlrpc_server($callbacks);
++}
++else {
++ xmlrpc_server_error(-32601, t('Server error. Requested method version (@version) not specified.', array("@version" => $_REQUEST['version'])));
++}
+Index: includes/menu.inc
+===================================================================
+RCS file: /cvs/drupal/drupal/includes/menu.inc,v
+retrieving revision 1.255.2.31
+diff -u -p -r1.255.2.31 menu.inc
+--- includes/menu.inc 27 Apr 2009 12:50:13 -0000 1.255.2.31
++++ includes/menu.inc 3 Aug 2009 22:38:02 -0000
+@@ -1422,8 +1422,8 @@ function menu_local_tasks($level = 0, $r
+ return $root_path;
+ }
+ else {
+- // We do not display single tabs.
+- return (isset($tabs[$level]) && $tabs[$level]['count'] > 1) ? $tabs[$level]['output'] : '';
++ // We do not display single tabs: BVDM yes we do for flukso!
++ return (isset($tabs[$level])) ? $tabs[$level]['output'] : '';
+ }
+ }
+
diff --git a/web/drupal/modules/logger/patches/menu.allow.display.of.single.tabs.patch b/web/drupal/modules/logger/patches/menu.allow.display.of.single.tabs.patch
new file mode 100644
index 0000000..d29f950
--- /dev/null
+++ b/web/drupal/modules/logger/patches/menu.allow.display.of.single.tabs.patch
@@ -0,0 +1,19 @@
+? menu.allow.display.of.single.tabs.patch
+Index: menu.inc
+===================================================================
+RCS file: /cvs/drupal/drupal/includes/menu.inc,v
+retrieving revision 1.255.2.31
+diff -u -p -r1.255.2.31 menu.inc
+--- menu.inc 27 Apr 2009 12:50:13 -0000 1.255.2.31
++++ menu.inc 3 Aug 2009 20:19:06 -0000
+@@ -1422,8 +1422,8 @@ function menu_local_tasks($level = 0, $r
+ return $root_path;
+ }
+ else {
+- // We do not display single tabs.
+- return (isset($tabs[$level]) && $tabs[$level]['count'] > 1) ? $tabs[$level]['output'] : '';
++ // We do not display single tabs: BVDM yes we do for flukso!
++ return (isset($tabs[$level])) ? $tabs[$level]['output'] : '';
+ }
+ }
+
diff --git a/web/drupal/modules/logger/xmlrpc.inc b/web/drupal/modules/logger/xmlrpc.inc
new file mode 100644
index 0000000..81e708e
--- /dev/null
+++ b/web/drupal/modules/logger/xmlrpc.inc
@@ -0,0 +1,104 @@
+resets += $resets;
+ db_query("UPDATE {logger_devices} SET access = %d, version = %d, upgrade = %d, resets = %d, uptime = %d WHERE device = '%s'", time(), $version, 0, $dev->resets, $uptime, $device);
+ return $dev->upgrade;
+}
+
+function _logger_measurement_add($logs) {
+ $info = 'added 5min interval measurements to the log';
+ $path = new stdClass();
+ $path->root = XMLRPC_PATH . '/' . XMLRPC_MODULE; // need to hardcode drupal_get_path('module', 'logger');
+ $path->base = $path->root .'/data/base/';
+ $path->night = $path->root .'/data/night/';
+ foreach ($logs as $meter => $measurements) {
+ //load the normalisation factor, relative to 1pulse = 1Wh
+ $meterdata = db_fetch_object(db_query("SELECT night, factor FROM {logger_meters} WHERE meter = '%s'", $meter));
+ $command = $path->root .'/rrdtool update '. $path->base . $meter .'.rrd ';
+ ksort($measurements); // sort the key-value pairs in the associative array by key, i.e. the timestamp
+ foreach ($measurements as $timestamp => $value) {
+ if (is_numeric($timestamp) and is_numeric($value)) {
+ $command .= $timestamp .':'. $value*$meterdata->factor .' ';
+ }
+ else {
+ watchdog_xmlrpc('logger.measurementAdd', 'corrupted input data for %meter : %timestamp : %value', array('%meter' => $meter, '%timestamp' => $timestamp, '%value' => $value), WATCHDOG_ERROR);
+ }
+ }
+ system($command, $return);
+ if ($return == 0) {
+ // update the night rrd every day at 6AM UTC
+ if (time() > $meterdata->night) {
+ $timestamp = floor(time()/86400)*86400;
+ $start = $timestamp + 3600;
+ $end = $start + 10800; //3h time interval
+ $command = $path->root ."/rrdtool fetch ". $path->base . $meter .".rrd AVERAGE -r 900 -s ". $start ." -e ". $end ." | tail -n 12 | awk -F': ' '{SUM += $2} END {print SUM/12}'";
+ $night = (float)shell_exec($command); //test shell_exec iso system
+ $command = $path->root .'/rrdtool update '. $path->night . $meter .'.rrd '. $timestamp .':'. $night;
+ system($command, $return);
+ if ($return == 0) {
+ watchdog_xmlrpc('logger.measurementAdd', 'successful update for night rrd: %command', array('%command' => $command), WATCHDOG_NOTICE); //debugging
+ }
+ else {
+ watchdog_xmlrpc('logger.measurementAdd', 'error updating night rrd: %command', array('%command' => $command), WATCHDOG_ERROR); //debugging
+ }
+ $meterdata->night = $timestamp + 104400; //add an offset of 29h, i.e. 5AM UTC next day
+ }
+ // {logger_meters} is updated with the true metervalue $value, NOT $value*$meterdata->factor since we're not normalising this entry!
+ db_query("UPDATE {logger_meters} SET access = %d, night = %d, value = %d WHERE meter = '%s'", time(), $meterdata->night, $value, $meter);
+ }
+ else {
+ watchdog_xmlrpc('logger.measurementAdd', 'shell command execution failed: %return %command', array('%command' => $command, '%return' => $return), WATCHDOG_ERROR);
+ }
+ }
+ return $command; //using $command for testing purposes, replace by $info afterwards
+}
diff --git a/web/drupal/modules/logger/xmlrpc1.inc b/web/drupal/modules/logger/xmlrpc1.inc
new file mode 100644
index 0000000..b4f290e
--- /dev/null
+++ b/web/drupal/modules/logger/xmlrpc1.inc
@@ -0,0 +1,160 @@
+resets += $monitor['reset'];
+ db_query("UPDATE {logger_devices} SET access = %d, version = %d, upgrade = %d, resets = %d, uptime = %d, memtotal = %d, memfree = %d, memcached = %d, membuffers = %d, uart_oe = %d WHERE device = '%s'", time(), $monitor['version'], 0, $device->resets, $monitor['uptime'], $monitor['memtotal'], $monitor['memfree'], $monitor['memcached'], $monitor['membuffers'], $monitor['uart_oe'], $auth['device']);
+
+ $action['upgrade'] = (int)$device->upgrade;
+ if ($action['upgrade']) {
+ $action['timestamp'] = time();
+ $action['signature'] = hash_hmac('sha1', $action['timestamp'] .':'. $action['upgrade'] .':'. $device->sha, $device->sha);
+ }
+ return $action;
+ }
+ else
+ return xmlrpc_error(-31000, t('Authentication failed.'));
+}
+
+function _logger_measurement_add($auth, $logs) {
+ if (_logger_authenticate_hmac_sha1($auth, $logs)) {
+ $info = 'added 5min interval measurements to the log';
+ $path = new stdClass();
+ $path->root = XMLRPC_PATH . '/' . XMLRPC_MODULE; // need to hardcode drupal_get_path('module', 'logger');
+ $path->base = $path->root .'/data/base/';
+ $path->night = $path->root .'/data/night/';
+ foreach ($logs as $meter => $measurements) {
+ //load the normalisation factor, relative to 1pulse = 1Wh
+ $meterdata = db_fetch_object(db_query("SELECT device, night, factor FROM {logger_meters} WHERE meter = '%s'", $meter));
+ if ($meterdata->device == $auth['device']) { // extra security check
+ $command = $path->root .'/rrdtool update '. $path->base . $meter .'.rrd ';
+ ksort($measurements); // sort the key-value pairs in the associative array by key, i.e. the timestamp
+ foreach ($measurements as $timestamp => $value) {
+ if (is_numeric($timestamp) and is_numeric($value)) {
+ $command .= $timestamp .':'. $value*$meterdata->factor .' ';
+ }
+ else {
+ watchdog_xmlrpc('logger.measurementAdd', 'corrupted input data for %meter : %timestamp : %value', array('%meter' => $meter, '%timestamp' => $timestamp, '%value' => $value),WATCHDOG_ERROR);
+ }
+ }
+
+ system($command, $return);
+
+ if ($return == 0) {
+ // update the night rrd every day at 6AM UTC
+ if (time() > $meterdata->night) {
+ $timestamp = floor(time()/86400)*86400;
+ $start = $timestamp + 3600;
+ $end = $start + 10800; //3h time interval
+ $command = $path->root ."/rrdtool fetch ". $path->base . $meter .".rrd AVERAGE -r 900 -s ". $start ." -e ". $end ." | tail -n 12 | awk -F': ' '{SUM += $2} END {print SUM/12}'";
+ $night = (float)shell_exec($command); //test shell_exec iso system
+ $command = $path->root .'/rrdtool update '. $path->night . $meter .'.rrd '. $timestamp .':'. $night;
+ system($command, $return);
+ if ($return == 0) {
+ watchdog_xmlrpc('logger.measurementAdd', 'successful update for night rrd: %command', array('%command' => $command), WATCHDOG_NOTICE); //debugging
+ }
+ else {
+ watchdog_xmlrpc('logger.measurementAdd', 'error updating night rrd: %command', array('%command' => $command), WATCHDOG_ERROR); //debugging
+ }
+ $meterdata->night = $timestamp + 104400; //add an offset of 29h, i.e. 5AM UTC next day
+ }
+ // {logger_meters} is updated with the true metervalue $value, NOT $value*$meterdata->factor since we're not normalising this entry!
+ db_query("UPDATE {logger_meters} SET access = %d, night = %d, value = %d WHERE meter = '%s'", time(), $meterdata->night, $value, $meter);
+ }
+ else {
+ watchdog_xmlrpc('logger.measurementAdd', 'shell command execution failed: %return %command', array('%command' => $command, '%return' => $return), WATCHDOG_ERROR);
+ }
+ }
+ }
+ return $command; //using $command for testing purposes, replace by $info afterwards
+ }
+ else
+ return xmlrpc_error(-31000, t('Authentication failed.'));
+}
+
+function _logger_authenticate_hmac_sha1($auth, $message) {
+ $auth['key'] = db_result(db_query("SELECT sha FROM {logger_devices} WHERE device = '%s'", $auth['device']));
+ if (hash_hmac('sha1', $auth['timestamp'] .':'. _logger_serialise($message) .':'. $auth['key'], $auth['key']) == $auth['signature'] && $auth['timestamp'] > time() - 300) {
+ // debugging: watchdog_xmlrpc('logger.auth', 'HMAC-SHA1 authentication succeeded for device: %device', array('%device' => $auth['device']), WATCHDOG_NOTICE);
+ return TRUE;
+ }
+ else {
+ watchdog_xmlrpc('logger.auth', 'HMAC-SHA1 authentication failed for device: %device', array('%device' => $auth['device']), WATCHDOG_ERROR);
+ return FALSE;
+ }
+}
+
+function _logger_serialise($data) {
+ if (is_array($data)) {
+ $sequence = '';
+ foreach ($data as $key => $value) {
+ $sequence .= (string)$key . _logger_serialise($value);
+ }
+ return $sequence;
+ }
+ else {
+ return (string)$data;
+ }
+}
diff --git a/web/drupal/themes/flukso/LICENSE.txt b/web/drupal/themes/flukso/LICENSE.txt
new file mode 100644
index 0000000..2c095c8
--- /dev/null
+++ b/web/drupal/themes/flukso/LICENSE.txt
@@ -0,0 +1,274 @@
+GNU GENERAL PUBLIC LICENSE
+
+ Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+ Preamble
+
+The licenses for most software are designed to take away your freedom to
+share and change it. By contrast, the GNU General Public License is
+intended to guarantee your freedom to share and change free software--to
+make sure the software is free for all its users. This General Public License
+applies to most of the Free Software Foundation's software and to any other
+program whose authors commit to using it. (Some other Free Software
+Foundation software is covered by the GNU Library General Public License
+instead.) You can apply it to your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the
+freedom to distribute copies of free software (and charge for this service if
+you wish), that you receive source code or can get it if you want it, that you
+can change the software or use pieces of it in new free programs; and that
+you know you can do these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to
+deny you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for
+a fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2)
+offer you this license which gives you legal permission to copy, distribute
+and/or modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients
+to know that what they have is not the original, so that any problems
+introduced by others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We
+wish to avoid the danger that redistributors of a free program will individually
+obtain patent licenses, in effect making the program proprietary. To prevent
+this, we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+ MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms
+of this General Public License. The "Program", below, refers to any such
+program or work, and a "work based on the Program" means either the
+Program or any derivative work under copyright law: that is to say, a work
+containing the Program or a portion of it, either verbatim or with
+modifications and/or translated into another language. (Hereinafter, translation
+is included without limitation in the term "modification".) Each licensee is
+addressed as "you".
+
+Activities other than copying, distribution and modification are not covered
+by this License; they are outside its scope. The act of running the Program is
+not restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made
+by running the Program). Whether that is true depends on what the Program
+does.
+
+1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this License
+and to the absence of any warranty; and give any other recipients of the
+Program a copy of this License along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you
+may at your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it,
+thus forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you
+also meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that
+you changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be
+licensed as a whole at no charge to all third parties under the terms of this
+License.
+
+c) If the modified program normally reads commands interactively when run,
+you must cause it, when started running for such interactive use in the most
+ordinary way, to print or display an announcement including an appropriate
+copyright notice and a notice that there is no warranty (or else, saying that
+you provide a warranty) and that users may redistribute the program under
+these conditions, and telling the user how to view a copy of this License.
+(Exception: if the Program itself is interactive but does not normally print such
+an announcement, your work based on the Program is not required to print
+an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be
+reasonably considered independent and separate works in themselves, then
+this License, and its terms, do not apply to those sections when you distribute
+them as separate works. But when you distribute the same sections as part
+of a whole which is a work based on the Program, the distribution of the
+whole must be on the terms of this License, whose permissions for other
+licensees extend to the entire whole, and thus to each and every part
+regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to
+control the distribution of derivative or collective works based on the
+Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of a
+storage or distribution medium does not bring the other work under the scope
+of this License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1
+and 2 above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above
+on a medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give
+any third party, for a charge no more than your cost of physically performing
+source distribution, a complete machine-readable copy of the corresponding
+source code, to be distributed under the terms of Sections 1 and 2 above on
+a medium customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for
+noncommercial distribution and only if you received the program in object
+code or executable form with such an offer, in accord with Subsection b
+above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source code
+means all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation and
+installation of the executable. However, as a special exception, the source
+code distributed need not include anything that is normally distributed (in
+either source or binary form) with the major components (compiler, kernel,
+and so on) of the operating system on which the executable runs, unless that
+component itself accompanies the executable.
+
+If distribution of executable or object code is made by offering access to
+copy from a designated place, then offering equivalent access to copy the
+source code from the same place counts as distribution of the source code,
+even though third parties are not compelled to copy the source along with the
+object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy,
+modify, sublicense or distribute the Program is void, and will automatically
+terminate your rights under this License. However, parties who have received
+copies, or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the
+Program or its derivative works. These actions are prohibited by law if you
+do not accept this License. Therefore, by modifying or distributing the
+Program (or any work based on the Program), you indicate your acceptance
+of this License to do so, and all its terms and conditions for copying,
+distributing or modifying the Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these terms and
+conditions. You may not impose any further restrictions on the recipients'
+exercise of the rights granted herein. You are not responsible for enforcing
+compliance by third parties to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues), conditions
+are imposed on you (whether by court order, agreement or otherwise) that
+contradict the conditions of this License, they do not excuse you from the
+conditions of this License. If you cannot distribute so as to satisfy
+simultaneously your obligations under this License and any other pertinent
+obligations, then as a consequence you may not distribute the Program at all.
+For example, if a patent license would not permit royalty-free redistribution
+of the Program by all those who receive copies directly or indirectly through
+you, then the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply and
+the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or
+other property right claims or to contest validity of any such claims; this
+section has the sole purpose of protecting the integrity of the free software
+distribution system, which is implemented by public license practices. Many
+people have made generous contributions to the wide range of software
+distributed through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing to
+distribute software through any other system and a licensee cannot impose
+that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain
+countries either by patents or by copyrighted interfaces, the original copyright
+holder who places the Program under this License may add an explicit
+geographical distribution limitation excluding those countries, so that
+distribution is permitted only in or among countries not thus excluded. In such
+case, this License incorporates the limitation as if written in the body of this
+License.
+
+9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will be
+similar in spirit to the present version, but may differ in detail to address new
+problems or concerns.
+
+Each version is given a distinguishing version number. If the Program specifies
+a version number of this License which applies to it and "any later version",
+you have the option of following the terms and conditions either of that
+version or of any later version published by the Free Software Foundation. If
+the Program does not specify a version number of this License, you may
+choose any version ever published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software
+Foundation, write to the Free Software Foundation; we sometimes make
+exceptions for this. Our decision will be guided by the two goals of
+preserving the free status of all derivatives of our free software and of
+promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
+THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
+PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
+STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
+WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
+PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
+NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
+AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
+ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
+SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE
+PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
+OR DATA BEING RENDERED INACCURATE OR LOSSES
+SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
+PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
+IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
diff --git a/web/drupal/themes/flukso/README.txt b/web/drupal/themes/flukso/README.txt
new file mode 100644
index 0000000..44ddecb
--- /dev/null
+++ b/web/drupal/themes/flukso/README.txt
@@ -0,0 +1,9 @@
+$Id$
+
+Description:
+------------
+This is the Flukso theme, based on Denis Polevoi's teleology theme.
+
+Authors:
+--------
+icarus75 a.k.a. Bart Van Der Meerssche
diff --git a/web/drupal/themes/flukso/comment.tpl.php b/web/drupal/themes/flukso/comment.tpl.php
new file mode 100644
index 0000000..db0f9a9
--- /dev/null
+++ b/web/drupal/themes/flukso/comment.tpl.php
@@ -0,0 +1,26 @@
+new) ? ' comment-new' : '') .
+ ' ' . $status . ' ' . $zebra;
+?>
+
diff --git a/web/drupal/themes/flukso/favicon.ico b/web/drupal/themes/flukso/favicon.ico
new file mode 100755
index 0000000..1b347b5
Binary files /dev/null and b/web/drupal/themes/flukso/favicon.ico differ
diff --git a/web/drupal/themes/flukso/flukso.info b/web/drupal/themes/flukso/flukso.info
new file mode 100644
index 0000000..63b8040
--- /dev/null
+++ b/web/drupal/themes/flukso/flukso.info
@@ -0,0 +1,23 @@
+ ; $Id:$
+ ; --------------------------------------------------------------------------
+ ; Basic information about the theme.
+ ; --------------------------------------------------------------------------
+name = Flukso
+description = "Fixed width theme derived from the Teleology theme"
+screenshot = screenshot.png
+core = 6.x
+engine = phptemplate
+
+ ; --------------------------------------------------------------------------
+ ; Theme features.
+ ; --------------------------------------------------------------------------
+features[] = name
+features[] = slogan
+features[] = search
+features[] = favicon
+features[] = primary_links
+features[] = secondary_links
+features[] = mission
+features[] = node_user_picture
+features[] = comment_user_picture
+features[] = logo
diff --git a/web/drupal/themes/flukso/icons/calendar.png b/web/drupal/themes/flukso/icons/calendar.png
new file mode 100644
index 0000000..c245f1b
Binary files /dev/null and b/web/drupal/themes/flukso/icons/calendar.png differ
diff --git a/web/drupal/themes/flukso/icons/feed.png b/web/drupal/themes/flukso/icons/feed.png
new file mode 100644
index 0000000..315c4f4
Binary files /dev/null and b/web/drupal/themes/flukso/icons/feed.png differ
diff --git a/web/drupal/themes/flukso/icons/key.png b/web/drupal/themes/flukso/icons/key.png
new file mode 100644
index 0000000..7662674
Binary files /dev/null and b/web/drupal/themes/flukso/icons/key.png differ
diff --git a/web/drupal/themes/flukso/icons/menu-collapsed.png b/web/drupal/themes/flukso/icons/menu-collapsed.png
new file mode 100644
index 0000000..95a214a
Binary files /dev/null and b/web/drupal/themes/flukso/icons/menu-collapsed.png differ
diff --git a/web/drupal/themes/flukso/icons/menu-expanded.png b/web/drupal/themes/flukso/icons/menu-expanded.png
new file mode 100644
index 0000000..46f39ec
Binary files /dev/null and b/web/drupal/themes/flukso/icons/menu-expanded.png differ
diff --git a/web/drupal/themes/flukso/icons/menu-leaf.png b/web/drupal/themes/flukso/icons/menu-leaf.png
new file mode 100644
index 0000000..827ba08
Binary files /dev/null and b/web/drupal/themes/flukso/icons/menu-leaf.png differ
diff --git a/web/drupal/themes/flukso/icons/page_white.png b/web/drupal/themes/flukso/icons/page_white.png
new file mode 100644
index 0000000..3103c92
Binary files /dev/null and b/web/drupal/themes/flukso/icons/page_white.png differ
diff --git a/web/drupal/themes/flukso/icons/page_white_add.png b/web/drupal/themes/flukso/icons/page_white_add.png
new file mode 100644
index 0000000..555602c
Binary files /dev/null and b/web/drupal/themes/flukso/icons/page_white_add.png differ
diff --git a/web/drupal/themes/flukso/icons/page_white_go.png b/web/drupal/themes/flukso/icons/page_white_go.png
new file mode 100644
index 0000000..e3cc0d5
Binary files /dev/null and b/web/drupal/themes/flukso/icons/page_white_go.png differ
diff --git a/web/drupal/themes/flukso/icons/printer.png b/web/drupal/themes/flukso/icons/printer.png
new file mode 100644
index 0000000..888f9ed
Binary files /dev/null and b/web/drupal/themes/flukso/icons/printer.png differ
diff --git a/web/drupal/themes/flukso/icons/user.png b/web/drupal/themes/flukso/icons/user.png
new file mode 100644
index 0000000..1e26c0c
Binary files /dev/null and b/web/drupal/themes/flukso/icons/user.png differ
diff --git a/web/drupal/themes/flukso/images/comment-dark.gif b/web/drupal/themes/flukso/images/comment-dark.gif
new file mode 100644
index 0000000..93a7f80
Binary files /dev/null and b/web/drupal/themes/flukso/images/comment-dark.gif differ
diff --git a/web/drupal/themes/flukso/images/comment-light.gif b/web/drupal/themes/flukso/images/comment-light.gif
new file mode 100644
index 0000000..f2e48b0
Binary files /dev/null and b/web/drupal/themes/flukso/images/comment-light.gif differ
diff --git a/web/drupal/themes/flukso/images/menu-bg.png b/web/drupal/themes/flukso/images/menu-bg.png
new file mode 100644
index 0000000..1a47065
Binary files /dev/null and b/web/drupal/themes/flukso/images/menu-bg.png differ
diff --git a/web/drupal/themes/flukso/logo.png b/web/drupal/themes/flukso/logo.png
new file mode 100755
index 0000000..d3710f3
Binary files /dev/null and b/web/drupal/themes/flukso/logo.png differ
diff --git a/web/drupal/themes/flukso/logo.png.large b/web/drupal/themes/flukso/logo.png.large
new file mode 100755
index 0000000..99cdbf4
Binary files /dev/null and b/web/drupal/themes/flukso/logo.png.large differ
diff --git a/web/drupal/themes/flukso/node.tpl.php b/web/drupal/themes/flukso/node.tpl.php
new file mode 100644
index 0000000..1ea6d62
--- /dev/null
+++ b/web/drupal/themes/flukso/node.tpl.php
@@ -0,0 +1,22 @@
+">
+
+
+
+
+
+
+
+
+
theme('username', $node), '!date' => format_date($node->created))); ?>
+
+
+
+
+
+
>>
+
+
+
+
+
+
diff --git a/web/drupal/themes/flukso/page.tpl.php b/web/drupal/themes/flukso/page.tpl.php
new file mode 100644
index 0000000..67212a1
--- /dev/null
+++ b/web/drupal/themes/flukso/page.tpl.php
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/drupal/themes/flukso/screenshot-drupal.org.png b/web/drupal/themes/flukso/screenshot-drupal.org.png
new file mode 100644
index 0000000..2de8caf
Binary files /dev/null and b/web/drupal/themes/flukso/screenshot-drupal.org.png differ
diff --git a/web/drupal/themes/flukso/screenshot.large.png b/web/drupal/themes/flukso/screenshot.large.png
new file mode 100644
index 0000000..a42835a
Binary files /dev/null and b/web/drupal/themes/flukso/screenshot.large.png differ
diff --git a/web/drupal/themes/flukso/screenshot.png b/web/drupal/themes/flukso/screenshot.png
new file mode 100644
index 0000000..53313e0
Binary files /dev/null and b/web/drupal/themes/flukso/screenshot.png differ
diff --git a/web/drupal/themes/flukso/style.css b/web/drupal/themes/flukso/style.css
new file mode 100644
index 0000000..5e35c05
--- /dev/null
+++ b/web/drupal/themes/flukso/style.css
@@ -0,0 +1,517 @@
+/* Flukso theme by icarus75 */
+/* derived from the teleology theme */
+
+/* $Id* */
+
+a {
+ font-weight: normal;
+ text-decoration: none;
+ padding: 1px;
+}
+
+a:link {
+ color: rgb(150,150,150);
+}
+
+a:visited {
+ color: rgb(150,150,150);
+}
+
+a:hover {
+ background-color:
+ rgb(0,174,239);
+ color: rgb(255,255,255);
+}
+
+body {
+ background: #fff;
+ color: #444;
+ font-family: Arial, system, Tahoma, sans-serif;
+ font-size: 100%;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ margin: 0;
+ font-size: 1em;
+}
+
+table {
+ font-size: 1em;
+}
+
+#container {
+ margin: 15px auto;
+ width: 920px;
+ padding 0 10px 10px;
+}
+
+#top {
+ background: #f3f3f3;
+ border: 1px solid #ddd;
+ margin: 0 0 12px;
+ width: 684px;
+}
+
+#home a:hover {
+ background-color: #f3f3f3;
+}
+
+#logo img {
+ margin: 1em auto 1em 1em;
+ vertical-align: bottom;
+}
+
+.site-name {
+ font-size: 1.8em;
+ vertical-align: bottom;
+}
+
+.site-slogan {
+ font-size: .8em;
+ padding: 0em 0em 0em 1em;
+}
+
+#navigation {
+ background: #f3f3f3 url(images/menu-bg.png) center repeat;
+ /*border-top: 1px solid #ddd;*/
+ float: left; font-size: 1em;
+ text-align: center; width: 100%;
+}
+
+#search {
+ background: #f3f3f3 url(images/menu-bg.png) center repeat;
+ padding-right: .4em;
+ text-align: right;
+}
+
+#search .form-text {
+ border-width: 1px;
+ font-size: .6em;
+}
+
+#search .form-submit {
+ border-width: 1px;
+ font-size: .6em;
+}
+
+#leftnav {
+ float: left;
+ width: 222px;
+ margin: 0 auto auto 0;
+}
+
+#rightnav {
+ float: right;
+ width: 222px;
+ margin: 0 0 auto auto;
+}
+
+#leftnav .block, #rightnav .block {
+ background: #f3f3f3;
+ border: 1px solid #ddd;
+ font-size: 0.8em;
+ margin: 0% 0 6% 0;
+}
+
+#content {
+ background: #f3f3f3;
+ border: 1px solid #ddd;
+ font-size: .8em;
+ width: 660px;
+ margin: 0;
+ padding: 12px;
+ text-align: justify;
+}
+
+/* :BVDM: center charts in text body */
+#chart {
+ padding: 1em;
+ text-align: center;
+}
+
+#header .block {
+ border: 1px solid #ddd;
+ font-size: 1em;
+}
+
+#header .content {
+ font-size: 1em;
+}
+
+.content-title {
+ font-size: 1.4em;
+ font-weight: normal;
+}
+
+.node {
+ /* border: 1px solid #ddd; */
+ font-size: 1em;
+ margin-bottom: 3em;
+}
+
+.breadcrumb {
+ font-size: 1em;
+ padding: 0 0 .2em 0;
+}
+
+/* forms */
+.form-item {
+ font-size: 1em;
+}
+
+/* .form-item label { color: #000;} */
+
+fieldset {
+ border: 1px solid #ddd;
+}
+
+input, textarea, select {
+ border: 1px silver inset;
+}
+
+input[type="submit"] {
+ border: 1px #ddd outset;
+ background-color: #f3f3f3;
+}
+
+input[type="text"], textarea, input.form-password {
+ padding: 1px;
+}
+
+.node h2 {
+ /* background: #f3f3f3; */
+ color: #f7f7f7;
+ font-size: 1.6em;
+ padding: 0 0 1em 0;
+}
+
+.node .content, .node .info {
+ background: #f3f3f3;
+ font-size: 1em;
+}
+
+.node .picture {
+ border: 1px solid #ddd;
+ float: right;
+ margin: 0.5em;
+}
+
+.node.item-listing {
+ padding: 1em;
+ border-top: 1px solid #ff0000;
+}
+
+.node.item-listing.sticky {
+ background-image: none;
+}
+
+.node table ul {
+ margin: 0;
+ padding: 1em;
+}
+
+.node.sticky .title {
+ background-image: none;
+}
+
+.node .title {
+ border-bottom: 1px solid #ff0000;
+}
+
+.messages {
+ background: #eee;
+ margin: 0em;
+}
+
+.clear.links {
+ font-size: 1em;
+ padding: 0em;
+}
+
+br.clear {
+ clear: both;
+}
+
+.block {
+ margin-bottom: 1em;
+ background: #f7f7f7;
+}
+
+.block h2 {
+ background: #f3f3f3 url(images/menu-bg.png) center repeat;
+ color: #444;
+ font-size: 1em;
+ font-weight: normal;
+ height: auto;
+ padding: .2em;
+}
+
+.block .content {
+ font-size: 1em;
+ padding-left: 0.2em;
+}
+
+.box {
+ /*border: 1px solid #ddd;*/
+ font-size: 1em;
+ padding: 0em 0em 2em 1em;
+ text-align: left;
+}
+
+.box .title {
+ font-size: 1em;
+}
+
+.quote-author {
+ font-style: normal;
+ font-weight: normal;
+}
+
+.quote-msg {
+ /* border: 1px solid #ddd; */
+ background-color: #fefefe;
+ font-style: italic;
+ padding: 0em .2em 0 .2em;
+}
+
+#footer {
+ background: #f3f3f3;
+ border: 1px solid #ddd;
+ color: #aaa;
+ font-size: 0.64em;
+ padding: .2em 0 .2em 0;
+ width: 684px;
+ margin: 12px 0 0 0;
+ text-align: center;
+}
+
+/* icons */
+a.read-more,
+.icon-print,
+.icon-add-child-page,
+.icon-calendar,
+.form-text#edit-name,
+.form-password#edit-pass,
+.form-text#edit-pass,
+.form-text#edit-date {
+ background-repeat: no-repeat;
+ background-position: left center;
+ padding-left: 3em;
+}
+
+a.read-more {
+ background-image: url("icons/page_white_go.png");
+}
+
+.icon-print {
+ background-image: url("icons/printer.png");
+}
+
+.icon-add-child-page {
+ background-image: url("icons/page_white_add.png");
+}
+
+.icon-calendar {
+ background-image: url("icons/calendar.png");
+}
+
+.form-text#edit-name {
+ background-image: url("icons/user.png");
+}
+
+.form-password#edit-pass, .form-text#edit-pass {
+ background-image: url("icons/key.png");
+}
+
+.form-text#edit-date {
+ background-image: url("icons/calendar.png");
+}
+
+/* tables */
+tr th, tr.light td, tr.dark td, tr.odd td, tr.even td {
+ padding: 0em .2em 0em .2em;
+}
+
+tr.light, tr.dark, tr.even, tr.odd {
+ background-color: #fff;
+}
+
+tr.light .active, tr.dark .active, tr.even .active, tr.odd .active {
+ background-color: #fff;
+}
+
+/* tr th {border: 1px solid #ddd;} */
+
+tr.watchdog-user {
+ background-color: #ffd;
+ font-size: .7em;
+}
+
+tr.watchdog-user .active {
+ background-color: #eed;
+ font-size: .7em;
+}
+
+tr.watchdog-content {
+ background-color: #ddf;
+ font-size: .7em;
+}
+
+tr.watchdog-content .active {
+ background-color: #cce;
+ font-size: .7em;
+}
+
+tr.watchdog-page-not-found, tr.watchdog-access-denied {
+ background-color: #dfd;
+ font-size: .7em;
+}
+
+tr.watchdog-page-not-found .active, tr.watchdog-access-denied .active {
+ background-color: #cec;
+ font-size: .7em;
+}
+
+tr.watchdog-error {
+ background-color: #ffc9c9;
+ font-size: .7em;
+}
+
+tr.watchdog-error .active {
+ background-color: #eeb9b9;
+ font-size: .7em;
+}
+
+tr.watchdog-menu, tr.watchdog-menu {
+ background-color: #fff;
+ font-size: .7em;
+}
+
+/* lists */
+ul.primary li a:hover {
+ background-color: rgb(0,174,239);
+ color: rgb(255,255,255);
+}
+
+ul.secondary a {
+ padding: 1px;
+}
+
+ul li a:hover {
+ background-color: rgb(0,174,239);
+ color: rgb(255,255,255);
+}
+
+html.js fieldset legend a:hover {
+ background-color: rgb(0,174,239);
+ color: rgb(255,255,255);
+}
+
+/* ------------------------------------------------------------------------
+ * Image-assist: overruling clear: both to eliminate spacing below img
+ * --------------------------------------------------------------------- */
+.image-clear {
+ clear: none;
+}
+
+/* ------------------------------------------------------------------------
+ * Comments
+ * --------------------------------------------------------------------- */
+#comments {
+ margin-top: 0em;
+ margin-bottom: 2em;
+ padding: 0 1.5em 0 1.5em;
+}
+
+div.comment {
+ margin: 0 0 2em 0;
+}
+
+div.even div.content,
+div.odd div.content {
+ border: 1px solid #ccc;
+ border-bottom: 0;
+ margin: 0;
+ padding: 1em;
+}
+
+div.comment div.content h3 {
+ font-weight: normal;
+ margin: 0;
+}
+
+div.comment div.content h3 a:link,
+div.comment div.content h3 a:visited {
+ color: #333;
+ font-weight: normal;
+ text-decoration: none;
+}
+
+div.comment div.content h3 a:hover {
+ color: #ed1a09;
+}
+
+div.comment div.content p {
+ margin: 0;
+ padding: 0;
+}
+
+div.comment img.user-picture {
+ float: left;
+ padding: 0 10px 5px 0;
+}
+
+div.comment div.content p+p { margin-top: 1.5em; }
+
+div.even div.content { background-color: #fff; }
+
+div.comment-meta {
+ display: block;
+ margin: 0;
+ padding: 0.66em 0 0 0;
+}
+
+div.comment-meta span {
+ margin-left: 0.66em;
+ font-style: normal;
+}
+
+div.even div.comment-meta {
+ background: #f3f3f3 url(images/comment-light.gif) no-repeat top left;
+ margin: 0;
+ font-style: normal;
+}
+
+div.odd {
+ background-color: #eee;
+}
+
+div.odd div.comment-meta {
+ background: #f3f3f3 url(images/comment-dark.gif) no-repeat top left;
+}
+
+em.moderation {
+ background-color: #fffbcc;
+ padding: 2px;
+ border: 1px solid #e6db55;
+}
+
+#respond { font-weight: normal }
+
+#comment-form-wrapper { margin: 0 1.5em 0 1.5em; }
+
+#comments h2.comments,
+#comments div.box h2 {
+ display: block;
+ font-size: 1.75em;
+ margin-bottom: 0;
+ text-transform: lowercase;
+}
+
+#comments div.box {
+ margin-top: 10px;
+}
+
+#comment-controls {
+ margin: 20px 0;
+}