Changeset 1398584
- Timestamp:
- 04/18/2016 08:20:30 PM (10 years ago)
- Location:
- fareharbor
- Files:
-
- 8 edited
-
assets/banner-1544x500.jpg (modified) (previous)
-
assets/banner-772x250.jpg (modified) (previous)
-
assets/icon-128x128.png (modified) (previous)
-
assets/icon-256x256.png (modified) (previous)
-
assets/screenshot-1.png (modified) (previous)
-
assets/screenshot-2.png (modified) (previous)
-
trunk/fareharbor.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
fareharbor/trunk/fareharbor.php
r1261996 r1398584 1 1 <?php 2 2 /* 3 Plugin Name: FareHarbor Reservation Calendars3 Plugin Name: FareHarbor 4 4 Plugin URI: https://fareharbor.com/help/setup/wordpress-plugin/ 5 Description: Adds shortcodes for adding FareHarbor embeds to your site6 Version: 2.15 Description: Easily add FareHarbor reservation calendars and buttons to your site 6 Version: 3.0 7 7 Author: FareHarbor 8 8 Author URI: https://fareharbor.com 9 9 */ 10 11 defined('ABSPATH') or die("What are you looking at?"); 12 13 add_shortcode("fareharbor", "fh_shortcode"); 14 add_shortcode("lightframe", "lightframe_shortcode"); 15 add_shortcode("partners", "partners_shortcode"); 16 17 // Defaults 18 19 DEFINE('FH_SHORTNAME', ''); 20 DEFINE('FH_EMBED_TYPE', 'calendar-small'); 21 DEFINE('FH_ITEMS', ''); 22 DEFINE('FH_LIGHTFRAME', 'yes'); 23 DEFINE('FH_ASN', ''); 24 DEFINE('FH_ASN_REF', ''); 25 DEFINE('FH_REF', ''); 26 DEFINE('FH_CLASS', ''); 27 DEFINE('FH_ID', ''); 28 29 DEFINE('FH_FULL_ITEMS', 'no'); 30 DEFINE('FH_API_VIEW', 'items'); 31 DEFINE('FH_API_VIEW_ITEM', ''); 32 DEFINE('FH_API_VIEW_AVAILABILITY', ''); 33 34 DEFINE('FH_PARTNERS_INCLUDE', ''); 35 36 // Process the info returned from a shortcode 37 // --------------------------------------------- 38 39 function fh_sanitize_csv( $value ) { 40 $value = str_replace(" ", "", $value); 41 $value = rtrim($value, ","); 42 return $value; 10 11 // Make sure this file isn't loaded on its own 12 // ----------------------------------------------- 13 14 defined( 'ABSPATH' ) or die( 'Lost? <a href="/">Return home.</a>' ); 15 16 17 // Register the shortcodes 18 // ----------------------------------------------- 19 20 add_shortcode( 'fareharbor', array( 'fareharbor', 'fareharbor_shortcode' ) ); 21 add_shortcode( 'lightframe', array( 'fareharbor', 'lightframe_shortcode' ) ); 22 add_shortcode( 'partners', array( 'fareharbor', 'partners_shortcode' ) ); 23 add_shortcode( 'itemgrid', array( 'fareharbor', 'itemgrid_shortcode' ) ); 24 // add_shortcode( 'bookembed', array( 'fareharbor', 'bookembed_shortcode' ) ); 25 26 // Register our Settings Page & Settings 27 // ----------------------------------------------- 28 29 add_action( 'admin_menu', array( 'fareharbor', 'register_options_page' ) ); 30 add_action( 'admin_init', array( 'fareharbor', 'register_settings' ) ); 31 32 33 // Include the script in the footer 34 // ----------------------------------------------- 35 36 add_action( 'wp_footer', array( 'fareharbor', 'lightframe_api_footer' ) ); 37 38 39 // Maybe include fh-kit styles in header 40 // ----------------------------------------------- 41 // Depends on option being set in the admin dashboard 42 add_action( 'wp_enqueue_scripts', array( 'fareharbor', 'maybe_enqueue_fh_kit_styles' ) ); 43 44 45 // Global functions for potential backwards compatability 46 // ----------------------------------------------- 47 48 function fh_shortcode( $attrs ) { 49 return fareharbor::fareharbor_shortcode( $attrs ); 43 50 } 44 51 45 function fh_process_attrs( $attrs ) { 46 if( is_array($attrs) ) { 47 48 // Trim whitespace 49 50 $attrs = array_map('trim', $attrs); 51 52 function lightframe_shortcode( $attrs, $content = '' ) { 53 return fareharbor::lightframe_shortcode( $attrs, $content ); 54 } 55 56 function partners_shortcode( $attrs ) { 57 return fareharbor::partners_shortcode( $attrs ); 58 } 59 60 61 // The important stuff 62 // ----------------------------------------------- 63 64 final class fareharbor { 65 66 // This class is just a namespace for static methods & variables 67 private function __construct() {} 68 69 70 // Script source 71 // =============================================== 72 73 public static function lightframe_api_footer() { 74 75 echo '<!-- FareHarbor plugin activated --><script src="https://' . self::url() . '/embeds/api/v1/"></script>'; 76 77 } 78 79 80 // FH-Kit Styles 81 // =============================================== 82 83 public static function maybe_enqueue_fh_kit_styles() { 84 85 if ( self::is_lite() ) 86 return; 87 88 if( !self::get_option( 'fh_buttons_active' ) ) 89 return; 90 91 $query_string = self::get_option( 'fh_buttons_query' ); 92 93 $query_string = strip_tags( $query_string ); 94 $query_string = trim( $query_string ); 95 $query_string = ltrim( $query_string, '?' ); 96 97 $fh_kit_src = 'https://fh-kit.com/buttons/v1/'; 98 99 if ( $query_string ) 100 $fh_kit_src .= '?' . $query_string; 101 102 $fh_kit_src = apply_filters( 'fareharbor/buttons_style_src', $fh_kit_src ); 103 104 // the 4th parameter is $version. Leaving it out causes WP to 105 // add ver={current-wp-version} to the url. passing null stops this. 106 wp_enqueue_style( 'fh-buttons', $fh_kit_src, array(), null ); 107 108 } 109 110 111 // Default Arguments 112 // =============================================== 113 114 private static $shared_defaults, 115 $lightframe_defaults, 116 $calendar_defaults, 117 $partners_defaults, 118 $itemgrid_defaults, 119 $bookembed_defaults; 120 121 private static function init_defaults() { 122 123 if ( isset( self::$shared_defaults ) ) 124 return; 125 126 self::$shared_defaults = array( 127 128 'shortname' => self::get_option( 'fh_default_shortname', '' ), 129 'items' => '', 130 'asn' => self::get_option( 'fh_default_asn', '' ), 131 'asn_ref' => self::get_option( 'fh_default_asn_ref', '' ), 132 'ref' => self::get_option( 'fh_default_ref', '' ), 133 'lightframe' => self::get_option( 'fh_default_lightframe', 'yes' ), 134 'full_items' => self::get_option( 'fh_default_full_items', 'no' ), 135 'sheet' => self::get_option( 'fh_default_sheet', '' ), 136 'fallback' => self::get_option( 'fh_default_fallback', '' ), 137 138 ); 139 140 self::$bookembed_defaults = 141 self::$lightframe_defaults = array( 142 143 // 'class' => '', 144 // 'style' => '', 145 // 'id' => '', 146 'view' => 'items', 147 'view_item' => '', 148 'view_availability' => '', 149 150 ); 151 self::$bookembed_defaults[ 'fallback' ] = ''; 152 153 self::$calendar_defaults = array( 154 155 'type' => 'calendar-small', 156 157 ); 158 159 self::$partners_defaults = array( 160 161 'include' => '', 162 163 ); 164 165 self::$itemgrid_defaults = array(); 166 167 } 168 169 170 // [lightframe][/lightframe] shortcode 171 // =============================================== 172 173 public static function lightframe_shortcode( $attrs, $content = '' ) { 174 175 $link_attrs = self::lightframe_link_attrs( $attrs ); 176 177 if ( $error = self::maybe_handle_shortcode_error( $link_attrs ) ) 178 return $error; 179 180 if ( !empty( $attrs[ 'class' ] ) ) 181 $link_attrs[ 'class' ] = $attrs[ 'class' ]; 182 elseif ( $default_class = self::get_option( 'fh_default_class' ) ) 183 $link_attrs[ 'class' ] = $default_class; 184 185 if ( !empty( $attrs[ 'id' ] ) ) 186 $link_attrs[ 'id' ] = $attrs[ 'id' ]; 187 188 if ( !empty( $attrs[ 'style' ] ) ) 189 $link_attrs[ 'style' ] = $attrs[ 'style' ]; 190 191 $link_attrs = array_map( 'strip_tags', $link_attrs ); 192 193 $link_attrs_string = ''; 194 foreach ( $link_attrs as $name => $value ) 195 $link_attrs_string .= " $name=\"$value\""; 196 197 if ( trim( $content ) ) 198 $content = do_shortcode( $content ); 199 200 return "<a$link_attrs_string>$content</a>"; 201 202 } 203 204 public static function lightframe_link_attrs( $args ) { 205 206 if ( !isset( self::$lightframe_defaults ) ) 207 self::init_defaults(); 208 209 $args = self::process_args( $args, self::$lightframe_defaults, 'lightframe' ); 210 211 if ( isset( $args[ 'error' ] ) ) 212 return $args; 213 214 if ( $args[ 'view_availability' ] && !$args[ 'view_item' ] ) 215 return array( 'error' => 'view_availability but no view_item' ); 216 217 $fallback_url = self::lightframe_fallback_url( $args ); 218 219 $api_options = self::lightframe_api_options( $args ); 220 $json = strtr( json_encode( $api_options ), '"', "'" ); 221 222 return array( 223 'href' => $fallback_url, 224 'target' => '_blank', 225 'onclick' => "FH.open($json); return false;", 226 ); 227 228 } 229 230 private static function lightframe_fallback_url( $args, $is_bookembed = false ) { 231 232 // The bookembed url structure is very similar to the simple fallback url structure 233 $is_simple_fallback = ( $args[ 'fallback' ] === 'simple' ) || $is_bookembed; 234 235 $url = 'https://' . self::url() . '/'; 236 237 if ( $is_bookembed ) 238 $url .= 'embeds/script/book/'; 239 elseif ( $is_simple_fallback ) 240 $url .= 'embeds/book/'; 241 242 $url .= $args[ 'shortname' ] . '/'; 243 244 if ( !$is_simple_fallback 245 || $args[ 'view' ] === 'all-availability' 246 || $args[ 'view_item' ] 247 ) { 248 $url .= 'items/'; 249 } 250 251 if ( $args[ 'view_item' ] ) { 252 253 $url .= $args[ 'view_item' ] . '/'; 254 255 if ( $args[ 'view_availability' ] ) 256 $url .= 'availability/' . $args[ 'view_availability' ] . '/book/'; 257 elseif ( $args[ 'full_items' ] === 'no' ) 258 $url .= 'calendar/'; 259 260 } elseif ( $args[ 'view' ] === 'all-availability' ) { 261 262 $url .= 'calendar/'; 263 264 } 265 266 $query = self::get_shared_args_array( $args, 'dash', $is_simple_fallback, false ); 267 268 if ( $is_simple_fallback && $args[ 'items' ] ) 269 $query[ 'selected-items' ] = $args[ 'items' ]; 270 271 if ( !$is_bookembed ) { 272 273 $current_url = get_home_url(); 274 if ( !$current_url ) { 275 $current_url = ( is_ssl() ? 'https' : 'http' ) . '://' 276 . ( !empty( $_SERVER[ 'HTTP_HOST' ] ) ? $_SERVER[ 'HTTP_HOST' ] : $_SERVER[ 'SERVER_NAME' ] ); 277 } 278 $current_url .= $_SERVER[ 'REQUEST_URI' ]; 279 280 if ( $is_simple_fallback ) 281 $query[ 'referrer' ] = $current_url; 282 283 } 284 285 if ( $query ) 286 $url .= '?' . http_build_query( $query ); 287 288 return $url; 289 290 } 291 292 private static function lightframe_api_options( $args ) { 293 294 $lo = array( 'shortname' => $args[ 'shortname' ] ); 295 296 // Filter the visible items 297 if ( $args[ 'items' ] ) 298 // putting it in an array so it gets brackets in json_encode 299 $lo[ 'items' ] = explode( ',', $args[ 'items' ] ); 300 301 // Set the view 302 if ( $args[ 'view_item' ] ) { 303 304 $lo[ 'view' ] = array( 'item' => $args[ 'view_item' ] ); 305 306 if ( $args[ 'view_availability' ] ) 307 $lo[ 'view' ][ 'availability' ] = $args[ 'view_availability' ]; 308 309 } elseif ( in_array( $args[ 'view' ], array( 'items', 'all-availability' ), true ) ) { 310 311 $lo[ 'view' ] = $args[ 'view' ]; 312 313 } else { 314 315 $lo[ 'view' ] = 'items'; 316 317 } 318 319 // Modifiers: shared args like fallback, asn, asn_ref, etc. 320 $lo += self::get_shared_args_array( $args, 'camel', true, true ); 321 322 return $lo; 323 324 } 325 326 327 // [bookembed] shortcode 328 // =============================================== 329 330 public static function bookembed_shortcode( $attrs ) { 331 332 $script_src = self::bookembed_script_src( $attrs ); 333 334 $maybe_error = self::maybe_handle_shortcode_error( $script_src ); 335 return $maybe_error ? $maybe_error : "<script src=\"$script_src\"></script>"; 336 337 } 338 339 public static function bookembed_script_src( $args ) { 340 341 if ( !isset( self::$bookembed_defaults ) ) 342 self::init_defaults(); 343 344 $args = self::process_args( $args, self::$bookembed_defaults, 'bookembed' ); 345 346 if ( isset( $args[ 'error' ] ) ) 347 return $args; 348 349 if ( $args[ 'view_availability' ] && !$args[ 'view_item' ] ) 350 return array( 'error' => 'view_availability but no view_item' ); 351 352 return self::lightframe_fallback_url( $args, true ); 353 354 } 355 356 357 // [fareharbor] shortcode (calendar) 358 // =============================================== 359 360 public static function fareharbor_shortcode( $attrs ) { 361 362 $script_src = self::calendar_script_src( $attrs ); 363 364 $maybe_error = self::maybe_handle_shortcode_error( $script_src ); 365 return $maybe_error ? $maybe_error : "<script src=\"$script_src\"></script>"; 366 367 } 368 369 public static function calendar_script_src( $args ) { 370 371 if ( !isset( self::$calendar_defaults ) ) 372 self::init_defaults(); 373 374 $args = self::process_args( $args, self::$calendar_defaults, 'fareharbor' ); 375 376 if ( isset( $args[ 'error' ] ) ) 377 return $args; 378 379 $type = $args[ 'type' ]; 380 if ( $type === 'small' ) 381 $type = 'calendar-small'; 382 elseif ( $type === 'large' ) 383 $type = 'calendar'; 384 385 $url_suffix = ''; 386 if ( $args[ 'items' ] ) 387 $url_suffix = 'items/' . $args[ 'items' ] . '/'; 388 389 return self::embed_script_src( $type, $args, $url_suffix, array(), false ); 390 391 } 392 393 394 // [partners] shortcode 395 // =============================================== 396 397 public static function partners_shortcode( $attrs ) { 398 399 $script_src = self::partners_script_src( $attrs ); 400 401 $maybe_error = self::maybe_handle_shortcode_error( $script_src ); 402 return $maybe_error ? $maybe_error : "<script src=\"$script_src\"></script>"; 403 404 } 405 406 public static function partners_script_src( $args ) { 407 408 if ( !isset( self::$partners_defaults ) ) 409 self::init_defaults(); 410 411 $args = self::process_args( $args, self::$partners_defaults, 'partners' ); 412 413 if ( isset( $args[ 'error' ] ) ) 414 return $args; 415 416 $query = array(); 417 if ( $args[ 'include' ] ) 418 $query[ 'include' ] = $args[ 'include' ]; 419 420 return self::embed_script_src( 'partners', $args, '', $query, true ); 421 422 } 423 424 425 // [items] shortcode 426 // =============================================== 427 428 public static function itemgrid_shortcode( $attrs ) { 429 430 $script_src = self::itemgrid_script_src( $attrs ); 431 432 $maybe_error = self::maybe_handle_shortcode_error( $script_src ); 433 return $maybe_error ? $maybe_error : "<script src=\"$script_src\"></script>"; 434 435 } 436 437 438 public static function itemgrid_script_src( $args ) { 439 440 if ( !isset( self::$itemgrid_defaults ) ) 441 self::init_defaults(); 442 443 $args = self::process_args( $args, self::$itemgrid_defaults, 'itemgrid' ); 444 445 if ( isset( $args[ 'error' ] ) ) 446 return $args; 447 448 $query = array(); 449 if ( $args[ 'items' ] ) 450 $query[ 'selected-items' ] = $args[ 'items' ]; 451 452 return self::embed_script_src( 'items', $args, $url_suffix, $query, true ); 453 454 } 455 456 457 // Shared Shortcode Functionality 458 // =============================================== 459 460 // Handling Argument Input 461 // ----------------------------------------------- 462 463 private static function process_args( $args, $defaults, $shortcode_name ) { 464 465 if ( !isset( self::$shared_defaults ) ) 466 self::init_defaults(); 467 468 $defaults += self::$shared_defaults; 469 470 if ( !is_array( $args ) ) // Just in case. 471 $args = $defaults; 472 473 // Trim whitespace && Strip Tags 474 $args = array_map( array( __CLASS__, 'sanitize' ), $args ); 475 52 476 // Strip smart quotes, because WordPress returns them as part of the value if the shortcode was set up using them 53 54 $attrs = str_replace( 55 array("\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", chr(145), chr(146), chr(147), chr(148)), 56 array('', '', '', '', '', '', '', ''), 57 $attrs); 58 59 // Process options and assign defaults if needed 60 61 $attrs = shortcode_atts(array( 62 "shortname" => FH_SHORTNAME, 63 "items" => FH_ITEMS, 64 "asn" => FH_ASN, 65 "asn_ref" => FH_ASN_REF, 66 "ref" => FH_REF, 67 "lightframe" => FH_LIGHTFRAME, 68 "full_items" => FH_FULL_ITEMS, 69 "sheet" => '', 70 71 // [fareharbor] only 72 73 "type" => FH_EMBED_TYPE, 74 "lightframe" => FH_LIGHTFRAME, 75 76 // [lightframe] only 77 78 "class" => FH_CLASS, 79 "id" => FH_ID, 80 "view" => FH_API_VIEW, 81 "view_item" => FH_API_VIEW_ITEM, 82 "view_availability" => FH_API_VIEW_AVAILABILITY, 83 84 // [partners] only 85 86 "include" => FH_PARTNERS_INCLUDE 87 88 ), $attrs); 89 477 $args = str_replace( 478 array( "\xe2\x80\x98", "\xe2\x80\x99", "\xe2\x80\x9c", "\xe2\x80\x9d", chr(145), chr(146), chr(147), chr(148) ), 479 array( '', '', '', '', '', '', '', '' ), 480 $args 481 ); 482 483 // Merge in defaults 484 $args = shortcode_atts( $defaults, $args, $shortcode_name ); 485 486 // Confirm there's a shortname. All of our shortcodes require this 487 if ( !$args[ 'shortname' ] ) 488 return array( 'error' => 'no shortname' ); 489 490 // Shortnames only work if lowercase 491 $args[ 'shortname' ] = strtolower( $args[ 'shortname' ] ); 492 90 493 // Clean up item IDs and included companies because users can't be trusted 91 92 $attrs["items"] = fh_sanitize_csv( $attrs["items"] ); 93 $attrs["view_item"] = fh_sanitize_csv( $attrs["view_item"] ); 94 $attrs["include"] = fh_sanitize_csv( $attrs["include"] ); 95 96 return $attrs; 97 98 } 99 } 100 101 function fh_url() { 102 $env_url = defined('FH_ENVIRONMENT') ? FH_ENVIRONMENT . '.fareharbor.com' : 'fareharbor.com'; 103 return $env_url; 104 } 105 106 // [fareharbor] shortcode 107 // --------------------------------------------- 108 109 function fh_shortcode( $attrs ) { 110 111 $fh_options = fh_process_attrs( $attrs ); 112 113 $output = ''; 114 115 // Bail if a shortname isn't provided 116 117 if ( empty( $fh_options["shortname"] ) ) { 118 119 $output .= '<p>Please provide a FareHarbor shortname. (Format: <code>shortname="yourshortname"</code>)</p>'; 120 121 } else { 122 123 $output = '<script src="https://' . fh_url() . '/embeds/script/'; 124 125 // Types: Clean up "small" and "large" options, otherwise use passed type 126 127 switch ( $fh_options["type"] ) { 128 case "small": 129 $output .= "calendar-small"; 130 break; 131 case "large": 132 $output .= "calendar"; 133 break; 134 default: 135 $output .= $fh_options["type"]; 136 } 137 138 $output .= '/'; 139 140 // Shortname 141 142 $output .= $fh_options["shortname"] . '/'; 143 144 // Items, if any were included 145 146 if ( !empty( $fh_options["items"] ) ) { 147 $output .= 'items/' . $fh_options["items"] . '/'; 148 } 149 150 // Build query string of options. "lightframe" is always included, with either yes or no. 151 152 $output .= '?'; 153 154 $fh_query_string_options = array('lightframe' => $fh_options["lightframe"]); 155 156 if ( !empty( $fh_options["asn"] ) ) { 157 $fh_query_string_options["asn"] = $fh_options["asn"]; 158 159 if( !empty( $fh_options["asn_ref"] ) ) { 160 $fh_query_string_options["asn-ref"] = $fh_options["asn_ref"]; 161 } 162 } 163 164 if ( !empty( $fh_options["ref"] ) ) { 165 $fh_query_string_options["ref"] = $fh_options["ref"]; 166 } 167 168 if ( !empty( $fh_options["sheet"] ) ) { 169 $fh_query_string_options["sheet"] = $fh_options["sheet"]; 170 } 171 172 $output .= http_build_query($fh_query_string_options); 173 174 175 $output .= '"></script>'; 176 } 177 178 return $output; 179 } 180 181 // [lightframe][/lightframe] shortcode 182 // --------------------------------------------- 183 184 function lightframe_shortcode( $attrs, $content = null ) { 185 186 $attrs = fh_process_attrs( $attrs ); 187 188 $output = ''; 189 190 if ( empty( $attrs["shortname"] ) ) { 191 192 $output .= '<p>Please provide a FareHarbor shortname. (Format: <code>shortname="yourshortname"</code>)</p>'; 193 194 } elseif ( empty( $attrs["view_item"] ) && !empty( $attrs["view_availability"] ) ) { 195 196 echo '<p>Please provide <code>view_item</code> if using <code>view_availability</code.</p>'; 197 198 } else { 199 200 // Fallback URL 201 // --------------------------------------------- 202 203 $fallback_url = 'https://' . fh_url() . '/'; 204 $fallback_url .= $attrs["shortname"] . '/items/'; 205 206 if( !empty( $attrs["items"] ) ) { 207 208 // If filtering but just to one item, link to it 209 210 $fallback_items = explode(',', $attrs["items"]); 211 212 if( count($fallback_items) == 1 ) { 213 $fallback_url .= $fallback_items[0] . '/'; 214 215 if( $attrs["full_items"] == 'no' ) { 216 $fallback_url .= 'calendar/'; 494 $csv_keys = array( 'items', 'view_item', 'include' ); 495 foreach ( $csv_keys as $key ) 496 if ( isset( $args[ $key ] ) ) 497 $args[ $key ] = self::sanitize_csv( $args[ $key ] ); 498 499 if ( $shortcode_name === 'lightframe' || $shortcode_name === 'bookembed' ) { 500 501 if ( !$args[ 'view_item' ] ) { 502 // See if we're filtering to just one item 503 // in that case we treat it as if it were 504 // passed to view_item instead 505 $items_array = explode( ',', $args[ 'items' ] ); 506 if ( count( $items_array ) === 1 ) { 507 $args[ 'view_item' ] = $items_array[0]; 508 $args[ 'items' ] = ''; 217 509 } 218 510 } 219 511 220 } else { 221 222 if( $attrs["view"] == 'all-availability' ) { 223 $fallback_url .= 'calendar/'; 224 } 225 226 if( !empty( $attrs["view_item"] ) ) { 227 $fallback_url .= $attrs["view_item"] . '/'; 512 } 513 514 return $args; 515 516 } 517 518 public static function sanitize( $value ) { 519 520 return trim( strip_tags( $value ) ); 521 522 } 523 524 private static function sanitize_csv( $value ) { 525 526 return rtrim( str_replace( ' ', '', $value ), ',' ); 527 528 } 529 530 531 // Handling Errors 532 // ----------------------------------------------- 533 534 private static $error_messages = array( 535 536 'no shortname' 537 => '<p>Please provide a FareHarbor shortname. (Format: <code>shortname="yourshortname"</code>)</p>', 538 'view_availability but no view_item' 539 => '<p>Please provide <code>view_item</code> if using <code>view_availability</code>.</p>', 540 541 ); 542 543 private static function maybe_handle_shortcode_error( $output ) { 544 545 if ( is_array( $output ) && !empty( $output[ 'error' ] ) ) { 546 547 return isset( self::$error_messages[ $output[ 'error' ] ] ) 548 ? self::$error_messages[ $output[ 'error' ] ] 549 : '<p>' . $output[ 'error' ] . '</p>'; 550 551 } 552 553 return false; 554 555 } 556 557 558 // Scripts for embeds (in use by [partners] and [fareharbor]) 559 // ----------------------------------------------- 560 561 private static function embed_script_src( $type, $args, $url_suffix, $query, $include_full_items ) { 562 563 $script_src = 'https://' . self::url() . '/embeds/script/' 564 . $type 565 . '/' 566 . $args[ 'shortname' ] 567 . '/' 568 . $url_suffix; 569 570 $args[ 'lightframe' ] = strtolower( trim( $args[ 'lightframe' ] ) ); 571 if ( $args[ 'lightframe' ] === 'no' ) 572 $query += array( 'lightframe' => $args[ 'lightframe' ] ); 573 574 $query += self::get_shared_args_array( $args, 'dash', $include_full_items, true ); 575 576 $query_string = http_build_query( $query ); 577 578 return $script_src . ( $query_string ? "?$query_string" : '' ); 579 580 } 581 582 583 // Shared arguments 584 // ----------------------------------------------- 585 586 private static function get_shared_args_array( $args, $casing, $include_full_items, $include_fallback ) { 587 588 $out = array(); 589 590 if ( $include_fallback && in_array( $args[ 'fallback' ], array( 'simple', 'classic' ) ) ) 591 $out[ 'fallback' ] = $args[ 'fallback' ]; 592 593 if ( $include_full_items && $args[ 'full_items' ] !== 'no' ) 594 $out[ 'full_items' ] = $args[ 'full_items' ]; 595 596 if ( $args[ 'asn' ] ) { 597 598 $out[ 'asn' ] = $args[ 'asn' ]; 599 600 if ( $args[ 'asn_ref' ] ) 601 $out[ 'asn_ref' ] = $args[ 'asn_ref' ]; 602 603 } 604 605 if ( $args[ 'ref' ] ) 606 $out[ 'ref' ] = $args[ 'ref' ]; 607 608 if ( $args[ 'sheet' ] ) 609 $out[ 'sheet' ] = $args[ 'sheet' ]; 610 611 return self::fix_array_key_casing( $out, $casing ); 612 613 } 614 615 private static function fix_array_key_casing( $in, $casing ) { 616 617 $out = array(); 618 foreach ( $in as $key => $value ) 619 $out[ self::fix_casing( $key, $casing ) ] = $value; 620 621 return $out; 622 623 } 624 625 private static function fix_casing( $text, $casing ) { 626 // Assumes $text is already in snake_case 627 switch ( $casing ) { 628 629 case 'camel': 630 631 // ucwords() doesn't support a $delimeters param 632 // until php 5.4.32/5.5.16 633 $text = strtr( $text, '_', ' ' ); 634 $text = ucwords( $text ); 635 $text = str_replace( ' ', '', $text ); 636 // lcfirst() doesn't exist until php 5.3.0 637 $text[0] = strtolower( $text[0] ); 638 return $text; 639 640 case 'dash': 641 642 return strtr( $text, '_', '-' ); 643 644 } 645 646 return $text; 647 648 } 649 650 651 // FareHarbor URL with optional FH_ENVIRONMENT 652 // ----------------------------------------------- 653 654 private static function url() { 655 656 $environment = ( defined( 'FH_ENVIRONMENT' ) && FH_ENVIRONMENT ) ? FH_ENVIRONMENT : ''; 657 $environment = apply_filters( 'fareharbor/environment', $environment ); 658 659 return trim( $environment ) 660 ? trim( $environment ) . '.fareharbor.com' 661 : 'fareharbor.com'; 662 663 } 664 665 666 // Settings & Settings Pages 667 // =============================================== 668 669 // Settings 670 // ----------------------------------------------- 671 672 private static function is_lite() { 673 // This filter allows fh-kit and the admin page 674 // to be turned off with: 675 // add_filter( 'fareharbor/lite', '__return_true' ); 676 return apply_filters( 'fareharbor/lite', false ); 677 678 } 679 680 private static $options; 681 682 private static function get_option( $option_name, $default = '', $filtered = true ) { 683 684 $filter_name = str_replace( 'fh_', '', $option_name ); 685 $filter_name = str_replace( 'default_', 'defaults/', $filter_name ); 686 $filter_name = str_replace( 'buttons_', 'buttons/', $filter_name ); 687 $filter_name = "fareharbor/$filter_name"; 688 689 if ( self::is_lite() ) 690 return $filtered ? apply_filters( $filter_name, $default ) : $default; 691 692 if ( !isset( self::$options ) ) 693 self::$options = get_option( 'fareharbor_settings' ); 694 695 $value = isset( self::$options[ $option_name ] ) 696 ? self::$options[ $option_name ] 697 : $default; 698 699 if ( $filtered ) 700 $value = apply_filters( $filter_name, $value ); 701 702 return $value; 703 704 } 705 706 707 // The settings page 708 // ----------------------------------------------- 709 710 public static function register_options_page() { 711 712 if ( self::is_lite() ) 713 return; 714 715 add_options_page( 716 'FareHarbor Plugin Settings', 717 'FareHarbor', 718 'manage_options', 719 __FILE__, 720 array( __CLASS__, 'render_options_page' ) 721 ); 722 723 } 724 725 public static function register_settings() { 726 727 if ( self::is_lite() ) 728 return; 729 730 register_setting( 731 'fareharbor_settings', 732 'fareharbor_settings', 733 array( __CLASS__, 'validate_settings' ) 734 ); 735 736 add_settings_section( 737 'fh_about_section', 738 'About', 739 array( __CLASS__, 'render_fh_about_section_text' ), 740 __FILE__ 741 ); 742 743 add_settings_section( 744 'fh_shortcode_defaults_section', 745 'Shortcode Defaults', 746 array( __CLASS__, 'render_fh_shortcode_defaults_section_text' ), 747 __FILE__ 748 ); 749 750 add_settings_field( 751 'fh_default_shortname', 752 'shortname', 753 array( __CLASS__, 'render_input_field' ), 754 __FILE__, 755 'fh_shortcode_defaults_section', 756 array( 757 'option_name' => 'fh_default_shortname', 758 'description' => 'Your company\'s FareHarbor shortname.', 759 ) 760 ); 761 762 add_settings_field( 763 'fh_default_fallback', 764 'fallback', 765 array( __CLASS__, 'render_select_field' ), 766 __FILE__, 767 'fh_shortcode_defaults_section', 768 array( 769 'option_name' => 'fh_default_fallback', 770 'choices' => array( 771 'simple', 772 'classic', 773 ), 774 'description' => 'On certain mobile and touch devices, the book form will open as a new page instead of in a Lightframe. Choose "simple" for this page to mirror the look and feel of the Lightframe. Choose "classic" to take the user to your FareHarbor shortname website (and to the appropriate item on that site, if applicable).', 775 ) 776 ); 777 778 add_settings_field( 779 'fh_default_asn', 780 'asn', 781 array( __CLASS__, 'render_input_field' ), 782 __FILE__, 783 'fh_shortcode_defaults_section', 784 array( 785 'option_name' => 'fh_default_asn', 786 'description' => 'The shortname of your partner company. Include if using the ASN network.', 787 ) 788 ); 789 790 add_settings_field( 791 'fh_default_asn_ref', 792 'asn_ref', 793 array( __CLASS__, 'render_input_field' ), 794 __FILE__, 795 'fh_shortcode_defaults_section', 796 array( 797 'option_name' => 'fh_default_asn_ref', 798 'description' => 'The voucher number that should be set for ASN bookings.', 799 ) 800 ); 801 802 add_settings_field( 803 'fh_default_ref', 804 'ref', 805 array( __CLASS__, 'render_input_field' ), 806 __FILE__, 807 'fh_shortcode_defaults_section', 808 array( 809 'option_name' => 'fh_default_ref', 810 'description' => 'The online booking reference that should be set for bookings', 811 ) 812 ); 813 814 add_settings_field( 815 'fh_default_sheet', 816 'sheet', 817 array( __CLASS__, 'render_input_field' ), 818 __FILE__, 819 'fh_shortcode_defaults_section', 820 array( 821 'option_name' => 'fh_default_sheet', 822 'description' => 'The price sheet that should be used while creating bookings.', 823 ) 824 ); 825 826 add_settings_field( 827 'fh_default_full_items', 828 'full_items', 829 array( __CLASS__, 'render_select_field' ), 830 __FILE__, 831 'fh_shortcode_defaults_section', 832 array( 833 'option_name' => 'fh_default_full_items', 834 'choices' => array( 835 'yes', 836 'no', 837 ), 838 'description' => 'Should the Lightframe that is opened include item descriptions and photos? Defaults to "no".', 839 ) 840 ); 841 842 add_settings_field( 843 'fh_default_lightframe', 844 'lightframe', 845 array( __CLASS__, 'render_select_field' ), 846 __FILE__, 847 'fh_shortcode_defaults_section', 848 array( 849 'option_name' => 'fh_default_lightframe', 850 'choices' => array( 851 'yes', 852 'no', 853 ), 854 'description' => 'Choose "yes" to open new bookings inside a Lightframe. Choose "no" to open them in an external page instead. Defaults to "yes".' 855 ) 856 ); 857 858 add_settings_field( 859 'fh_default_class', 860 'class', 861 array( __CLASS__, 'render_input_field' ), 862 __FILE__, 863 'fh_shortcode_defaults_section', 864 array( 865 'option_name' => 'fh_default_class', 866 'description' => 'The class option only applies to the [lightframe] shortcode.', 867 ) 868 ); 869 870 add_settings_section( 871 'fh_buttons_section', 872 'FareHarbor Buttons', 873 array( __CLASS__, 'render_fh_buttons_section_text' ), 874 __FILE__ 875 ); 876 877 add_settings_field( 878 'fh_buttons_active', 879 'FH Buttons Activation', 880 array( __CLASS__, 'render_input_field' ), 881 __FILE__, 882 'fh_buttons_section', 883 array( 884 'option_name' => 'fh_buttons_active', 885 'type' => 'checkbox', 886 'label' => 'Load the FH Buttons stylesheet on all pages.', 887 ) 888 ); 889 890 add_settings_field( 891 'fh_buttons_query', 892 'Button Colors', 893 array( __CLASS__, 'render_input_field' ), 894 __FILE__, 895 'fh_buttons_section', 896 array( 897 'option_name' => 'fh_buttons_query', 898 'description' => 'Provide colors in the format <code>red=ff0000&green=00ff00</code>', 899 ) 900 ); 901 902 } 903 904 public static function validate_settings( $input ) { 905 906 // Strip tags and trim whitespace 907 $input = array_map( array( __CLASS__, 'sanitize' ), $input ); 908 909 // Let's not pollute the DB with empty settings 910 $input = array_filter( $input ); 911 912 // Shortnames only work when lowercase 913 if ( isset( $input[ 'fh_default_shortname' ] ) ) 914 $input[ 'fh_default_shortname' ] = strtolower( $input[ 'fh_default_shortname' ] ); 915 916 return $input; 917 918 } 919 920 public static function render_options_page() { 921 922 ?> 923 924 <div class="wrap"> 925 926 <h2>FareHarbor Plugin Settings</h2> 927 928 <form action="options.php" method="post"> 929 930 <?php settings_fields( 'fareharbor_settings' ); ?> 931 932 <?php do_settings_sections( __FILE__ ); ?> 933 934 <p class="submit"> 935 <input name="Submit" type="submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" /> 936 </p> 228 937 229 if( $attrs["full_items"] == 'no' && empty( $attrs["view_availability"] ) ) { 230 $fallback_url .= 'calendar/'; 231 } 232 } 233 234 if( !empty( $attrs["view_availability"] ) ) { 235 $fallback_url .= 'availability/' . $attrs["view_availability"] . '/book/'; 236 } 938 </form> 939 940 </div> 941 942 <?php 943 944 } 945 946 public static function render_fh_about_section_text() { 947 948 echo '<p><b>This plugin requires a FareHarbor account to work.</b> FareHarbor provides powerful, intuitive, and highly customizable reservation software for activity and tourism businesses. We can help you enable online booking on your website using this plugin and a whole range of other features. Don\'t have an account? <a href="https://fareharbor.com/join/" target="_blank">Get in touch at fareharbor.com.</a></p><p>If you\'re already with FareHarbor, check out our <a href="https://fareharbor.com/help/setup/wordpress-plugin/" target="_blank">help center article</a> on how to get started with this plugin.</p>'; 949 950 } 951 952 public static function render_fh_shortcode_defaults_section_text() { 953 954 echo '<p>Set default options for the [lightframe], [fareharbor], [itemgrid], and [partners] shortcodes here. The options written in when including a shortcode on a page will always take precedence over the default values entered below. <a href="https://fareharbor.com/help/setup/wordpress-plugin/#setting-defaults" target="_blank">Learn how default options work.</a></p>'; 955 956 } 957 958 public static function render_fh_buttons_section_text() { 959 960 echo '<p>FareHarbor can automatically load in a CSS stylesheet which includes classes to make your Lightframe links look like beautifully designed buttons.</p>'; 961 962 } 963 964 public static function render_input_field( $args ) { 965 // We use this function to render all of our options fields 966 // $args is supplied as the last parameter to each 967 // add_settings_field() call in fareharbor::register_settings() 968 // This function is only run on our settings page in the admin 969 970 $type = isset( $args[ 'type' ] ) ? $args[ 'type' ] : 'text'; 971 972 $option_name = $args[ 'option_name' ]; 973 $value = self::get_option( $option_name, '', false ); 974 975 // Attributes all input tags need 976 $attrs = array( 977 'name' => "fareharbor_settings[$option_name]", 978 'id' => $option_name, 979 'type' => $type, 980 ); 981 982 // Type-specific attributes 983 switch ( $type ) { 984 985 case 'text': 986 $attrs[ 'size' ] = '40'; 987 $attrs[ 'value' ] = (string) $value; 988 break; 989 990 case 'checkbox': 991 if ( $value ) 992 $attrs[ 'checked' ] = 'checked'; 993 break; 237 994 238 995 } 239 996 240 $fallback_url_query_string = array(); 241 242 if ( !empty( $attrs["asn"] ) ) { 243 $fallback_url_query_string["asn"] = $attrs["asn"]; 244 245 if( !empty( $attrs["asn_ref"] ) ) { 246 $fallback_url_query_string["asn-ref"] = $attrs["asn_ref"]; 247 } 248 } 249 250 if ( !empty( $attrs["ref"] ) ) { 251 $fallback_url_query_string["ref"] = $attrs["ref"]; 252 } 253 254 if ( !empty( $attrs["sheet"] ) ) { 255 $fallback_url_query_string["sheet"] = $attrs["sheet"]; 256 } 257 258 if( !empty( $fallback_url_query_string ) ) { 259 $fallback_url .= '?'; 260 $fallback_url .= http_build_query($fallback_url_query_string); 261 } 262 263 // $lightframe_options array, to be JSONified for Lightframe API call 264 // --------------------------------------------- 265 266 $lightframe_options["shortname"] = $attrs["shortname"]; 267 268 // We should still set a default for full_items, but avoid writing it in if it's just a 'no' 269 270 if ( $attrs["full_items"] != 'no' ) { 271 $lightframe_options["fullItems"] = $attrs["full_items"]; 272 } 273 274 if ( !empty( $attrs["asn"] ) ) { 275 $lightframe_options["asn"] = $attrs["asn"]; 276 277 if( !empty( $attrs["asn_ref"] ) ) { 278 $lightframe_options["asnRef"] = $attrs["asn_ref"]; 279 } 280 } 281 282 if ( !empty( $attrs["ref"] ) ) { 283 $lightframe_options["ref"] = $attrs["ref"]; 284 } 285 286 if ( !empty( $attrs["sheet"] ) ) { 287 $lightframe_options["sheet"] = $attrs["sheet"]; 288 } 289 290 if ( !empty( $attrs["items"] ) ) { 291 $lightframe_options["items"] = array($attrs["items"]); // Put these in an array so it gets brackets 292 } 293 294 // If the view is a string type, just write it in 295 296 if ($attrs["view"] == 'items' || $attrs["view"] == 'all-availability') { 297 $lightframe_options["view"] = $attrs["view"]; 298 } 299 300 // If the view is not a string type, you need to pass just view_item= OR view_item= and view_availability= 301 302 if( !empty( $attrs["view_item"] ) && empty( $attrs["view_availability"] )) { 303 $lightframe_options["view"] = array( 'item' => $attrs["view_item"] ); 304 } 305 306 if( !empty( $attrs["view_item"] ) && !empty( $attrs["view_availability"] )) { 307 $lightframe_options["view"] = array( 'item' => $attrs["view_item"], 'availability' => $attrs["view_availability"] ); 308 } 309 310 // Put it all together now 311 // --------------------------------------------- 312 313 $output .= '<a href="' . $fallback_url . '" '; 314 315 if ( !empty( $attrs["class"] ) ) { 316 $output .= 'class="' . $attrs["class"] .'" '; 317 } 318 319 if ( !empty( $attrs["id"] ) ) { 320 $output .= 'id="' . $attrs["id"] .'" '; 321 } 322 323 $output .= 'onclick="FH.open(' . str_replace('"',"'", json_encode($lightframe_options)) . '); return false;">'; 324 325 $output .= do_shortcode( $content ) . '</a>'; 326 } 327 328 return $output; 997 // Merge in attributes supplied in the arguments 998 if ( isset( $args[ 'attrs' ] ) ) 999 // attributes from the arguments will override the defaults 1000 $attrs = $args[ 'attrs' ] + $attrs; 1001 1002 // Actually generate the input tag 1003 $out = '<input'; 1004 foreach ( $attrs as $name => $value ) 1005 $out .= " $name=\"$value\""; 1006 $out .= ' />'; 1007 1008 if ( !empty( $args[ 'label' ] ) ) 1009 $out .= "<label for=\"$option_name\">{$args[ 'label' ]}</label>"; 1010 1011 if ( !empty( $args[ 'description' ] ) ) 1012 $out .= "<p class=\"description\">{$args[ 'description' ]}</p>"; 1013 1014 // And print it onto the page 1015 echo $out; 1016 1017 } 1018 1019 public static function render_select_field( $args ) { 1020 1021 $option_name = $args[ 'option_name' ]; 1022 $value = self::get_option( $option_name, '', false ); 1023 1024 ?> 1025 1026 <select name="<?php echo "fareharbor_settings[$option_name]" ?>" id="<?php echo $option_name ?>"> 1027 1028 <option value="" <?php selected( '', $value ); ?>></option> 1029 1030 <?php foreach ( $args[ 'choices' ] as $choice ) { ?> 1031 <option value="<?php echo $choice ?>" <?php selected( $choice, $value ) ?>><?php echo $choice ?></option> 1032 <?php } ?> 1033 1034 </select> 1035 1036 <?php 1037 1038 if ( !empty( $args[ 'description' ] ) ) 1039 echo "<p class=\"description\">{$args[ 'description' ]}</p>"; 1040 1041 } 1042 329 1043 } 330 331 // [partners] shortcode332 // ---------------------------------------------333 334 function partners_shortcode( $attrs ) {335 336 $attrs = fh_process_attrs( $attrs );337 338 $output = '';339 340 // Bail if a shortname isn't provided341 342 if ( empty( $attrs["shortname"] ) ) {343 344 $output .= '<p>Please provide a FareHarbor shortname. (Format: <code>shortname="yourshortname"</code>)</p>';345 346 } else {347 348 $output = '<script src="https://' . fh_url() . '/embeds/script/partners/';349 350 $output .= $attrs["shortname"] . '/';351 352 // Build query string of options353 354 $output .= '?';355 356 // lightframe is always included357 358 $fh_query_string_options = array('lightframe' => $attrs["lightframe"]);359 360 // For safety, always set asn, just to the shortname if asn isn't given361 362 $fh_query_string_options["asn"] = !empty( $attrs["asn"] ) ? $attrs["asn"] : $attrs["shortname"];363 364 if( !empty( $attrs["asn_ref"] ) ) {365 $fh_query_string_options["asn-ref"] = $attrs["asn_ref"];366 }367 368 if ( !empty( $attrs["ref"] ) ) {369 $fh_query_string_options["ref"] = $attrs["ref"];370 }371 372 if ( $attrs["full_items"] != 'no' ) {373 $fh_query_string_options["full-items"] = $attrs["full_items"];374 }375 376 if ( !empty( $attrs["include"] ) ) {377 $fh_query_string_options["include"] = $attrs["include"];378 }379 380 $output .= http_build_query($fh_query_string_options);381 382 $output .= '"></script>';383 }384 385 return $output;386 }387 388 // Add API script to footer389 // ---------------------------------------------390 391 add_action('wp_footer', 'lightframe_api_footer');392 function lightframe_api_footer() {393 echo '<!-- FareHarbor plugin activated --><script src="https://' . fh_url() . '/embeds/api/v1/"></script>';394 }395 ?> -
fareharbor/trunk/readme.txt
r1261996 r1398584 1 1 === Plugin Name === 2 2 Contributors: fareharbor 3 Tags: reservations, booking calendar, booking, bookingplugin, reservation calendar, booking system3 Tags: reservations, booking calendar, booking, reservation plugin, reservation calendar, booking system 4 4 Requires at least: 3.0 5 Tested up to: 4. 3.15 Tested up to: 4.5 6 6 Stable tag: trunk 7 7 License: GPLv2 or later 8 8 License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 9 10 Adds shortcodes for FareHarbor reservation booking calendar embeds and buttons to your site 10 Easily add FareHarbor reservation calendars, booking embeds, and buttons to your site. 11 11 12 12 == Description == 13 13 14 Adds shortcodes that make s it easy to embed FareHarbor's free booking reservation calendars or buttons on your site. Learn more about the FareHarbor reservation system at [fareharbor.com](https://fareharbor.com/ "Freereservation software").14 Adds shortcodes that make it easy to embed FareHarbor booking calendars and buttons on your site. Learn more about the FareHarbor reservation system at [fareharbor.com](https://fareharbor.com/ " Enterprise-level reservation software"). 15 15 16 Usage example: `[fareharbor shortname="companyname" items="500" lightframe="yes"]`.16 Includes shortcodes for embedded calendars (`[fareharbor]`), embedded grids of activities (`[itemgrid]`), and buttons that open a booking overlay (`[lightframe]`). 17 17 18 18 For more examples and available options, please visit <https://fareharbor.com/help/setup/wordpress-plugin/>. … … 35 35 ## Then 36 36 37 Use the `[fareharbor shortname="companyname"]` or `[lightframe shortname="companyname"][/lightframe]` shortcodes in your posts or pages.37 Use the `[fareharbor shortname="companyname"]`, `[lightframe shortname="companyname"][/lightframe]`, `[partners shortname="companyname" include="company1,company2"]`, and `[itemgrid shortname="companyname"]` shortcodes in your posts or pages. 38 38 39 39 == Screenshots == 40 40 41 1. Generated reservations calendar42 2. Lightframe booking o ptions: customers book your activity without leaving your website41 1. Generated embedded reservations calendar 42 2. Lightframe booking overlay: customers book your activity without leaving your website 43 43 44 44 == Changelog == 45 45 46 = 2.2 = 47 * Add support for [itemgrid] shortcode 48 * Add support for an even better mobile booking experience via `fallback="simple"` shortcode option 49 * Add a settings page (Settings > FareHarbor) 50 * Allow setting shortcode option defaults in the settings page and via filters. 51 * Add support for including a FareHarbor Buttons stylesheet on all pages. 52 46 53 = 2.1 = 47 * Add support for specifying price sheet to use while booking54 * Add support for specifying price sheets to use while booking 48 55 49 56 = 2.0 =
Note: See TracChangeset
for help on using the changeset viewer.