*/ function pinfesta_defaults() { return array( 'comuni' => '', 'regione' => '', 'categoria' => '', 'limite' => '5', 'formato' => 'lista', 'tema' => 'chiaro', 'colore' => '', 'titolo' => '', 'altezza' => '320', 'dal' => '', 'al' => '', 'cerca' => '', 'font' => '', 'lingua' => '', ); } /** * Categories accepted by the service. Anything else is dropped instead of * being forwarded, so a typo shows every event rather than none. * * @return string[] */ function pinfesta_categorie() { return array( 'religioso', 'sagra_enogastronomica', 'musica_concerto', 'culturale_mostra', 'incontro_conferenza', 'fiera_mercato', 'mercatini', 'sportivo', 'natura_escursione', 'spettacolo', 'teatro', 'cinema', 'bambini_famiglie', 'istituzionale', 'altro', ); } /** * Cleans a comma separated list of town identifiers (the slug you see in the * address of a town page, for instance "la-morra"). * * The hard limit is the service's, not ours: twelve towns on the free plan, * fifty with a key. Here we only cut at the higher of the two, so a site with * a key is not trimmed by the plugin; the extra towns are simply ignored by * the service when the key is missing. * * @param string $raw Raw attribute value. * @return string Normalised list, empty when nothing survives. */ function pinfesta_clean_comuni( $raw ) { $out = array(); foreach ( explode( ',', (string) $raw ) as $item ) { $slug = sanitize_title( trim( $item ) ); if ( '' !== $slug ) { $out[] = $slug; } } return implode( ',', array_slice( array_unique( $out ), 0, 50 ) ); } /** * Turns the parameters into the URL of the widget page on the service. * * @param array $args Parameters, already merged with the defaults. * @return string */ function pinfesta_build_url( $args ) { $query = array(); $comuni = pinfesta_clean_comuni( $args['comuni'] ); if ( '' !== $comuni ) { $query['comune'] = $comuni; } elseif ( '' !== $args['regione'] ) { $query['regione'] = sanitize_text_field( $args['regione'] ); } // One category or several, comma separated. Unknown ones are dropped, so a // typo widens the widget instead of emptying it. $categorie = array(); foreach ( explode( ',', (string) $args['categoria'] ) as $item ) { $item = trim( $item ); if ( in_array( $item, pinfesta_categorie(), true ) ) { $categorie[] = $item; } } if ( $categorie ) { $query['categoria'] = implode( ',', array_unique( $categorie ) ); } // Period: only events that start between these two dates (YYYY-MM-DD), for // a page about Christmas or the summer. Anything that is not a date is // ignored. foreach ( array( 'dal', 'al' ) as $chiave_data ) { $data = trim( (string) $args[ $chiave_data ] ); if ( preg_match( '/^\d{4}-\d{2}-\d{2}$/', $data ) ) { $query[ $chiave_data ] = $data; } } // A word in the title, for instance "natale" inside the markets. $cerca = sanitize_text_field( $args['cerca'] ); if ( mb_strlen( $cerca ) >= 3 ) { $query['cerca'] = mb_substr( $cerca, 0, 40 ); } // Language of the widget: event texts (when translated), labels and links. // Empty means "follow the site": the current locale, which multilingual // plugins such as WPML or Polylang switch page by page. The service speaks // Italian, English, German and French; any other language gets English, // which a foreign visitor reads better than Italian. $lingua = sanitize_key( (string) $args['lingua'] ); if ( '' === $lingua ) { $lingua = substr( function_exists( 'determine_locale' ) ? determine_locale() : get_locale(), 0, 2 ); } if ( ! in_array( $lingua, array( 'it', 'en', 'de', 'fr' ), true ) ) { $lingua = 'en'; } if ( 'it' !== $lingua ) { $query['lingua'] = $lingua; } // The widget lives in an iframe and cannot inherit the site's font: the // service offers a few safe stacks to pick from. if ( in_array( $args['font'], array( 'helvetica', 'serif' ), true ) ) { $query['font'] = $args['font']; } $limite = absint( $args['limite'] ); if ( $limite > 0 ) { $query['limite'] = min( $limite, 20 ); } // Elenco, schede con la locandina, o carosello che scorre da solo. if ( in_array( $args['formato'], array( 'schede', 'carosello' ), true ) ) { $query['formato'] = $args['formato']; } if ( 'scuro' === $args['tema'] ) { $query['tema'] = 'scuro'; } $colore = sanitize_hex_color( $args['colore'] ); if ( $colore ) { // Without the hash: add_query_arg() leaves "#" as it is, and a bare "#" // in a URL opens the fragment, which would swallow this parameter and // every parameter after it. The service accepts the six digits alone. $query['accent'] = ltrim( $colore, '#' ); } $titolo = sanitize_text_field( $args['titolo'] ); if ( '' !== $titolo ) { $query['titolo'] = $titolo; } // Service key: optional. It only tells the service that this site may ask // for the version without the Piazza in Festa credit line; every feature of // this plugin works exactly the same with or without it. $chiave = pinfesta_get_option( 'chiave' ); if ( '' !== $chiave ) { $query['chiave'] = $chiave; $query['marchio'] = '0'; } return add_query_arg( $query, pinfesta_base_url() . '/embed' ); } /** * Renders the widget. * * An iframe, on purpose: the CSS of the host site cannot break the widget and * the widget cannot break the host site. The height arrives from the framed * page via postMessage (assets/js/resize.js), so nobody has to guess it; the * `altezza` parameter is only the starting height, and what visitors see when * JavaScript is off. * * @param array $args Parameters, already merged with the defaults. * @return string HTML. */ function pinfesta_render_iframe( $args ) { $args = wp_parse_args( $args, pinfesta_defaults() ); if ( '' === pinfesta_clean_comuni( $args['comuni'] ) && '' === trim( (string) $args['regione'] ) ) { if ( ! current_user_can( 'edit_posts' ) ) { return ''; } return '
' . esc_html__( 'Piazza in Festa: set at least one town or a region.', 'piazza-in-festa' ) . '
'; } wp_enqueue_style( 'pinfesta-widget' ); wp_enqueue_script( 'pinfesta-resize' ); $altezza = absint( $args['altezza'] ); $altezza = ( $altezza >= 120 && $altezza <= 4000 ) ? $altezza : 320; return sprintf( '', esc_url( pinfesta_build_url( $args ) ), esc_attr__( 'Upcoming local events', 'piazza-in-festa' ), $altezza ); }