mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-27 16:29:22 +01:00
Work In Progress on weather widget
This commit is contained in:
parent
d51c83402d
commit
befabcb2eb
@ -477,6 +477,7 @@ class home_ui
|
|||||||
*/
|
*/
|
||||||
public function ajax_set_properties($portlet_id, $attributes, $values, $group = false)
|
public function ajax_set_properties($portlet_id, $attributes, $values, $group = false)
|
||||||
{
|
{
|
||||||
|
//error_log(__METHOD__ . "($portlet_id, " .array2string($attributes).','.array2string($values).",$group)");
|
||||||
if(!$attributes)
|
if(!$attributes)
|
||||||
{
|
{
|
||||||
$attributes = array();
|
$attributes = array();
|
||||||
@ -546,11 +547,9 @@ class home_ui
|
|||||||
unset($values['value']);unset($values['align']);
|
unset($values['value']);unset($values['align']);
|
||||||
|
|
||||||
// Get portlet settings, and merge new with old
|
// Get portlet settings, and merge new with old
|
||||||
$context = $values+(array)$portlets[$portlet_id];
|
$context = array_merge((array)$portlets[$portlet_id], $values);
|
||||||
$context['group'] = $group;
|
$context['group'] = $group;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Handle add IDs
|
// Handle add IDs
|
||||||
$classname =& $context['class'];
|
$classname =& $context['class'];
|
||||||
if(strpos($classname,'add_') == 0 && !class_exists($classname))
|
if(strpos($classname,'add_') == 0 && !class_exists($classname))
|
||||||
|
259
home/inc/class.home_weather_portlet.inc.php
Normal file
259
home/inc/class.home_weather_portlet.inc.php
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Egroupware Weather widget
|
||||||
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
||||||
|
* @package home
|
||||||
|
* @subpackage portlet
|
||||||
|
* @link http://www.egroupware.org
|
||||||
|
* @author Nathan Gray
|
||||||
|
* @version $Id$
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Widget displaying the weather
|
||||||
|
*
|
||||||
|
* This widget displays more or less data depending on the portlet size using
|
||||||
|
* a combination of the disabled attribute in the template, and unsetting
|
||||||
|
* things to fit. It also uses some CSS to make sure things fit according to
|
||||||
|
* the grid size.
|
||||||
|
*
|
||||||
|
* We use openweathermap.org as a data source.
|
||||||
|
*/
|
||||||
|
class home_weather_portlet extends home_portlet
|
||||||
|
{
|
||||||
|
|
||||||
|
const API_URL = "http://api.openweathermap.org/data/2.5/";
|
||||||
|
const ICON_URL = 'http://openweathermap.org/img/w/';
|
||||||
|
const API_KEY = '';
|
||||||
|
const CACHE_TIME = 3600; // Cache weather for an hour
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for this portlet
|
||||||
|
*/
|
||||||
|
public function __construct(Array &$context = array(), &$need_reload = false)
|
||||||
|
{
|
||||||
|
// City not set for new widgets created via context menu
|
||||||
|
if(!$context['city'] || $context['height'] < 2)
|
||||||
|
{
|
||||||
|
// Set initial size to 3x2, default is too small
|
||||||
|
$context['width'] = 3;
|
||||||
|
$context['height'] = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
$need_reload = true;
|
||||||
|
|
||||||
|
$this->context = $context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exec($id = null, etemplate_new &$etemplate = null)
|
||||||
|
{
|
||||||
|
// Allow to submit directly back here
|
||||||
|
if(is_array($id) && $id['id'])
|
||||||
|
{
|
||||||
|
$id = $id['id'];
|
||||||
|
}
|
||||||
|
$etemplate->read('home.weather');
|
||||||
|
|
||||||
|
$etemplate->set_dom_id($id);
|
||||||
|
$content = $this->context;
|
||||||
|
$request = array(
|
||||||
|
'q' => $this->context['city'],
|
||||||
|
'units' => $this->context['units'] ? $this->context['units'] : 'metric',
|
||||||
|
'lang' => $GLOBALS['egw_info']['user']['preferences']['common']['lang'],
|
||||||
|
// Always get (& cache) 10 days, we'll cut down later
|
||||||
|
'cnt' => 10
|
||||||
|
);
|
||||||
|
|
||||||
|
if($this->context['city'])
|
||||||
|
{
|
||||||
|
$content += $this->get_weather($request);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust data to match portlet size
|
||||||
|
if($this->context['height'] <= 2 && $this->context['width'] <= 3)
|
||||||
|
{
|
||||||
|
// Too small for the other days
|
||||||
|
unset($content['list']);
|
||||||
|
}
|
||||||
|
else if ($this->context['height'] == 2 && $this->context['width'] > 3)
|
||||||
|
{
|
||||||
|
// Wider, but not taller
|
||||||
|
unset($content['current']);
|
||||||
|
}
|
||||||
|
// Even too small for current high/low
|
||||||
|
if($this->context['width'] < 3)
|
||||||
|
{
|
||||||
|
$content['current']['no_current_temp'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Direct to full forecast page
|
||||||
|
$content['attribution'] ='http://openweathermap.org/city/'.$content['city_id'];
|
||||||
|
|
||||||
|
$etemplate->exec('home.home_weather_portlet.exec',$content,array(),array('__ALL__'=>true),array('id' =>$id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch weather data from provider openweathermap.org
|
||||||
|
*
|
||||||
|
* @see http://openweathermap.org/api
|
||||||
|
* @param array $query
|
||||||
|
*/
|
||||||
|
public function get_weather(Array $query, $api_url = '')
|
||||||
|
{
|
||||||
|
static $debug = true;
|
||||||
|
if(!$api_url)
|
||||||
|
{
|
||||||
|
$api_url = self::API_URL . '/weather?';
|
||||||
|
}
|
||||||
|
if(self::API_KEY)
|
||||||
|
{
|
||||||
|
$query['APPID'] = self::API_KEY;
|
||||||
|
}
|
||||||
|
$data = egw_cache::getTree('home', json_encode($query), function($query) use(&$clear_cache) {
|
||||||
|
$debug = true;
|
||||||
|
if($debug) error_log('Fetching fresh data from ' . static::API_URL);
|
||||||
|
|
||||||
|
$url = static::API_URL.'forecast/daily?'. http_build_query($query);
|
||||||
|
$forecast = file_get_contents($url);
|
||||||
|
if($debug) error_log(__METHOD__ . ' forecast: ' . $weather);
|
||||||
|
if($forecast === FALSE)
|
||||||
|
|
||||||
|
$url = static::API_URL.'weather?'. http_build_query($query);
|
||||||
|
$current = file_get_contents($url) || array();
|
||||||
|
if($debug) error_log(__METHOD__ . ' current: ' . $current);
|
||||||
|
|
||||||
|
return array_merge(array('current' => json_decode($current,true)), json_decode($forecast,true));
|
||||||
|
}, array($query), self::CACHE_TIME);
|
||||||
|
|
||||||
|
// Some sample data, if you need to test
|
||||||
|
//error_log('Using hardcoded data instead of ' . $api_url . http_build_query($query));
|
||||||
|
//$weather = '{"coord":{"lon":-114.05,"lat":53.23},"sys":{"message":0.3098,"country":"Canada","sunrise":1420559329,"sunset":1420587344},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"base":"cmc stations","main":{"temp":-21.414,"temp_min":-21.414,"temp_max":-21.414,"pressure":947.79,"sea_level":1050.73,"grnd_level":947.79,"humidity":69},"wind":{"speed":3,"deg":273.5},"clouds":{"all":32},"dt":1420502430,"id":0,"name":"Thorsby","cod":200}';
|
||||||
|
//$weather = '{"cod":"200","message":0.1743,"city":{"id":"5978233","name":"Thorsby","coord":{"lon":-114.051,"lat":53.2285},"country":"Canada","population":0},"cnt":6,"list":[{"dt":1420743600,"temp":{"day":-17.49,"min":-27.86,"max":-16.38,"night":-27.86,"eve":-19.91,"morn":-16.77},"pressure":966.21,"humidity":66,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":6.91,"deg":312,"clouds":0,"snow":0.02},{"dt":1420830000,"temp":{"day":-24.86,"min":-29.71,"max":-17.98,"night":-18.31,"eve":-18.32,"morn":-29.51},"pressure":948.46,"humidity":54,"weather":[{"id":801,"main":"Clouds","description":"few clouds","icon":"02d"}],"speed":3.21,"deg":166,"clouds":20},{"dt":1420916400,"temp":{"day":-18.51,"min":-25.57,"max":-17.86,"night":-23.83,"eve":-23.91,"morn":-19.28},"pressure":947.22,"humidity":74,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.97,"deg":314,"clouds":48},{"dt":1421002800,"temp":{"day":-26.69,"min":-29.86,"max":-20.19,"night":-21.82,"eve":-24.66,"morn":-28.85},"pressure":951.93,"humidity":22,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"02d"}],"speed":1.36,"deg":196,"clouds":8},{"dt":1421089200,"temp":{"day":0.9,"min":-8.24,"max":0.9,"night":-4.99,"eve":-0.21,"morn":-8.24},"pressure":929.31,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":6.01,"deg":302,"clouds":5,"snow":0},{"dt":1421175600,"temp":{"day":-1.53,"min":-6.7,"max":2.23,"night":-3.65,"eve":2.23,"morn":-6.7},"pressure":934.51,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":3.9,"deg":201,"clouds":78}]}';
|
||||||
|
|
||||||
|
if($debug)
|
||||||
|
{
|
||||||
|
error_log(__METHOD__ .' weather info:');
|
||||||
|
foreach($data as $key => $val)
|
||||||
|
{
|
||||||
|
error_log($key . ': ' .array2string($data[$key]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(is_string($data['message']))
|
||||||
|
{
|
||||||
|
$desc = $this->get_description();
|
||||||
|
egw_framework::message($desc['displayName'] . ': ' . $desc['title'] . "\n".$data['message'], 'warning');
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(array_key_exists('city', $data))
|
||||||
|
{
|
||||||
|
$data['city_id'] = $data['city']['id'];
|
||||||
|
}
|
||||||
|
elseif ($data['city'])
|
||||||
|
{
|
||||||
|
$data['city_id'] = $data['id'];
|
||||||
|
}
|
||||||
|
if($data['list'])
|
||||||
|
{
|
||||||
|
$massage =& $data['list'];
|
||||||
|
|
||||||
|
for($i = 0; $i < min(count($massage), $this->context['width']); $i++)
|
||||||
|
{
|
||||||
|
$forecast =& $massage[$i];
|
||||||
|
$forecast['day'] = egw_time::to($forecast['dt'],'l');
|
||||||
|
self::format_forecast($forecast);
|
||||||
|
}
|
||||||
|
// Chop data to fit into portlet
|
||||||
|
for($i; $i < count($massage); $i++)
|
||||||
|
{
|
||||||
|
unset($massage[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($data['current'])
|
||||||
|
{
|
||||||
|
// Current weather
|
||||||
|
$data['current']['temp'] = $data['current']['main'];
|
||||||
|
self::format_forecast($data['current']);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if ($data['list'])
|
||||||
|
{
|
||||||
|
$data['current']['temp'] = array_merge($data['current']['temp'],$data['list'][0]['temp']);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format weather to our liking
|
||||||
|
*/
|
||||||
|
protected static function format_forecast(&$data)
|
||||||
|
{
|
||||||
|
$weather =& $data['weather'] ? $data['weather'] : $data;
|
||||||
|
$temp =& $data['temp'] ? $data['temp'] : $data;
|
||||||
|
|
||||||
|
// Full URL for icon
|
||||||
|
if(is_array($weather))
|
||||||
|
{
|
||||||
|
foreach($weather as &$w)
|
||||||
|
{
|
||||||
|
$w['icon'] = static::ICON_URL . $w['icon'].'.png';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Round
|
||||||
|
foreach(array('temp','temp_min','temp_max','min','max') as $temp_name)
|
||||||
|
{
|
||||||
|
if(array_key_exists($temp_name, $temp))
|
||||||
|
{
|
||||||
|
$temp[$temp_name] = ''.round($temp[$temp_name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_actions()
|
||||||
|
{
|
||||||
|
$actions = array(
|
||||||
|
);
|
||||||
|
return $actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of settings to customize the portlet.
|
||||||
|
*
|
||||||
|
* Settings should be in the same style as for preferences. It is OK to return an empty array
|
||||||
|
* for no customizable settings.
|
||||||
|
*
|
||||||
|
* These should be already translated, no further translation will be done.
|
||||||
|
*
|
||||||
|
* @see preferences/inc/class.preferences_settings.inc.php
|
||||||
|
* @return Array of settings. Each setting should have the following keys:
|
||||||
|
* - name: Internal reference
|
||||||
|
* - type: Widget type for editing
|
||||||
|
* - label: Human name
|
||||||
|
* - help: Description of the setting, and what it does
|
||||||
|
* - default: Default value, for when it's not set yet
|
||||||
|
*/
|
||||||
|
public function get_properties()
|
||||||
|
{
|
||||||
|
$properties = parent::get_properties();
|
||||||
|
|
||||||
|
$properties[] = array(
|
||||||
|
'name' => 'city',
|
||||||
|
'type' => 'textbox',
|
||||||
|
'label' => lang('Location'),
|
||||||
|
);
|
||||||
|
return $properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get_description()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'displayName'=> lang('Weather'),
|
||||||
|
'title'=> $this->context['city'],
|
||||||
|
'description'=> lang('Weather')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -127,3 +127,40 @@ div.calendar_favorite_portlet.et2_portlet.ui-widget-content > div:last-of-type {
|
|||||||
[class*="favorite_portlet"].et2_portlet .et2_nextmatch.header_hidden .egwGridView_outer thead:first-of-type th {
|
[class*="favorite_portlet"].et2_portlet .et2_nextmatch.header_hidden .egwGridView_outer thead:first-of-type th {
|
||||||
visibility:hidden;
|
visibility:hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Weather
|
||||||
|
*/
|
||||||
|
.home_weather_portlet table[id$="current"] {
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
.home_weather_portlet .temperature:after {
|
||||||
|
content: "\00B0";
|
||||||
|
}
|
||||||
|
.home_weather_portlet .current {
|
||||||
|
font-size: large;
|
||||||
|
}
|
||||||
|
.home_weather_portlet .forecast [id$="day"] {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
.home_weather_portlet .forecast > div {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
width: 52px;
|
||||||
|
}
|
||||||
|
.home_weather_portlet .forecast img {
|
||||||
|
margin: -10px -6px;
|
||||||
|
width: 40px;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
.home_weather_portlet .high_low {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
.home_weather_portlet .high_low[id$="min"] {
|
||||||
|
background-color: rgba(200,200,255,.3);
|
||||||
|
}
|
||||||
|
.home_weather_portlet .attribution {
|
||||||
|
position: relative;
|
||||||
|
bottom: 0.5em;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
49
home/templates/default/weather.xet
Normal file
49
home/templates/default/weather.xet
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<overlay>
|
||||||
|
<template id="home.weather" template="" lang="" group="0" version="1.9.001">
|
||||||
|
<grid id="current" disabled="!@current" width="100%">
|
||||||
|
<columns>
|
||||||
|
<column/>
|
||||||
|
<column/>
|
||||||
|
<column/>
|
||||||
|
<column/>
|
||||||
|
</columns>
|
||||||
|
<rows>
|
||||||
|
<row>
|
||||||
|
<image class="weather_icon" src="weather[0][icon]"/>
|
||||||
|
<hbox id="temp">
|
||||||
|
<description class="current temperature" id="temp" no_lang="true"/>
|
||||||
|
</hbox>
|
||||||
|
<vbox id="temp" disabled="@no_current_temp">
|
||||||
|
<description class="high_low temperature" id="max" no_lang="true"/>
|
||||||
|
<description class="high_low temperature" id="min" no_lang="true"/>
|
||||||
|
</vbox>
|
||||||
|
</row>
|
||||||
|
<row disabled="!@weather[0][description]">
|
||||||
|
<description id="weather[0][description]" no_lang="true"/>
|
||||||
|
</row>
|
||||||
|
</rows>
|
||||||
|
</grid>
|
||||||
|
<box id="list" class="forecast" disabled="!@list" width="100%">
|
||||||
|
<!-- Box wrapper needed to get box to auto-repeat -->
|
||||||
|
<box id="${row}">
|
||||||
|
<grid width="100%">
|
||||||
|
<columns>
|
||||||
|
<column/>
|
||||||
|
</columns>
|
||||||
|
<rows>
|
||||||
|
<row><description align="center" id="day"/></row>
|
||||||
|
<row class="weather_icon"><image align="center" class="weather_icon" src="weather[0][icon]"/></row>
|
||||||
|
<row>
|
||||||
|
<vbox align="center" id="temp">
|
||||||
|
<description class="high_low temperature" id="max" no_lang="true"/>
|
||||||
|
<description class="high_low temperature" id="min" no_lang="true"/>
|
||||||
|
</vbox>
|
||||||
|
</row>
|
||||||
|
</rows>
|
||||||
|
</grid>
|
||||||
|
</box>
|
||||||
|
</box>
|
||||||
|
<description activate_links="true" align="center" class="attribution" extra_link_target="_blank" href="@attribution" value="openweathermap.org"/>
|
||||||
|
</template>
|
||||||
|
</overlay>
|
Loading…
Reference in New Issue
Block a user