GeoMashup
[ class tree: GeoMashup ] [ index: GeoMashup ] [ all elements ]

Source for file geo-mashup-options.php

Documentation is available at geo-mashup-options.php

  1. <?php
  2. /**
  3.  * Management of Geo Mashup saved options.
  4.  *
  5.  * @package GeoMashup
  6.  */
  7.  
  8. /**
  9.  * A singleton to manage Geo Mashup saved options.
  10.  * 
  11.  * @since 1.2
  12.  * @access public
  13.  * @package GeoMashup
  14.  */
  15.     /**
  16.      * Valid options with default values.
  17.      * 
  18.      * @since 1.2
  19.      * @var array 
  20.      */
  21.     public $default_options = array (
  22.         'overall' => array (
  23.             'version' => '',
  24.             'google_key' => '',
  25.             'mashup_page' => '',
  26.             'category_link_separator' => '::',
  27.             'category_link_text' => 'map',
  28.             'category_zoom' => 'auto',
  29.             'add_category_links' => 'false',
  30.             'copy_geodata' => 'false',
  31.             'theme_stylesheet_with_maps' => 'false',
  32.             'located_post_types' => array( 'post', 'page' ),
  33.             'located_object_name' => array( 
  34.                 'post' => 'deprecated',
  35.                 'user' => 'false',
  36.                 'comment' => 'false' ),
  37.             'enable_reverse_geocoding' => 'true',
  38.             'adsense_code' => 'partner-pub-5088093001880917',
  39.             'geonames_username' => 'geomashup',
  40.              'map_api' => 'googlev3',
  41.             'import_custom_field' => '' ),
  42.         'global_map' => array (
  43.             'width' => '400',
  44.             'height' => '400',
  45.             'map_type' => 'G_NORMAL_MAP',
  46.             'zoom' => 'auto',
  47.             'background_color' => 'c0c0c0',
  48.             'category_color' => array ( ),
  49.             'category_line_zoom' => array ( ),
  50.             'map_control' => 'GSmallZoomControl3D',
  51.             'add_map_type_control' => array(),
  52.             'add_overview_control' => 'false',
  53.             'add_google_bar' => 'false',
  54.             'enable_scroll_wheel_zoom' => 'false',
  55.             'show_post' => 'false',
  56.             'show_future' => 'false',
  57.             'marker_select_info_window' => 'true',
  58.             'marker_select_highlight' => 'false',
  59.             'marker_select_center' => 'false',
  60.             'marker_select_attachments' => 'false',
  61.             'max_posts' => '',
  62.             'auto_info_open' => 'true',
  63.             'click_to_load' => 'false',
  64.             'click_to_load_text' => '',
  65.              'cluster_max_zoom' => '',
  66.              'cluster_lib' => 'clustermarker'    ),
  67.         'single_map' => array (
  68.             'width' => '400',
  69.             'height' => '400',
  70.             'map_control' => 'GSmallZoomControl3D',
  71.             'map_type' => 'G_NORMAL_MAP',
  72.             'zoom' => '11',
  73.             'background_color' => 'c0c0c0',
  74.             'add_overview_control' => 'false',
  75.             'add_map_type_control' => array(),
  76.             'add_google_bar' => 'false',
  77.             'enable_scroll_wheel_zoom' => 'false',
  78.             'click_to_load' => 'false',
  79.              'click_to_load_text' => '' ), 
  80.         'context_map' => array (
  81.             'width' => '200',
  82.             'height' => '200',
  83.             'map_control' => 'GSmallZoomControl3D',
  84.             'map_type' => 'G_NORMAL_MAP',
  85.             'zoom' => 'auto',
  86.             'background_color' => 'c0c0c0',
  87.             'add_overview_control' => 'false',
  88.             'add_map_type_control' => array(),
  89.             'add_google_bar' => 'false',
  90.             'enable_scroll_wheel_zoom' => 'false',
  91.             'marker_select_info_window' => 'true',
  92.             'marker_select_highlight' => 'false',
  93.             'marker_select_center' => 'false',
  94.             'marker_select_attachments' => 'false',
  95.             'click_to_load' => 'false',
  96.              'click_to_load_text' => '' ) );
  97.  
  98.     /**
  99.      * Map of old option names to new ones.
  100.      * 
  101.      * @since 1.2
  102.      * @var array 
  103.      */
  104.     private $conversions = array (
  105.         'google_key' => array ( 'overall', 'google_key' ),
  106.         'mashup_page' => array ( 'overall', 'mashup_page' ),
  107.         'category_link_separator' => array ( 'overall', 'category_link_separator' ),
  108.         'category_link_text' => array ( 'overall', 'category_link_text' ),
  109.         'category_zoom' => array ( 'overall', 'category_zoom' ),
  110.         'add_category_links' => array ( 'overall', 'add_category_links' ),
  111.         'theme_stylesheet_with_maps' => array ( 'overall', 'theme_stylesheet_with_maps' ),
  112.         'map_width' => array ( 'global_map', 'width' ),
  113.         'map_height' => array ( 'global_map', 'height' ),
  114.         'map_type' => array ( 'global_map', 'map_type' ),
  115.         'zoom_level' => array ( 'global_map', 'zoom' ),
  116.         'category_color' => array ( 'global_map', 'category_color' ),
  117.         'category_line_zoom' => array ( 'global_map', 'category_line_zoom' ),
  118.         'map_control' => array ( 'global_map', 'map_control' ),
  119.         'add_map_type_control' => array ( 'global_map', 'add_map_type_control' ),
  120.         'add_overview_control' => array ( 'global_map', 'add_overview_control' ),
  121.         'show_post' => array ( 'global_map', 'show_post' ),
  122.         'show_future' => array ( 'global_map', 'show_future' ),
  123.         'max_posts' => array ( 'global_map', 'max_posts' ),
  124.         'auto_info_open' => array ( 'global_map', 'auto_info_open' ),
  125.         'auto_open_info_window' => array ( 'global_map', 'auto_info_open' ),
  126.         'click_to_load' => array ( 'global_map', 'click_to_load' ),
  127.         'click_to_load_text' => array ( 'global_map', 'click_to_load_text' ),
  128.         'in_post_map_control' => array ( 'single_map', 'map_control' ),
  129.         'in_post_map_width' => array ( 'single_map', 'width' ),
  130.         'in_post_map_height' => array ( 'single_map', 'height' ),
  131.         'in_post_map_type' => array ( 'single_map', 'map_type' ),
  132.         'in_post_zoom_level' => array ( 'single_map', 'zoom' ),
  133.         'in_post_add_overview_control' => array ( 'single_map', 'add_overview_control' ),
  134.         'in_post_add_map_type_control' => array ( 'single_map', 'add_map_type_control' ),
  135.         'in_post_click_to_load' => array ( 'single_map', 'click_to_load' ),
  136.         'in_post_click_to_load_text' => array ( 'single_map', 'click_to_load_text' ) );
  137.  
  138.     /**
  139.      * Options keys whose values aren't predictable.
  140.      * 
  141.      * @since 1.2
  142.      * @var array 
  143.      */
  144.     private $freeform_option_keys = array ( 'category_color', 'category_line_zoom', 'add_map_type_control', 'located_post_types' );
  145.  
  146.     /**
  147.      * Valid map types.
  148.      * 
  149.      * @since 1.2
  150.      * @var array 
  151.      */
  152.     public $valid_map_types = array ( 'G_NORMAL_MAP', 'G_SATELLITE_MAP', 'G_HYBRID_MAP', 'G_PHYSICAL_MAP', 'G_SATELLITE_3D_MAP' );
  153.  
  154.     /**
  155.      * Saved option values.
  156.      *
  157.      * Use the GeoMashupOptions::get() method for access.
  158.      * 
  159.      * @since 1.2
  160.      * @var array 
  161.      */
  162.     private $options;
  163.  
  164.     /**
  165.      * Old option values that can't be converted.
  166.      * 
  167.      * @since 1.2
  168.      * @var array 
  169.      */
  170.     private $corrupt_options = '';
  171.  
  172.     /**
  173.      * Validation messages.
  174.      * 
  175.      * @since 1.2
  176.      * @var array 
  177.      */
  178.     private $validation_errors = array();
  179.  
  180.     /**
  181.      * PHP5 constructor
  182.      *
  183.      * Should be used only in this file.
  184.      * 
  185.      * @since 1.2
  186.      * @return void 
  187.      */
  188.     function __construct() {
  189.         $shared_google_api_key = get_option ( 'google_api_key' );
  190.         if ( $shared_google_api_key ) {
  191.             $this->default_options['overall']['google_key'] = $shared_google_api_key;
  192.         }
  193.         $this->options = $this->default_options;
  194.         $settings = get_option ( 'geo_mashup_options' );
  195.         if ( is_array ( $settings ) ) {
  196.             $settings = $this->convert_old_settings ( $settings );
  197.             $this->options = $this->valid_options ( $settings, $this->default_options, $add_missing = true );
  198.         } else {
  199.             $failed_options = $settings;
  200.             if ( is_string ( $settings ) && !empty ( $settings ) ) {
  201.                 $this->corrupt_options = $settings;
  202.             }
  203.         }
  204.     }
  205.  
  206.     /**
  207.      * Change old option names.
  208.      * 
  209.      * @since 1.2
  210.      *
  211.      * @param array $settings Existing settings.
  212.      * @return array Converted settings.
  213.      */
  214.     private function convert_old_settings( $settings ) {
  215.         foreach ( $this->conversions as $old_key => $new_keys ) {
  216.             if ( isset ( $settings[$old_key] ) ) {
  217.                 $settings[$new_keys[0]][$new_keys[1]] = $settings[$old_key];
  218.                 unset ( $settings[$old_key] );
  219.             }
  220.         }
  221.         if ( isset( $settings['overall']['located_object_name']['post']) and 'true' == $settings['overall']['located_object_name']['post'] ) {
  222.             $settings['overall']['located_object_name']['post'] = 'deprecated';
  223.             if ( empty( $settings['overall']['located_post_types']['post'] ) )
  224.                 $settings['overall']['located_post_types'] = array( 'post', 'page' );
  225.         }
  226.         return $settings;
  227.     }
  228.  
  229.     /**
  230.      * Write current values to the database.
  231.      * 
  232.      * @since 1.2
  233.      *
  234.      * @return bool Success or failure.
  235.      */
  236.     public function save() {
  237.         $saved = false;
  238.         if ($this->options == $this->valid_options ( $this->options ) ) {
  239.             $saved = update_option('geo_mashup_options', $this->options);
  240.             
  241.             // Share our Google API key
  242.             $google_api_key = $this->options['overall']['google_key'];
  243.             if ( !empty ( $google_api_key ) && !get_option ( 'google_api_key' ) ) {
  244.                 update_option( 'google_api_key', $google_api_key );
  245.             }
  246.         }
  247.         return $saved;
  248.     }
  249.  
  250.     /**
  251.      * Get a saved option value.
  252.      * 
  253.      * <code>
  254.      * $single_map_options_array = $geo_mashup_options->get( 'single_map' );
  255.      * $google_key = $geo_mashup_options->get( 'overall', 'google_key' );
  256.      * $add_global_satellite_map = $geo_mashup_options->get( 'global_map', 'add_map_type_control', 'G_SATELLITE_MAP' );
  257.      * </code>
  258.      *
  259.      * @since 1.2
  260.      *
  261.      * @param string $key1 Highest level key.
  262.      * @param string $key2 Second level key.
  263.      * @param string $key3 Third level key.
  264.      * @return string|arrayThe option value or values.
  265.      */
  266.     public function get( $key1, $key2 = null, $key3 = null ) {
  267.         $subset = array();
  268.         if ( is_null ( $key2 ) ) {
  269.             // Getting first dimension options
  270.             if ( is_array ( $key1 ) ) {
  271.                 foreach ( $key1 as $key ) $subset[$key] = isset( $this->options[$key] ) ? $this->options[$key] : null;
  272.                 return $subset;
  273.             } else {
  274.                 return isset( $this->options[$key1] ) ? $this->options[$key1] : null;
  275.             }
  276.         } else if ( is_null ( $key3 ) ) {
  277.             // Getting second dimension options
  278.             if ( is_array ( $key2 ) ) {
  279.                 foreach ( $key2 as $key ) $subset[$key] = isset( $this->options[$key1][$key] ) ? $this->options[$key1][$key] : null;
  280.                 return $subset;
  281.             } else {
  282.                 return isset( $this->options[$key1][$key2] ) ? $this->options[$key1][$key2] : null;
  283.             } 
  284.         } else {
  285.             // Getting third dimension options
  286.             if ( is_array ( $key3 ) ) {
  287.                 foreach ( $key3 as $key ) $subset[$key] = isset( $this->options[$key1][$key2][$key] ) ? $this->options[$key1][$key2][$key] : null;
  288.                 return $subset;
  289.             } else {
  290.                 return isset( $this->options[$key1][$key2][$key3] ) ? $this->options[$key1][$key2][$key3] : null;
  291.             }
  292.         }
  293.     }
  294.  
  295.     /**
  296.      * Import valid options from an array.
  297.      * 
  298.      * @since 1.2
  299.      *
  300.      * @param array $option_array Associative array of option names and values.
  301.      */
  302.     public function set_valid_options( $option_array ) {
  303.         $this->validation_errors = array ( );
  304.         $this->options = $this->valid_options ( $option_array );
  305.     }
  306.  
  307.     /**
  308.      * Remove invalid option keys from an array, and replace invalid values with defaults.
  309.      *
  310.      * @since 1.2
  311.      *
  312.      * @param array $option_array An array of options to validate.
  313.      * @param array $defaults Optional array of valid default values.
  314.      * @param boolean $add_missing Set if input is not from a form, so missing values are not unchecked checkboxes and should get default values.
  315.      * @return array Valid options.
  316.      */
  317.     private function valid_options( $option_array, $defaults = null, $add_missing = false ) {
  318.         $valid_options = array ( );
  319.         if ( is_null ( $defaults ) ) {
  320.             $defaults = ( empty ( $this->options ) ) ? $this->default_options : $this->options;
  321.         }
  322.         if ( !is_array( $option_array ) ) return $defaults;
  323.  
  324.         foreach ( $defaults as $key => $default_value ) {
  325.             if ( isset( $option_array[$key] ) && $this->is_valid ( $key, $option_array[$key] ) ) {
  326.                 if ( is_array ( $option_array[$key] ) && !in_array ( $key, $this->freeform_option_keys ) ) {
  327.                     // Validate options in sub-arrays, except freeform array options, whose keys aren't known
  328.                     $valid_options[$key] = $this->valid_options ( $option_array[$key], $default_value );
  329.                 } else {
  330.                     // Use the valid non-array value
  331.                     $valid_options[$key] = $option_array[$key];
  332.                 }
  333.             } else {
  334.                 // Value in question is invalid
  335.                 if ( ! $add_missing and empty ( $option_array[$key] ) and in_array ($default_value, array ( 'true', 'false' ) ) ) {
  336.                     // Convert empty booleans to false to handle unchecked checkboxes
  337.                     $valid_options[$key] = 'false';
  338.                 } else { 
  339.                     $valid_options[$key] = $default_value;
  340.                 }
  341.             }
  342.         }
  343.         return $valid_options;
  344.     }
  345.  
  346.     /**
  347.      * Check an option key and value for validity.
  348.      * 
  349.      * @since 1.2
  350.      *
  351.      * @param string $key Option key.
  352.      * @param mixed $value Option value, modified in some cases.
  353.      * @return bool True if valid.
  354.      */
  355.     public function is_valid( $key, &$value ) {
  356.         switch ( $key ) {
  357.             case 'map_type':
  358.                 if ( !in_array ( $value, $this->valid_map_types ) ) {
  359.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  360.                         __(', which must be a valid map type (see documentation)', 'GeoMashup') );
  361.                     return false;
  362.                 }
  363.                 return true;
  364.  
  365.             case 'add_map_type_control':
  366.                 if ( !is_array ( $value ) ) {
  367.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  368.                         __(', which must be an array of valid map types (see documentation)', 'GeoMashup') );
  369.                     return false;
  370.                 }
  371.                 foreach( $value as $map_type ) {
  372.                     if ( !in_array ( $map_type, $this->valid_map_types ) ) {
  373.                         array_push ( $this->validation_errors, '"'. $map_type . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  374.                             __(', which must be a valid map type (see documentation)', 'GeoMashup') );
  375.                         return false;
  376.                     }
  377.                 }
  378.                 return true;
  379.  
  380.             case 'map_control':
  381.                 $valid_map_controls = array ( 'GSmallZoomControl', 'GSmallMapControl', 'GLargeMapControl', 'GLargeMapControl3D', 'GSmallZoomControl3D' );
  382.                 if ( !in_array ( $value, $valid_map_controls ) ) {
  383.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  384.                         __(', which must be a valid map control (see documentation)', 'GeoMashup') );
  385.                     return false;
  386.                 }
  387.                 return true;
  388.  
  389.             case 'map_api':
  390.                 $valid_apis = array( 'google', 'openlayers', 'googlev3' );
  391.                 if ( !in_array ( $value, $valid_apis ) ) {
  392.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  393.                         __(', which must be a valid map provider.', 'GeoMashup') );
  394.                     return false;
  395.                 }
  396.                 return true;
  397.  
  398.             case 'cluster_lib':
  399.                 $valid_libs = array( 'clustermarker', 'markerclusterer' );
  400.                 if ( !in_array( $value, $valid_libs ) ) {
  401.                     array_push( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  402.                         __(', which must be a valid clustering library.', 'GeoMashup') );
  403.                     return false;
  404.                 }
  405.                 return true;
  406.  
  407.             // strings without HTML
  408.             case 'adsense_code':
  409.                 if ( empty( $value ) )
  410.                     $value = 'partner-pub-5088093001880917';
  411.             case 'geonames_user':
  412.                 if ( empty( $value ) )
  413.                     $value = 'geomashup';
  414.             case 'category_link_separator':
  415.             case 'category_link_text':
  416.             case 'click_to_load_text':
  417.             case 'import_custom_field':
  418.                 if ( empty ( $value ) ) return true;
  419.             case 'google_key':
  420.             case 'version':
  421.             case 'mashup_page':
  422.                 if ( !is_string ( $value ) ) {
  423.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  424.                         __(', which must be a string', 'GeoMashup') );
  425.                     return false;
  426.                 }
  427.                 if ( preg_match( "/<.*>/", $value ) ) {
  428.                     array_push ( $this->validation_errors, '"'. esc_html( $value ) . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  429.                         __(', which must not contain XML tags.', 'GeoMashup') );
  430.                     return false;
  431.                 }
  432.                 return true;
  433.  
  434.             // numbers
  435.             case 'max_posts':
  436.                 if ( empty ( $value ) ) return true;
  437.             case 'width':
  438.             case 'height':
  439.                 if ( substr ( $value, -1 ) == '%' && is_numeric ( substr ( $value, 0, -1 ) ) ) return true;
  440.                 if ( !is_numeric ( $value ) ) {
  441.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  442.                         __(', which must be a number', 'GeoMashup') );
  443.                     return false;
  444.                 }
  445.                 return true;
  446.  
  447.             // show_future: boolean or 'only'
  448.             case 'show_future':
  449.                 if ( 'only' == $value ) 
  450.                     return true;
  451.  
  452.             // booleans
  453.             case 'auto_info_open':
  454.             case 'add_category_links':
  455.             case 'add_overview_control':
  456.             case 'add_google_bar':
  457.             case 'copy_geodata':
  458.             case 'enable_scroll_wheel_zoom':
  459.             case 'marker_select_info_window':
  460.             case 'marker_select_highlight':
  461.             case 'marker_select_center':
  462.             case 'marker_select_attachments':
  463.             case 'theme_stylesheet_with_maps':
  464.             case 'show_post':
  465.             case 'click_to_load':
  466.             case 'user':
  467.             case 'comment':
  468.             case 'enable_reverse_geocoding':
  469.                 if ( empty ( $value ) ) {
  470.                     // fail quietly - it will be converted to false
  471.                     return false;
  472.                 } else if ( $value != 'true' && $value != 'false' ) { 
  473.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  474.                         __(', which must be "true" or "false"', 'GeoMashup') );
  475.                     return false;
  476.                 }
  477.                 return true;
  478.  
  479.             // arrays
  480.             case 'overall':
  481.             case 'global_map':
  482.             case 'single_map':
  483.             case 'context_map':
  484.             case 'category_color':
  485.             case 'category_line_zoom':
  486.             case 'located_post_types':
  487.             case 'located_object_name':
  488.                 if ( !is_array ( $value ) ) {
  489.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  490.                         __(', which must be an array', 'GeoMashup') );
  491.                     return false;
  492.                 }
  493.                 return true;
  494.  
  495.             // zoom levels
  496.             case 'cluster_max_zoom':
  497.                 if ( empty ( $value ) ) return true;
  498.             case 'zoom':
  499.             case 'category_zoom':
  500.                 if ( $value == 'auto' ) return true;
  501.                 if ( !is_numeric ( $value ) || $value < 0 || $value > GEO_MASHUP_MAX_ZOOM ) {
  502.                     array_push ( $this->validation_errors, '"'. $value . '" ' . __('is invalid for', 'GeoMashup') . ' ' . $key .
  503.                         __(', which must be a number from 0 to ', 'GeoMashup') . GEO_MASHUP_MAX_ZOOM );
  504.                     return false;
  505.                 }
  506.                 return true;
  507.  
  508.             // deprecated
  509.             case 'post':
  510.                 if ( 'deprecated' != $value ) {
  511.                     array_push( $this->validation_errors, '"'. $value . '" ' . __( 'is invalid for', 'GeoMashup' ) . ' ' . $key .
  512.                         __( ', which is a deprecated option', 'GeoMashup' ) );
  513.                     return false;
  514.                 }
  515.             default:
  516.                 return false;
  517.         }
  518.     }
  519. }
  520.  
  521. /**
  522.  * @global GeoMashupOptions The global instance of the GeoMashupOptions class.
  523.  */
  524. global $geo_mashup_options;
  525. $geo_mashup_options = new GeoMashupOptions();

Documentation generated on Sat, 09 Jul 2011 21:54:44 -0700 by phpDocumentor 1.4.3