1: <?php
2: /**
3: * Weather support
4: *
5: * @package event-post
6: * @version 5.11.0
7: * @since 4.3.0
8: */
9:
10: namespace EventPost;
11:
12: $EventPostWeather = new Weather();
13:
14: /**
15: * Provides weather support thanks to OpenWeatherMap
16: * http://openweathermap.org
17: *
18: * License: Creative Commons (cc-by-sa)
19: * http://creativecommons.org/licenses/by-sa/2.0/.
20: *
21: *
22: * Get an API key
23: * http://openweathermap.org/appid#get
24: */
25: class Weather {
26:
27: var $META_WEATHER;
28:
29: var $api_key;
30: var $units;
31: var $unit_names;
32: var $theme;
33:
34: function __construct() {
35: // Hook into the plugin
36:
37: add_action('eventpost_getsettings_action', array(&$this, 'get_settings'), 1, 2);
38: add_action('eventpost_settings_form', array(&$this, 'settings_form'));
39: add_action('evenpost_init', array(&$this, 'init'));
40: }
41:
42: /**
43: * PHP4 constructor
44: */
45: function Weather() {
46: $this->__construct();
47: }
48:
49: /**
50: * Only for localization
51: *
52: * Available values:
53: *
54: * - clear sky
55: * - few clouds
56: * - scattered clouds
57: * - broken clouds
58: * - shower rain
59: * - rain
60: * - thunderstorm
61: * - snow
62: * - mist
63: */
64: function localize(){
65: __('clear sky', 'event-post');
66: __('few clouds', 'event-post');
67: __('scattered clouds', 'event-post');
68: __('broken clouds', 'event-post');
69: __('shower rain', 'event-post');
70: __('rain', 'event-post');
71: __('thunderstorm', 'event-post');
72: __('snow', 'event-post');
73: __('mist', 'event-post');
74:
75: }
76:
77: /**
78: * Get settings
79: *
80: * @param array reference &$ep_settings
81: * @param boolean reference &$reg_settings
82: */
83: function get_settings(&$ep_settings, &$reg_settings) {
84: if (!isset($ep_settings['weather_enabled'])) {
85: $ep_settings['weather_enabled'] = false;
86: $reg_settings = true;
87: }
88: if (!isset($ep_settings['weather_api_key'])) {
89: $ep_settings['weather_api_key'] = '';
90: $reg_settings = true;
91: }
92: if (!isset($ep_settings['weather_units'])) {
93: $ep_settings['weather_units'] = 'standard';
94: $reg_settings = true;
95: }
96: }
97:
98: /**
99: * Settings form
100: *
101: * @param type $ep_settings
102: */
103: function settings_form($ep_settings) {
104: ?>
105: <h2><?php esc_html_e('Weather', 'event-post'); ?></h2>
106: <?php // Translators: %s is a link to openweathermap.org ?>
107: <p class="description"><?php printf(esc_html(__('Provided thanks to %s', 'event-post')), '<a href="http://openweathermap.org" target="_blank">openweathermap.org</a>'); ?></p>
108: <table class="form-table" id="eventpost-settings-table-weather">
109: <tbody>
110: <tr>
111: <th>
112: <?php esc_html_e('Enable weather', 'event-post') ?>
113: </th>
114: <td>
115: <label for="weather_enabled">
116: <input type="checkbox" name="ep_settings[weather_enabled]" id="weather_enabled" <?php if ($ep_settings['weather_enabled'] == '1') {
117: echo esc_attr('checked');
118: } ?> value="1">
119: <?php esc_html_e('Enable weather feature', 'event-post') ?>
120: </label>
121: </td>
122: </tr>
123: <tr>
124: <th>
125: <label for="weather_api_key">
126: <?php esc_html_e('API key', 'event-post') ?>
127: </label>
128: </th>
129: <td>
130: <input type="text" name="ep_settings[weather_api_key]" id="weather_api_key" value="<?php echo esc_attr($ep_settings['weather_api_key']); ?>" size="40">
131: <?php // Translators: %s is a link to openweathermap.org/appid#get ?>
132: <p class="description"><?php printf(esc_html(__('Get a free API key at: %s', 'event-post')), '<a href="http://openweathermap.org/appid#get" target="_blank">openweathermap.org/appid#get</a>'); ?></p>
133: </td>
134: </tr>
135: <tr>
136: <th>
137: <label for="weather_units">
138: <?php esc_html_e('Units', 'event-post') ?>
139: </label>
140: </th>
141: <td>
142: <select name="ep_settings[weather_units]" id="weather_units">
143: <option value="standard" <?php selected($ep_settings['weather_units'], 'standard', true);?>>
144: <?php esc_html_e('Standard (Fahrenheit)', 'event-post') ?>
145: </option>
146: <option value="metric" <?php selected($ep_settings['weather_units'], 'metric', true);?>>
147: <?php esc_html_e('Metric (Celsius)', 'event-post') ?>
148: </option>
149: <option value="imperial" <?php selected($ep_settings['weather_units'], 'imperial', true);?>>
150: <?php esc_html_e('Imperial (Kelvin)', 'event-post') ?>
151: </option>
152: </select>
153: </td>
154: </tr>
155: </tbody>
156: </table><!-- #eventpost-settings-table-weather -->
157: <?php
158: }
159:
160: /**
161: * Initialization of weather support
162: *
163: * @param object $EP
164: *
165: * @return void
166: */
167: function init($EP) {
168: // Ensure OpenWeatherMap is required and available.
169: if (!$EP->settings['weather_enabled'] || !$EP->settings['weather_api_key']) {
170: return;
171: }
172:
173: $this->META_WEATHER = 'event_weather';
174: $this->api_key = $EP->settings['weather_api_key'];
175: $this->units = $EP->settings['weather_units'];
176: $this->theme = 'default';
177: $this->unit_names = array('standard'=>'F', 'metric'=>'C', 'imperial'=>'K');
178:
179: // Alter objects
180: add_filter('eventpost_params', array(&$this, 'params'));
181: add_filter('eventpost_retreive', array(&$this, 'retreive'));
182:
183: // Alter schema
184: add_filter('eventpost_item_scheme_entities', array(&$this, 'scheme_entities'));
185: add_filter('eventpost_item_scheme_values', array(&$this, 'scheme_values'), 1, 2);
186: add_filter('eventpost_default_list_shema', array(&$this, 'default_shema'));
187: add_filter('eventpost_get_single', array(&$this, 'get_single'), 1, 2);
188:
189:
190: add_action('eventpost_custom_box_date', array(&$this, 'get_weather'));
191:
192:
193: }
194:
195: /**
196: * Alters parameters
197: *
198: * @param array $params
199: *
200: * @return array
201: */
202: function params($params = array()) {
203: $params['weather'] = true;
204: return $params;
205: }
206:
207: /**
208: * Alters an event object
209: *
210: * @param WP_Post $event
211: *
212: * @return WP_Post
213: */
214: function retreive($event) {
215: $event->weather = get_post_meta($event->ID, $this->META_WEATHER, true);
216: return $event;
217: }
218:
219: /**
220: * Alters schema entities
221: *
222: * @param array $attr
223: *
224: * @return array
225: */
226: function scheme_entities($attr = array()) {
227: array_push($attr, '%event_weather%');
228: return $attr;
229: }
230:
231: /**
232: * Alters schema values
233: *
234: * @param array $values
235: *
236: * @return array
237: */
238: function scheme_values($values = array(), $post = null) {
239: array_push($values, $this->get_weather($post));
240: return $values;
241: }
242:
243: /**
244: * Alters default schema
245: *
246: * @param array $schema
247: *
248: * @return array
249: */
250: function default_shema($schema) {
251: $schema['item'] = str_replace('</%child%>', "%event_weather%\n</%child%>", $schema['item']);
252: return $schema;
253: }
254:
255: function get_single($event_datas, $post = null) {
256: return $event_datas . $this->get_weather($post);
257: }
258:
259: //From here, methods intends to get datas
260:
261: function get_weather_icons($weather){
262: $string = '';
263: foreach((array) $weather as $item){
264: $text = ucfirst(__(strtolower($item->description), 'event-post'));
265: $string.='<span class="eventpost-weather-'.str_replace('-', '', strtolower($item->description)).' eventpost-weather-'.$item->icon.'">'
266: // Translators: %s is the weather description
267: . '<img src="'. plugins_url('../img/weather/'.$this->theme.'/'.$item->icon.'.png', __FILE__).'" alt="'.sprintf('%s icon', $text).'">'
268: . '<em class="eventpost-weather-text">'.$text.'</em>'
269: . '</span>';
270: }
271: return $string;
272: }
273: /**
274: * Get weather item
275: *
276: * @param object $item
277: *
278: * @return string
279: */
280: function get_weather_item($item){
281: $string = '';
282: $string.= '<div class="eventpost-weather-item">'
283: .'<span class="eventpost-weather-date">'.date_i18n(get_option('date_format').' '.get_option('time_format'), $item->dt).'</span> '
284: .'<span class="eventpost-weather-temp">'.round($item->main->temp).' &deg'.$this->unit_names[$this->units].'</span> '
285: .'<span class="eventpost-weather-list">'.$this->get_weather_icons($item->weather).'</span>'
286: .'</div>';
287: return $string;
288: }
289:
290: /**
291: * Get weather
292: *
293: * @param type $post
294: *
295: * @return string
296: */
297: function get_weather($post = null, $echo=false) {
298: global $EventPost;
299: $event = $EventPost->retreive($post);
300: if(false === $weather = $this->get_weather_datas($event)){
301: return '';
302: }
303: if(false == $weather['data']){
304: return '';
305: }
306:
307: $string='';
308:
309: switch ($weather['type']){
310: case 'current':
311: $string.=$this->get_weather_item($weather['data']);
312: break;
313: case 'history':
314: if(!isset($weather['data']->list) || !is_array($weather['data']->list)){
315: break;
316: }
317: foreach($weather['data']->list as $day){
318: if($day->dt >= $event->time_start && $day->dt <= $event->time_end){
319: $string.=$this->get_weather_item($day);
320: }
321: }
322: break;
323: case 'forecast':
324: if(!is_array($weather['data']->list)){
325: break;
326: }
327: foreach($weather['data']->list as $day){
328: if($day->dt >= $event->time_start && $day->dt <= $event->time_end){
329: $string.=$this->get_weather_item($day);
330: }
331: }
332: break;
333: }
334: if($echo){
335: echo wp_kses($string, EventPost()->kses_tags);
336: }
337: return $string;
338: }
339:
340: /**
341: * Get weather datas
342: *
343: * @param type $event
344: *
345: * @return type
346: */
347: function get_weather_datas($event){
348: if (!$event->lat || !$event->long || !is_numeric($event->time_start) || !is_numeric($event->time_end)) {
349: return false;
350: }
351:
352: $now = current_time('timestamp');
353: $local_weather = $event->weather;
354:
355:
356:
357: // Datas are allready stored, we probably won't get better ones.
358: if(is_array($local_weather) && $local_weather['data'] && ($local_weather['type']=='current' || $local_weather['type']=='history')){
359: return $local_weather;
360: }
361:
362: // Finally, we have to fetch datas...
363: $weather = array('type'=>false, 'data'=>false);
364:
365: // For Current and history results, we definitly store datas
366: if ( $event->time_start<= $now && $event->time_end>=$now) {
367: $weather = array('type'=>'current', 'data'=>$this->get_current($event), 'fetched'=>time());
368: update_post_meta($event->ID, $this->META_WEATHER, $weather);
369: }
370: elseif ( $event->time_end<$now) {// History
371: $weather = array('type'=>'history', 'data'=>$this->get_history($event), 'fetched'=>time());
372: update_post_meta($event->ID, $this->META_WEATHER, $weather);
373: }
374: else {
375: // Forecast datas are only stored in cache for 24 hours
376: $transient_name = 'eventpost_weather_' . $event->ID;
377: $weather = get_transient($transient_name);
378: if (false === $weather || empty($weather)) {
379: $weather = array('type'=>'forecast', 'data'=>$this->get_forecast($event), 'fetched'=>time());
380: set_transient($transient_name, $weather, 1 * DAY_IN_SECONDS);
381: }
382: }
383: return $weather;
384: }
385:
386:
387: /**
388: * Generates the URL to call the API
389: *
390: * @param type $method
391: * @param type $params
392: *
393: * @return type
394: */
395: function get_url($method = 'weather', $params = array()) {
396: $params['APPID']= $this->api_key;
397: $params['units']= $this->units;
398: return 'http://api.openweathermap.org/data/2.5/' . $method . '?' . http_build_query($params);
399: }
400:
401: /**
402: * Current weather
403: * http://api.openweathermap.org/data/2.5/weather?lat={lat}&lon={lon}&APPID=XXXX
404: *
405: * Return:
406: *
407: * {"coord":{"lon":139,"lat":35},
408: * "sys":{"country":"JP","sunrise":1369769524,"sunset":1369821049},
409: * "weather":[{"id":804,"main":"clouds","description":"overcast clouds","icon":"04n"}],
410: * "main":{"temp":289.5,"humidity":89,"pressure":1013,"temp_min":287.04,"temp_max":292.04},
411: * "wind":{"speed":7.31,"deg":187.002},
412: * "rain":{"3h":0},
413: * "clouds":{"all":92},
414: * "dt":1369824698,
415: * "id":1851632,
416: * "name":"Shuzenji",
417: * "cod":200}
418: *
419: * @param type $event
420: *
421: * @return object
422: */
423: function get_current($event){
424: if (!$event->lat || !$event->long) {
425: return;
426: }
427: return json_decode(wp_remote_retrieve_body(
428: wp_remote_get(
429: $this-> get_url('weather', array(
430: 'lat' => $event->lat,
431: 'lon' => $event->long,
432: ))
433: )
434: ));
435: }
436:
437: /**
438: * Forecast
439: * api.openweathermap.org/data/2.5/forecast?lat={lat}&lon={lon}&APPID=XXXX
440: *
441: * Return:
442: *
443: * {"city":{"id":1851632,"name":"Shuzenji",
444: * "coord":{"lon":138.933334,"lat":34.966671},
445: * "country":"JP",
446: * "cod":"200",
447: * "message":0.0045,
448: * "cnt":38,
449: * "list":[{
450: * "dt":1406106000,
451: * "main":{
452: * "temp":298.77,
453: * "temp_min":298.77,
454: * "temp_max":298.774,
455: * "pressure":1005.93,
456: * "sea_level":1018.18,
457: * "grnd_level":1005.93,
458: * "humidity":87
459: * "temp_kf":0.26},
460: * "weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],
461: * "clouds":{"all":88},
462: * "wind":{"speed":5.71,"deg":229.501},
463: * "sys":{"pod":"d"},
464: * "dt_txt":"2014-07-23 09:00:00"}
465: * ]}
466: *
467: * @param type $event
468: *
469: * @return type
470: */
471: function get_forecast($event){
472: if (!$event->lat || !$event->long) {
473: return;
474: }
475: return json_decode(wp_remote_retrieve_body(
476: wp_remote_get(
477: $this-> get_url('forecast', array(
478: 'lat' => $event->lat,
479: 'lon' => $event->long,
480: ))
481: )
482: ));
483: }
484:
485: /**
486: * ### history
487: * http://api.openweathermap.org/data/2.5/history/city?lat={lat}&lon={lon}&type=hour&start={start}&end={end}&APPID=XXXX
488: *
489: * Parameters:
490: * lat, lon coordinates of the location of your interest
491: * type type of the call, keep this parameter in the API call as 'hour'
492: * start start date (unix time, UTC time zone), e.g. start=1369728000
493: * end end date (unix time, UTC time zone), e.g. end=1369789200
494: * cnt amount of returned data (one per hour, can be used instead of 'end') *
495: * return:
496: * {"message":"","cod":"200","type":"tick","station_id":39419,"cnt":30,
497: * "list":[
498: * {"dt":1345291920,
499: * "main":{"temp":291.55,"humidity":95,"pressure":1009.3},
500: * "wind":{"speed":0,"gust":0.3},
501: * "rain":{"1h":0.6,"today":2.7},
502: * "calc":{"dewpoint":17.6} }
503: * ]}
504: *
505: * @param type $event
506: *
507: * @return type
508: */
509: function get_history($event) {
510: if (!$event->start || !$event->end || !$event->lat || !$event->long) {
511: return;
512: }
513: $history = json_decode(wp_remote_retrieve_body(
514: wp_remote_get(
515: $this-> get_url('history/city', array(
516: 'lat' => $event->lat,
517: 'lon' => $event->long,
518: 'start' => $event->time_start,
519: 'end' => $event->time_end,
520: 'type' => 'hour',
521: ))
522: )
523: ));
524: return $history ? $history : (object) array('result'=>false);
525: }
526:
527: }
528: