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