Commit fa4223e7 authored by agata's avatar agata

added squat-radar-calendar-integration

parent e01e05ef
.squat-radar.radar-event {
margin-bottom: 1em;
padding-bottom: 1em;
border-bottom: 1px solid #ddd;
text-align:left;
}
.squat-radar.radar-event::after {
content: '';
display: block;
float: none;
clear: both;
}
.squat-radar.rader-event:last-child {
border-bottom: 0;
padding-bottom: 0;
margin-bottom: 0;
}
.squat-radar.radar-event-cancelled {
text-decoration: line-through;
}
.squat-radar .squat-radar-title {
line-height: 1;
font-size: 1.4em;
margin-bottom: 0.2em;
}
.squat-radar .squat-radar-datetime {
font-weight: bold;
}
.squat-radar .squat-radar-datetime.squat-radar-datetime-start::before {
content: '';
display: block;
float: none;
clear: both;
}
.squat-radar .squat-radar-event-start-end::after,
.squat-radar .squat-radar-event-start {
content: '';
display: block;
float: none;
clear: both;
}
.squat-radar .squat-radar-list li {
display: inline;
list-style: none;
}
.squat-radar .squat-radar-list li:after {
content: ", ";
}
.squat-radar .squat-radar-list li:last-child:after {
content: "";
}
.squat-radar a.squat-radar-url-more {
padding: .4180469716em 1.11575em;
margin-left: 10px;
/* margin-bottom: 10px;*/
float: right;
}
.squat-radar a.squat-radar-url-more:hover {
color: white !important;
}
.squat_radar a.squat-radar-url-more:focus {
outline: 0;
}
jQuery(function($){
$(".squat-radar-widget.squat-radar-ajax").each(function(index, widget) {
$.ajax({
url: squat_radar_widget.ajaxurl,
context: document.body,
type: 'POST',
data: {
action: "squat_radar_events",
instance: window[widget.id],
},
success: function(result){
if (result.is_error) {
if (result.error) {
$(widget).append(
'<p>Error: ' + result.error.code + ' - ' + result.error.message + '</p>'
);
}
} else {
$(widget).empty();
$(widget).append(result.html);
// $(".squat-li a").on('click', function(e){
// if (this.hasAttribute("data-link")) {
// e.preventDefault();
// $(this).parent().find('p').show();
// this.textContent = this.getAttribute('data-nog-meer');
// this.setAttribute('href', this.getAttribute('data-link'));
// this.removeAttribute('data-link');
// }
//
// });
}
},
error: function(res){
console.log('AJAX error', res);
}
});
});
});
{
"name": "radar-wp/squat-radar",
"description": "Wordpress Calendar widget for https://radar.squat.net/ events",
"type": "wordpress-plugin",
"homepage": "https://radar.squat.net",
"license": "GPL-2.0+",
"authors": [ {
"name": "Radar contributors",
"homepage": "https://0xacab.org/radar"
} ],
"require": {
"php": ">=5.4.0"
}
}
<?php
/**
* Squat Radar Connector.
*
* Fetch data from Radar API.
*
* @package squat-radar
* @since 2.0.0
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
class Squat_Radar_Connector {
const BASE_URL = 'https://radar.squat.net';
const API_EVENTS = '/api/1.2/search/events.json';
/**
* Retrieve array of events from API based on a query.
*
* @see self::encode_api_query() for $query values.
*
* @param array $query
* Key value pairs for the API query.
*
* @return array
* Array of events.
*
* @throws Squat_Radar_Connector_Exception
* When events are not returned, but a timeout or API error.
*/
function get_events( $query ) {
$url = self::BASE_URL . self::API_EVENTS . '?' . build_query( $query );
$response = wp_remote_get( $url );
if ( is_wp_error( $response ) ) {
throw new Squat_Radar_Connector_Exception( $response->get_error_message() );
}
$code = wp_remote_retrieve_response_code( $response );
if ( $code != 200) {
throw new Squat_Radar_Connector_Exception( wp_remote_retrieve_body( $response ), $code );
}
return json_decode( wp_remote_retrieve_body( $response ), true);
}
/**
* Turn a Radar frontend Search URL into facets key value and language values.
*
* @param string $url
* The https://radar.squat.net/events filtered URL.
*
* @return array
* [ 'language' => language code, 'facets' => [key => value] ].
*/
function decode_search_url( $url ) {
$matches = [];
$result = [];
// Urldecode not required here because of the regex match.
// Radar paramaters here are transcoded so will match.
if (preg_match('|//radar.squat.net/([a-z]{2})/events/([a-zA-Z0-9\-/]*)|', $url, $matches)) {
$result['language'] = $matches[1];
foreach (array_chunk(explode('/', $matches[2]), 2) as $key_value_pair) {
$result['facets'][$key_value_pair[0]] = $key_value_pair[1];
}
}
return $result;
}
/**
* Encode a query key value from facets, fields, language, limit.
*
* @param array $facets
* Optional. Facet key => filter value array.
* @param array $fields
* Optional. Index array of API field names to retrieve.
* @param string $language
* Optional. Language code.
* @param int $limit
* Optional. Maximum number to items to return.
*
* @return array
* Array for use in self::get_events().
*/
function encode_api_query( $facets = [], $fields = [], $language = '', $limit = 10 ) {
$query = [];
// Urlencode should do nothing here @see comment in decode_search_url.
// If someone has snuck something in it will however help.
foreach ( $facets as $key => $value ) {
$query['facets[' . urlencode($key) . ']'][] = urlencode($value);
}
if ( ! empty($fields) ) {
// {raw}urlencode is encoding : and , both of which are valid pchar.
$query['fields'] = preg_replace('/[^a-z_:,]/', '', implode(',', $fields));
}
if ( ! empty($language) ) {
$query['language'] = urlencode($language);
}
if ( ! empty($limit) ) {
$query['limit'] = urlencode($limit);
}
return $query;
}
/**
* Return events meeting argument criteria. Either from cache, or retrieved from API.
*
* @param array $facets
* Facet name key => filter value.
* @param array $fields
* Optional. Array of key names.
* @param string $language
* Optional. Language code.
* @param int $limit
* Maximum number of items to return.
* @param int $expiration
* Seconds to cache results. 0 never expires.
* @param bool $reset
* Force a cache reset.
*
* @return array
* Array of event arrays, values keyed by field name.
*/
function events( $facets, $fields = [], $language = NULL, $limit = 10, $expiration = 10800, $reset = FALSE ) {
// Fields we often want to get data out of but not necessarily are chosen to be shown.
$fields = array_merge($fields, ['uuid', 'title', 'body:value', 'url', 'event_status']);
$transient_key = 'squat_radar_events_' . sha1(implode($facets) . implode($fields) . $language . $limit);
if (! $reset && $data = get_transient( $transient_key )) {
return $data;
}
$query = $this->encode_api_query( $facets, $fields, $language, $limit );
$events = $this->get_events($query);
set_transient( $transient_key, $events, $expiration );
return $events;
}
}
class Squat_Radar_Connector_Exception extends Exception { }
<?php
/**
* Squat Radar Events Formatter.
*
* Provides filters to format the output of Squat Radar Events.
*
* 'squat_radar_format_event' allows the whole event to be formatted.
* Default filter Squat_Radar_Formatter::format_event().
*
* 'squat_radar_field_html' formats individual fields.
* Basic implementation Squat_Radar_Formatter::field_html().
*
* @package squat-radar
* @since 2.0.0
*/
// Exit if accessed directly.
defined( 'ABSPATH' ) || exit;
class Squat_Radar_Formatter {
/**
* Register filters with Wordpress.
*/
static public function register() {
// Filter to go through fields and then call filters to turn these into HTML.
add_filter('squat_radar_format_event', [__CLASS__, 'format_event'], 10, 3);
// Filters to turn each individual field into HTML.
//
// $value is the data from the field and can be an array or string.
//
// These filters extract data from arrays based on the field structure.
// If you make a change it is a requirement to sanitize
// anything that will be output.
add_filter('squat_radar_field_html', [__CLASS__, 'field_date_html'], 5, 4);
add_filter('squat_radar_field_html', [__CLASS__, 'field_location_html'], 5, 4);
add_filter('squat_radar_field_html', [__CLASS__, 'field_link_html'], 5, 4);
add_filter('squat_radar_field_html', [__CLASS__, 'field_summary_html'], 5, 4);
// Field 'url' was already turned into a <a> link, by field_link_html.
// The field_image_html is an example of an override with more specificity.
add_filter('squat_radar_field_html', [__CLASS__, 'field_image_html'], 7, 4);
// If $value is an array it is flattened into a string here.
// If $value != $original it will _not_ be sanitized, assumption is that it has been already.
add_filter('squat_radar_field_html', [__CLASS__, 'field_html'], 10, 4);
// $value is always a string from this point.
// These filters just add additional wrapper markup.
add_filter('squat_radar_field_html', [__CLASS__, 'field_title_html'], 15, 4);
}
/**
* Implementation of 'squat_radar_format_event'.
*
* Formats an API event array into HTML.
*
* @param array $event
* The event array from the API. Nested field names with values.
* @param array $fields
* The field names required for display. Colons used to denote nesting.
* @param array $context
*
* @return string
* HTML.
*/
static public function format_event($event, $fields, $context) {
$context['event'] = $event;
$output = [];
$event_status = self::getValue( $event, ['event_status'] );
$output[] = '<div class="squat-radar radar-event radar-event-' . $event_status . '">';
foreach ($fields as $field) {
$field_tree = explode(':', $field);
$value = self::getValue($event, $field_tree);
$field_tree = array_reverse($field_tree);
$output[] = apply_filters('squat_radar_field_html', $value, $value, $field_tree, $context);
}
$output[] = '</div>';
return $output;
}
/**
* Basic implementation of 'squat_radar_field_html' filter.
*
* Put the output into HTML.
*
* @param array|string $value
* The field value being manipulated to become HTML to be displayed.
* @param array|string $original
* The original value of the field before any changes by filters.
* @param array $field
* The field tree. $field[0] being the name of the present field. $field[1]
* being any parent etc.
* @param array $context
*
* @return string
* Flattend array with additional default classes.
*/
static public function field_html($value, $original, $field, $context) {
if ($value != $original) {
return $value;
}
if (is_array($value)) {
if ( ! empty($value['value']) ) {
$value = $value['value'];
}
elseif ( ! empty($value['title']) ) {
$value = $value['title'];
}
elseif ( ! empty($value['name']) ) {
$value = $value['name'];
}
elseif ( ! empty($value[0]['value']) ) {
foreach ($value as $row) {
$values[] = $row['value'];
}
$value = $values;
}
elseif ( ! empty($value[0]['title']) ) {
foreach ($value as $row) {
$titles[] = $row['title'];
}
$value = $titles;
}
elseif ( ! empty($value[0]['name']) ) {
foreach ($value as $row) {
$names[] = $row['name'];
}
$value = $names;
}
}
if (is_array($value)) {
$output = '<ul class="squat-radar-' . sanitize_html_class($field[0]) . ' squat-radar-list">';
foreach ($value as $row) {
$output .= '<li class="squat-radar-item-' . sanitize_html_class($field[0]) . '">' . sanitize_text_field( $row ) . '</li>';
}
$output .= '</ul>';
return $output;
}
else {
$value = '<span class="squat-radar-' . sanitize_html_class($field[0]) . '">' . wp_kses_post( $value ) . '</span>';
}
return $value;
}
/**
* Date field formatting implementation of 'squat_radar_field_html' filter.
*/
static public function field_date_html($value, $original, $field, $context) {
switch ($field[0]) {
case 'created':
case 'updated':
$output = '';
if ($value) {
$output = date_i18n( get_option( 'date_format' ), $value );
$placeholder = ($field[0] == 'created') ? __('Created: %s', 'squat-radar') : __('Updated: %s', 'squat-radar');
$output = '<span class="squat-radar-meta-data squat-radar-' . $field[0] . '">' . sprintf($placeholder, $output) . '</span>';
}
return $output;
// "date_time": [
// {
// "value": "1556442000",
// "value2": "1556442000",
// "duration": 0,
// "time_start": "2019-04-28T11:00:00+02:00",
// "time_end": "2019-04-28T11:00:00+02:00",
// "rrule": null
// }
// ],
case 'date_time':
$output = '';
// There can only be one date. With repeat etc. but just one.
// Repeating events will appear as a new item for each repeat in the feed.
$value = $value[0];
$output = '<span class="squat-radar-event-start-end">';
$output .= self::field_date_format( $value['time_start'], 'start' );
if ($value['time_start'] != $value['time_end']) {
$time_only = ( substr($value['time_start'], 0, 10) == substr($value['time_end'], 0, 10) );
$output .= ' - ' . self::field_date_format( $value['time_end'], 'end', $time_only );
}
$output .= '</span>';
return $output;
case 'time_start':
$value = $value[0];
$output = '<span class="squat-radar-event-start">';
$output .= self::field_date_format($value, 'start');
$output .= '</span>';
return $output;
case 'time_end':
$value = $value[0];
$output = '<span class="squat-radar-event-end">';
$output .= self::field_date_format($value, 'end');
$output .= '</span>';
return $output;
}
return $value;
}
private static function field_date_format($time, $start_end, $time_only = FALSE) {
$date_format = get_option('squat_radar_date_format', 'j M Y');
$time_format = get_option('squat_radar_time_format', 'H:i');
// Remove offset to stop time being converted to UTC.
$time = substr($time, 0, -6);
$output = '<span class="squat-radar-datetime squat-radar-datetime-' . $start_end .'">';
if ( ! $time_only ) {
$output .= '<span class="squat-radar-date">';
$output .= date_i18n($date_format, strtotime($time));
$output .= '</span> ';
}
$output .= '<span class="squat-radar-time">';
$output .= date_i18n($time_format, strtotime($time));
$output .= '</span></span>';
return $output;
}
/**
* Location field implementation of 'squat_radar_field_html' filter.
*
* "offline": [
* {
* "uri": "https://radar.squat.net/api/1.2/location/b5786379-da49-4026-8c4e-bcc1a1563284",
* "id": "b5786379-da49-4026-8c4e-bcc1a1563284",
* "resource": "location",
* "title": "Yorck-Kino Yorckstr. 86 Berlin Deutschland",
* "map": {
* "geom": "POINT (13.3853499 52.4930248)",
* "geo_type": "point",
* "lat": "52.493024800000",
* "lon": "13.385349900000",
* "left": "13.385349900000",
* "top": "52.493024800000",
* "right": "13.385349900000",
* "bottom": "52.493024800000",
* "srid": null,
* "latlon": "52.493024800000,13.385349900000",
* "schemaorg_shape": ""
* }
* }
* ]
*/
static public function field_location_html($value, $original, $field, $context) {
switch ($field[0]) {
case 'map':
$output = [];
foreach ($value as $map) {
if ( is_array($map) && ! empty($map['lat']) && $map['lat'] !== NULL && $map['lon'] !== NULL ) {
$this_output = '<span class="squat-radar-location squat-radar-location-map-link">';
$lat = $map['lat'];
$lon = $map['lon'];
$this_output .= "<a href=\"https://www.openstreetmap.org/?mlat=$lat&mlon=$lon#map=14/$lat/$lon\" target=\"_blank\">";
$this_output .= __('[Map]', 'squat-radar');
$this_output .= '</a></span>';
$output[] = $this_output;
}
}
return implode(', ', $output);
case 'address':
$output = [];
foreach ($value as $address) {
if ( is_array($address) ) {
$this_address = [];
foreach (['name_line', 'thoroughfare', 'locality', 'postal_code', 'country'] as $field_name) {
if (! empty($address[$field_name])) {
$this_line = '<span class="squat-radar-location-' . $field_name . '">';
$this_line .= sanitize_text_field($address[$field_name]);
$this_line .= '</span>';
$this_address[] = $this_line;
}
}
$this_output = '<span class="squat-radar-location squat-radar-location-address">';
$this_output .= implode(', ', $this_address);
$this_output .= '</span>';
$output[] = $this_output;
}
}
return implode('; ', $output);
}
return $value;
}
/**
* Item Radar links implementation of 'squat_radar_field_html' filter.
*/
static public function field_link_html($value, $original, $field, $context) {
if ( ($field[0] == 'title' || $field[0] == 'title_field') && ! empty($context['event']['url'])) {
return '<a href="' . esc_url($context['event']['url']) . '" class="squat-radar-url squat-radar-url-title">' . sanitize_text_field( $value ) . '</a>';
}
if ($field[0] == 'url' && count($field) == 1) {
return '<a href="' . esc_url_raw($value) . '" class="squat-radar-url squat-radar-url-more">' . __('more…', 'squat-radar') . '</a>';
}
elseif ($field[0] == 'url') {
$title = esc_url($value);
array_shift($field);
if (is_array($field)) {
$field_tree = array_reverse($field);
$sibling_fields = self::getValue($context['event'], $field_tree);
$class = 'squat-radar-url-link';
if (! empty($sibling_fields['title']) ) {
$title = sanitize_text_field( $sibling_fields['title']);
$class = 'squat-radar-url-title';
}
elseif ( ! empty($sibling_fields['name']) ) {
$title = sanitize_text_field( $sibling_fields['name']);
$class = 'squat-radar-url-name';
}