66
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/acl/aco.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/acl/aco.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The Acces Control Object class. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.acl | ||||
|  */ | ||||
| class Ai1ec_Acl_Aco { | ||||
|  | ||||
|     /** | ||||
|      * Whether it's All event page or not. | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_all_events_page() { | ||||
|         global $typenow; | ||||
|         return $typenow === 'ai1ec_event'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Whether the current request is for a network or blog admin page | ||||
|      * | ||||
|      * Does not inform on whether the user is an admin! Use capability checks to | ||||
|      * tell if the user should be accessing a section or not. | ||||
|      * | ||||
|      * @return bool True if inside WordPress administration pages. | ||||
|      */ | ||||
|     public function is_admin() { | ||||
|         return is_admin(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if we are editing our custom post type. | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function are_we_editing_our_post() { | ||||
|         global $post; | ||||
|         return ( | ||||
|             is_object( $post ) && | ||||
|             isset( $post->post_type ) && | ||||
|             AI1EC_POST_TYPE === $post->post_type | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if it's our own custom post type. | ||||
|      * | ||||
|      * @param int|object $post Optional. Post ID or post object. | ||||
|      * Default is the current post from the loop. | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_our_post_type( $post_to_check = null ) { | ||||
|         if ( null === $post_to_check ) { | ||||
|             global $post; | ||||
|             $post_to_check = $post; | ||||
|         } | ||||
|         return get_post_type( $post_to_check ) === AI1EC_POST_TYPE; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The base class which simply sets the registry object. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Bootstrap | ||||
|  */ | ||||
| abstract class Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object | ||||
|      */ | ||||
|     protected $_registry; | ||||
|  | ||||
|     /** | ||||
|      * The contructor method. | ||||
|      * | ||||
|      * Stores in object injected registry object. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry Injected registry object. | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         $this->_registry = $registry; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,19 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Exceptions occuring during bootstrap | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Exception | ||||
|  */ | ||||
| class Ai1ec_Bootstrap_Exception extends Ai1ec_Exception { | ||||
|  | ||||
|     public function get_html_message() { | ||||
|         return '<p>Failure in All-in-One Event Calendar core:<br />' . | ||||
|             $this->getMessage() . '</p>'; | ||||
|     } | ||||
|  | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,525 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Autoloader Class | ||||
|  * | ||||
|  * This class is responsible for loading all the requested class of the | ||||
|  * system | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Loader | ||||
|  */ | ||||
| class Ai1ec_Loader { | ||||
|  | ||||
|     /** | ||||
|      * @var string Used to specify new instances every time. | ||||
|      */ | ||||
|     CONST NEWINST    = 'n'; | ||||
|  | ||||
|     /** | ||||
|      * @var string Used to specify to treat as singleton. | ||||
|      */ | ||||
|     CONST GLOBALINST = 'g'; | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of files to be included | ||||
|      */ | ||||
|     protected $_paths          = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var bool Set to true when internal state is changed | ||||
|      */ | ||||
|     protected $_modified       = false; | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of files already included | ||||
|      */ | ||||
|     protected $_included_files = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var string The prefix used for the classes | ||||
|      */ | ||||
|     protected $_prefix         = null; | ||||
|  | ||||
|     /** | ||||
|      * @var string Base path to plugins core directory | ||||
|      */ | ||||
|     protected $_base_path      = null; | ||||
|  | ||||
|     /** | ||||
|      * @var array Registered folders. | ||||
|      */ | ||||
|     protected $_registered     = array(); | ||||
|  | ||||
|     /** | ||||
|      * load method | ||||
|      * | ||||
|      * Load given class, via `require`, into memory | ||||
|      * | ||||
|      * @param string $class Name of class, which needs to be loaded | ||||
|      * | ||||
|      * @return Ai1ec_Loader Instance of self for chaining | ||||
|      */ | ||||
|     public function load( $class ) { | ||||
|         if ( isset( $this->_paths[$class] ) ) { | ||||
|             $this->include_file( $this->_paths[$class]['f'] ); | ||||
|         } | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method which actually includes required file. | ||||
|      * | ||||
|      * The PHP language construct used is `require` and not a `require_once`, | ||||
|      * as this is internal method, which shall guard itself against incidents | ||||
|      * that may occur during loading classes more than once. | ||||
|      * During include additional callbacks may be fired to include related | ||||
|      * files, i.e. speed-up further requires. | ||||
|      * | ||||
|      * @param string $file Name of file to include | ||||
|      * | ||||
|      * @return Ai1ec_Loader Instance of self for chaining | ||||
|      */ | ||||
|     public function include_file( $file ) { | ||||
|         if ( ! isset( $this->_included_files[$file] ) ) { | ||||
|             $this->_included_files[$file] = true; | ||||
|             require $file; | ||||
|         } | ||||
|         return $this->_included_files[$file]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * collect_classes method | ||||
|      * | ||||
|      * Method to extract classes list from filesystem. | ||||
|      * Returned array contains names of class, as keys, and file entites as | ||||
|      * value, where *entities* means either a file name | ||||
|      * - {@see self::match_file()} for more. | ||||
|      * | ||||
|      * @return array Map of classes and corresponding file entites | ||||
|      */ | ||||
|     public function collect_classes( $path = null, $folder_name = AI1EC_PLUGIN_NAME ) { | ||||
|         // extension inject theit own base path | ||||
|         $path  = ( null === $path ) ? $this->_base_path : $path; | ||||
|         $names = $this->_locate_all_files( $path, $folder_name ); | ||||
|         $names = $this->_process_reflections( $names ); | ||||
|         $this->_cache( $path, $names ); | ||||
|         $this->_paths = array_merge( $this->_paths, $names ); | ||||
|         return $names; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Read/write cached classes map. | ||||
|      * | ||||
|      * If no entries are provided - acts as cache reader. | ||||
|      * | ||||
|      * @param array $entries Entries to write [optional=null] | ||||
|      * | ||||
|      * @return bool|array False on failure, true on success in writer | ||||
|      *         mode, cached entry in reader mode on success | ||||
|      */ | ||||
|     protected function _cache( $path, array $entries = null ) { | ||||
|         $cache_file = $this->_get_cache_file_path( $path ); | ||||
|         if ( $entries ) { | ||||
|             if ( | ||||
|                 is_file( $cache_file ) && | ||||
|                 ! is_writable( $cache_file ) || | ||||
|                 ! is_writable( dirname( $cache_file ) ) | ||||
|             ) { | ||||
|                 return false; | ||||
|             } | ||||
|             ksort( $entries, SORT_STRING ); | ||||
|             $content = array( | ||||
|                 '0registered' => $this->_registered, | ||||
|                 '1class_map'  => $entries, | ||||
|             ); | ||||
|             $content = var_export( $content, true ); | ||||
|             $content = $this->_sanitize_paths( $content, $path ); | ||||
|             $content = '<?php return ' . $content . ';'; | ||||
|             $this->_modified = false; | ||||
|             if ( | ||||
|                 false === file_put_contents( $cache_file, $content, LOCK_EX ) | ||||
|             ) { // LOCK_EX is not supported on all hosts (streams) | ||||
|                 return (bool)file_put_contents( $cache_file, $content ); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|         if ( ! is_file( $cache_file ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         $cached = ( require $cache_file ); | ||||
|         $this->_registered[$cache_file] = true; | ||||
|         return $cached['1class_map']; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the way classes must be instanciated. | ||||
|      * | ||||
|      * Retrieves from annotations the way classes must be retrieved. | ||||
|      * Possible values are | ||||
|      *  - new: a new instance is instantiated every time | ||||
|      *  - global: treat as singleton | ||||
|      *  - classname.method: a factory is used, specify it in that order | ||||
|      * The default if nothing is specified is global. | ||||
|      * | ||||
|      * @param ReflectionClass $class | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function _get_instantiator( ReflectionClass $class ) { | ||||
|         $doc = $class->getDocComment(); | ||||
|         preg_match_all( | ||||
|             '#^\s\*\s@instantiator\s+(.*)$#im', | ||||
|             $doc, | ||||
|             $annotations | ||||
|         ); | ||||
|         $instantiator = ''; | ||||
|         if ( isset( $annotations[1][0] ) ) { | ||||
|             $instantiator = rtrim( $annotations[1][0] ); | ||||
|         } | ||||
|         return $this->_convert_instantiator_for_map( $instantiator ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if the registry must be injected in the constructor. | ||||
|      * By convention the registry will always be the first parameter. | ||||
|      * | ||||
|      * @param ReflectionClass $class The class to check | ||||
|      * | ||||
|      * @return boolean true if the registry must be injected, false if not. | ||||
|      */ | ||||
|     protected function _inject_registry( ReflectionClass $class ) { | ||||
|         $contructor = $class->getConstructor(); | ||||
|         if ( null !== $contructor ) { | ||||
|             foreach ( $contructor->getParameters() as $param ) { | ||||
|                 $param_class = $param->getClass(); | ||||
|                 if ( $param_class instanceof ReflectionClass ) { | ||||
|                     $name = $param_class->getName(); | ||||
|                     if ( 'Ai1ec_Registry_Object' === $name ) { | ||||
|                         return true; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update the classmap with Reflection informations. | ||||
|      * | ||||
|      * @param array $names The class map. | ||||
|      * | ||||
|      * @return array The classmap with instantiator. | ||||
|      */ | ||||
|     protected function _process_reflections( array $names ) { | ||||
|         $this->_paths = array_merge( $this->_paths, $names ); | ||||
|         spl_autoload_register( array( $this, 'load' ) ); | ||||
|         foreach ( $names as $classname => &$data ) { | ||||
|             try { | ||||
|                 $class = new ReflectionClass( $data['c'] ); | ||||
|                 $data['i'] = $this->_get_instantiator( $class ); | ||||
|                 if ( $this->_inject_registry( $class ) ) { | ||||
|                     $data['r'] = 'y'; | ||||
|                 } | ||||
|             } catch ( ReflectionException $excpt ) { // unreachable class | ||||
|                 $data['i'] = self::NEWINST; | ||||
|             } | ||||
|         } | ||||
|         return $names; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts the long form to the short form where applicable. | ||||
|      * | ||||
|      * @param string $instantiator | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function _convert_instantiator_for_map( $instantiator ) { | ||||
|         if ( empty( $instantiator ) || 'global' === $instantiator ) { | ||||
|             return self::GLOBALINST; | ||||
|         } | ||||
|         if ( 'new' === $instantiator ) { | ||||
|             return self::NEWINST; | ||||
|         } | ||||
|         return $instantiator; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _locate_all_files method | ||||
|      * | ||||
|      * Scan file system, given path, recursively, to search for files and | ||||
|      * extract `class` names from them. | ||||
|      * | ||||
|      * @param string $path File system path to scan | ||||
|      * | ||||
|      * @return array Map of classes and corresponding files | ||||
|      */ | ||||
|     protected function _locate_all_files( $path, $folder_name ) { | ||||
|         $class_list = array(); | ||||
|         $directory    = opendir( $path ); | ||||
|         while ( false !== ( $entry = readdir( $directory ) ) ) { | ||||
|             if ( is_null( $entry ) || '.' === $entry{0} || 'tests' === $entry || strpos( strtolower( $entry ), 'icalcreator' ) !== false ) { | ||||
|                 continue; | ||||
|             } | ||||
|             $local_path = $path . DIRECTORY_SEPARATOR . $entry; | ||||
|             $base_path  = substr( $local_path, strlen( $this->_base_path ) ); | ||||
|  | ||||
|             if ( is_dir( $local_path ) ) { | ||||
|                 $class_list += $this->_locate_all_files( $local_path, $folder_name ); | ||||
|             } else { | ||||
|                 $class_list += $this->_extract_classes( $local_path, $folder_name ); | ||||
|             } | ||||
|         } | ||||
|         closedir( $directory ); | ||||
|         return $class_list; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _extract_classes method | ||||
|      * | ||||
|      * Extract names of classes from given file. | ||||
|      * So far only files ending in `.php` are processed and regular expression | ||||
|      * is used instead of `token_get_all` to increase parsing speed. | ||||
|      * | ||||
|      * @param string $file Name of file to scan | ||||
|      * | ||||
|      * @return array List of classes in file | ||||
|      */ | ||||
|     protected function _extract_classes( $file, $folder_name ) { | ||||
|             $class_list = array(); | ||||
|             if ( '.php' === strrchr( $file, '.' ) ) { | ||||
|                 $tokens = token_get_all( file_get_contents( $file ) ); | ||||
|                 for ( $i = 2, $count = count( $tokens ); $i < $count; $i++ ) { | ||||
|                     if ( | ||||
|                         T_CLASS      === $tokens[$i - 2][0] || | ||||
|                         T_INTERFACE  === $tokens[$i - 2][0] && | ||||
|                         T_WHITESPACE === $tokens[$i - 1][0] && | ||||
|                         T_STRING     === $tokens[$i][0] | ||||
|                     ) { | ||||
|                         $names = $this->_generate_loader_names( | ||||
|                             $tokens[$i][1], | ||||
|                             $file, | ||||
|                             $folder_name | ||||
|                         ); | ||||
|                         foreach ( $names as $name ) { | ||||
|                             $class_list[$name] = array( | ||||
|                                 'f' => $file, | ||||
|                                 'c' => $tokens[$i][1], | ||||
|                             ); | ||||
|                         } | ||||
|                     } | ||||
|  | ||||
|                 } | ||||
|             } | ||||
|             return $class_list; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate path name abbreviation. | ||||
|      * | ||||
|      * @param string $name Path name particle. | ||||
|      * | ||||
|      * @return string Abbreviated path name. | ||||
|      */ | ||||
|     public function path_name_shortening( $name ) { | ||||
|         return strtoupper( $name[0] ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _sanitize_paths method | ||||
|      * | ||||
|      * Sanitize paths before writing to cache file. | ||||
|      * Make sure, that constants and absolute paths are used independently | ||||
|      * of system used, thus making file cross-platform generatable. | ||||
|      * | ||||
|      * @param string $content   Output to be written to cache file. | ||||
|      * @param string $base_path Base path to use if not default. | ||||
|      * | ||||
|      * @return string Modified content, with paths replaced | ||||
|      */ | ||||
|     protected function _sanitize_paths( | ||||
|         $content, | ||||
|         $base_path  = null | ||||
|     ) { | ||||
|         $local_ds   = '/'; | ||||
|         $ai1ec_path = $this->_base_path; | ||||
|         $const_name = 'AI1EC_PATH'; | ||||
|         if ( null !== $base_path ) { | ||||
|             $ai1ec_path = $base_path; | ||||
|             $const_name = implode( array_map( | ||||
|                 array( $this, 'path_name_shortening' ), | ||||
|                 explode( '-', basename( $base_path ) ) | ||||
|             ) ) . '_PATH'; | ||||
|             $const_name = str_replace( 'AIOEC', 'AI1EC', $const_name ); | ||||
|         } | ||||
|         if ( '\\' === DIRECTORY_SEPARATOR ) { | ||||
|             $local_ds   = '\\\\'; | ||||
|             $ai1ec_path = str_replace( '\\', '\\\\', $ai1ec_path ); | ||||
|         } | ||||
|         $content = str_replace( | ||||
|             '\'' . $ai1ec_path . $local_ds, | ||||
|             $const_name . ' . DIRECTORY_SEPARATOR . \'', | ||||
|             $content | ||||
|         ); | ||||
|         $content = str_replace( | ||||
|             $local_ds, | ||||
|             '\' . DIRECTORY_SEPARATOR . \'', | ||||
|             $content | ||||
|         ); | ||||
|         return $content; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate all the alternatives name that the loaded recognize. | ||||
|      * | ||||
|      * For example: | ||||
|      * The class Ai1ec_Html_Helper can be loaded as | ||||
|      * - html.helper ( the path to the file ) | ||||
|      * - Ai1ec_Html_Helper ( needed by Autoload ) | ||||
|      * | ||||
|      * @param $class string the original name of the class. | ||||
|      * @param $file string the file | ||||
|      * | ||||
|      * @return array An array of strings with the availables names. | ||||
|      */ | ||||
|     protected function _generate_loader_names( $class, $file, $folder_name ) { | ||||
|         $names  = array( $class ); | ||||
|         // Remove the extension. | ||||
|         $file   = substr( $file, 0, strrpos( $file , '.' ) ); | ||||
|         $file   = strtr( $file, array( '//' => '/' ) ); | ||||
|         // Get just the meaningful data. | ||||
|         $relative_path_position = strrpos( // offset of base directory | ||||
|             $file, | ||||
|             DIRECTORY_SEPARATOR . $folder_name . DIRECTORY_SEPARATOR | ||||
|         ); | ||||
|         $file   = substr( | ||||
|             $file, | ||||
|             strpos( // cut to app|lib|vendor|... | ||||
|                 $file, | ||||
|                 DIRECTORY_SEPARATOR, | ||||
|                 $relative_path_position + strlen( $folder_name ) + 2 | ||||
|             ) | ||||
|         ); | ||||
|         $names[] = str_replace( | ||||
|             DIRECTORY_SEPARATOR, | ||||
|             '.', | ||||
|             trim( $file, DIRECTORY_SEPARATOR ) | ||||
|         ); | ||||
|         return $names; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Translate the key to the actual class name if any | ||||
|      * | ||||
|      * @param $key string Key requested to initialize | ||||
|      * | ||||
|      * @return array|null Array of the class, or null if none is found | ||||
|      */ | ||||
|     public function resolve_class_name( $key ) { | ||||
|         if ( ! isset( $this->_paths[$key] ) ) { | ||||
|             return null; | ||||
|         } | ||||
|         return $this->_paths[$key]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update cache if object was modified | ||||
|      * | ||||
|      * @return void Destructor does not return | ||||
|      */ | ||||
|     public function __destruct() { | ||||
|         if ( $this->_modified ) { | ||||
|             $this->_cache( $this->_paths ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convenience wrapper to detect internal extension file path. | ||||
|      * | ||||
|      * @param string $path Absolute path to extension base directory. | ||||
|      * | ||||
|      * @return bool Success loading extension classes. | ||||
|      */ | ||||
|     public function register_extension_map( $path ) { | ||||
|         return $this->register_map( $this->_get_cache_file_path( $path ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register external class map to use in loading sequence | ||||
|      * | ||||
|      * @param string $file Path to class map | ||||
|      * | ||||
|      * @return bool Success loading it | ||||
|      */ | ||||
|     public function register_map( $file ) { | ||||
|         if ( | ||||
|             isset( $this->_registered[$file] ) && ( | ||||
|                 ! defined( 'AI1EC_DEBUG' ) || | ||||
|                 ! AI1EC_DEBUG | ||||
|             ) | ||||
|         ) { | ||||
|             return true; | ||||
|         } | ||||
|         if ( ! is_file( $file ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         $entries = ( require $file ); | ||||
|         foreach ( $entries['1class_map'] as $class_name => $properties ) { | ||||
|             $this->_paths[$class_name] = $properties; | ||||
|         } | ||||
|         $this->_registered[$file] = true; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
|      * | ||||
|      * Initialize the loader creating the map of available classes, if the | ||||
|      * AI1EC_DEBUG constants is true the list is regenerated | ||||
|      * | ||||
|      * @throws Exception if the map is invalid | ||||
|      * | ||||
|      * @return void Constructor does not return | ||||
|      */ | ||||
|     public function __construct( $base_path ) { | ||||
|         $this->_base_path = $base_path; | ||||
|         $this->_prefix = explode( '_', __CLASS__ ); | ||||
|         $this->_prefix = $this->_prefix[0]; | ||||
|         $class_map = $this->_cache( $base_path ); | ||||
|         if ( | ||||
|             ! is_array( $class_map ) || | ||||
|             defined( 'AI1EC_DEBUG' ) && AI1EC_DEBUG | ||||
|         ) { | ||||
|             if ( ! defined( 'AI1EC_DEBUG' ) || ! AI1EC_DEBUG ) { | ||||
|                 // using generic `Ai1ec_Exception` as others are, potentially, | ||||
|                 // not resolved at this time. | ||||
|                 throw new Ai1ec_Exception( | ||||
|                     'Generated class map is invalid: ' . | ||||
|                     var_export( $class_map, true ) . | ||||
|                     '. Please delete lib/bootstrap/loader-map.php (if it exists), make ' . | ||||
|                     'sure lib/bootstrap/ is writable by the web server, and enable ' . | ||||
|                     'debug mode by setting AI1EC_DEBUG to true (then back to false ' . | ||||
|                     'when done).' | ||||
|                 ); | ||||
|             } | ||||
|             $class_map = $this->collect_classes(); | ||||
|         } | ||||
|         $this->_paths = $class_map; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to get cache file path given path to plugin. | ||||
|      * | ||||
|      * @param string $path Path to plugin directory. | ||||
|      * | ||||
|      * @return string Absolute path to loader cache file. | ||||
|      */ | ||||
|     protected function _get_cache_file_path( $path ) { | ||||
|         return $path . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . | ||||
|             'bootstrap' . DIRECTORY_SEPARATOR . 'loader-map.php'; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,48 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Application Registry: handles application wide variables. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Object | ||||
|  */ | ||||
| class Ai1ec_Registry_Application implements Ai1ec_Registry { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object | ||||
|      */ | ||||
|     protected $_registry; | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $_environment = array(); | ||||
|  | ||||
|     /** | ||||
|      * The contructor method. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      */ | ||||
|     function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         $this->_registry = $registry; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Registry::get() | ||||
|      */ | ||||
|     public function get( $key ) { | ||||
|         if ( ! isset ( $this->_environment[$key] ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         return $this->_environment[$key]; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Registry::set() | ||||
|      */ | ||||
|     public function set( $key, $value ) { | ||||
|         $this->_environment[$key] = $value; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The basic registry interface. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Object | ||||
|  */ | ||||
| interface Ai1ec_Registry { | ||||
|  | ||||
|     /** | ||||
|      * Retrieves the key from the registry | ||||
|      * | ||||
|      * @param string $key | ||||
|      * | ||||
|      * @return mixed the value associated to the key. | ||||
|      */ | ||||
|     public function get( $key ); | ||||
|  | ||||
|     /** | ||||
|      * Set the key into the registry. | ||||
|      * | ||||
|      * @param string $key | ||||
|      * @param mixed $value | ||||
|      */ | ||||
|     public function set( $key, $value ); | ||||
| } | ||||
| @@ -0,0 +1,236 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Object Registry: get instance of requested and optionally registered object. | ||||
|  * | ||||
|  * Object (instance of a class) is generater, or returned from internal cache | ||||
|  * if it was requested and instantiated before. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Object | ||||
|  */ | ||||
| class Ai1ec_Registry_Object implements Ai1ec_Registry { | ||||
|  | ||||
|     /** | ||||
|     * @var array The internal objects cache | ||||
|     */ | ||||
|     private $_objects = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Loader The Ai1ec_Loader instance used by the registry | ||||
|      */ | ||||
|     private $_loader  = null; | ||||
|  | ||||
|     /** | ||||
|      * Get the loader ( used by extensions ) | ||||
|      * | ||||
|      * @return Ai1ec_Loader | ||||
|      */ | ||||
|     public function get_loader() { | ||||
|         return $this->_loader; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method prepares environment for easier extension integration. | ||||
|      * | ||||
|      * NOTICE: only extensions, that follow internal guideliness for | ||||
|      * files and methods organization must call this hook. | ||||
|      * | ||||
|      * Absolute path to extensions directory is autodetected, if not | ||||
|      * provided, appending plugins name to path to plugins dir. | ||||
|      * | ||||
|      * @param string $name Name of the extension. | ||||
|      * @param string $path Absolute path to extension directory. | ||||
|      * | ||||
|      * @return Ai1ec_Registry_Object Instance of self for chaining. | ||||
|      */ | ||||
|     public function extension_acknowledge( $name, $path = null ) { | ||||
|         if ( null === $path ) { | ||||
|             $path = AI1EC_EXTENSIONS_BASEDIR . $name; | ||||
|         } | ||||
|         if ( AI1EC_DEBUG ) { | ||||
|             $this->_loader->collect_classes( $path, $name ); | ||||
|         } | ||||
|         $this->get( 'theme.loader' )->register_extension( | ||||
|             $path, | ||||
|             plugins_url( $name ) | ||||
|         ); | ||||
|         $this->_loader->register_extension_map( $path ); | ||||
|         do_action( 'ai1ec_extension_loaded', $path, $name ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get class instance. | ||||
|      * | ||||
|      * Return an instance for the requested key, this method has an internal | ||||
|      * cache. | ||||
|      * | ||||
|      * @param string $key Name of previously registered object or parseable | ||||
|      *                    class name | ||||
|      * | ||||
|      * @return object Instance of the requested class | ||||
|      */ | ||||
|     public function get( $key ) { | ||||
|         $class_data = $this->_loader->resolve_class_name( $key ); | ||||
|         if ( ! $class_data ) { | ||||
|             throw new Ai1ec_Bootstrap_Exception( | ||||
|                 'Unable to resolve class for "' . $key . '"' | ||||
|             ); | ||||
|         } | ||||
|         $class_name   = $class_data['c']; | ||||
|         if ( | ||||
|             'Ai1ec_Event' === $class_name && | ||||
|             $this->get( 'compatibility.check' )->use_backward_compatibility() | ||||
|         ) { | ||||
|             $class_name = 'Ai1ec_Event_Compatibility'; | ||||
|         } | ||||
|         $instantiator = $class_data['i']; | ||||
|         $args         = array_slice( func_get_args(), 1 ); | ||||
|         if ( isset ( $class_data['r'] ) ) { | ||||
|             array_unshift( $args, $this ); | ||||
|         } | ||||
|         if ( Ai1ec_Loader::NEWINST === $instantiator ) { | ||||
|             return $this->initiate( | ||||
|                 $class_name, | ||||
|                 $args | ||||
|             ); | ||||
|         } | ||||
|         if ( Ai1ec_Loader::GLOBALINST === $instantiator ) { | ||||
|             if ( ! isset( $this->_objects[$class_name] ) ) { | ||||
|                 // Ask the loader to load the required files to avoid autoloader | ||||
|                 $this->_loader->load( $class_name ); | ||||
|                 $this->_objects[$class_name] = $this->initiate( | ||||
|                     $class_name, | ||||
|                     $args | ||||
|                 ); | ||||
|             } | ||||
|             return $this->_objects[$class_name]; | ||||
|         } | ||||
|         // Ok it's a factory. | ||||
|         $factory = explode( '.', $instantiator ); | ||||
|         return $this->dispatch( | ||||
|             $factory[0], | ||||
|             $factory[1], | ||||
|             $args | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Allow to set previously created globally accessible class instance. | ||||
|      * | ||||
|      * @param string $name   Class name to be used. | ||||
|      * @param object $object Actual instance of class above. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function inject_object( $name, $object ) { | ||||
|         if ( ! is_object( $object ) || ! ( $object instanceof $name ) ) { | ||||
|             throw new Ai1ec_Bootstrap_Exception( | ||||
|                 'Attempt to inject not an object / invalid object.' | ||||
|             ); | ||||
|         } | ||||
|         $this->_objects[$name] = $object; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Registry::set() | ||||
|      */ | ||||
|     public function set( $key, $value ) { | ||||
|         // The set method allows to inject classes from extensions into the registry. | ||||
|         new Ai1ec_Bootstrap_Exception( 'Not implemented' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Instanciate the class given the class names and arguments. | ||||
|      * | ||||
|      * @param string $class_name The name of the class to instanciate. | ||||
|      * @param array  $argv       An array of aguments for construction. | ||||
|      * | ||||
|      * @return object A new instance of the requested class | ||||
|      */ | ||||
|     public function initiate( $class_name, array $argv = array() ) { | ||||
|         switch ( count( $argv ) ) { | ||||
|             case 0: | ||||
|                 return new $class_name(); | ||||
|  | ||||
|             case 1: | ||||
|                 return new $class_name( $argv[0] ); | ||||
|  | ||||
|             case 2: | ||||
|                 return new $class_name( $argv[0], $argv[1] ); | ||||
|  | ||||
|             case 3: | ||||
|                 return new $class_name( $argv[0], $argv[1], $argv[2] ); | ||||
|  | ||||
|             case 4: | ||||
|                 return new $class_name( $argv[0], $argv[1], $argv[2], $argv[3] ); | ||||
|  | ||||
|             case 5: | ||||
|                 return new $class_name( $argv[0], $argv[1], $argv[2], $argv[3], $argv[4] ); | ||||
|         } | ||||
|  | ||||
|         $reflected = new ReflectionClass( $class_name ); | ||||
|         return $reflected->newInstanceArgs( $argv ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * A call_user_func_array alternative. | ||||
|      * | ||||
|      * @param string $class | ||||
|      * @param string $method | ||||
|      * @param array $params | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function dispatch( $class, $method, $params = array() ) { | ||||
|         if ( empty( $class ) ) { | ||||
|             switch ( count( $params)  ) { | ||||
|                 case 0: | ||||
|                     return $method(); | ||||
|                 case 1: | ||||
|                     return $method( $params[0] ); | ||||
|                 case 2: | ||||
|                     return $method( $params[0], $params[1] ); | ||||
|                 case 3: | ||||
|                     return $method( $params[0], $params[1], $params[2] ); | ||||
|                 default: | ||||
|                     return call_user_func_array( $method, $params ); | ||||
|             } | ||||
|         } else { | ||||
|             // get an instance of the class | ||||
|             $class = $this->get( $class ); | ||||
|             switch ( count( $params)  ) { | ||||
|                 case 0: | ||||
|                     return $class->{$method}(); | ||||
|                 case 1: | ||||
|                     return $class->{$method}( $params[0] ); | ||||
|                 case 2: | ||||
|                     return $class->{$method}( $params[0], $params[1] ); | ||||
|                 case 3: | ||||
|                     return $class->{$method}( $params[0], $params[1], $params[2] ); | ||||
|                 default: | ||||
|                     return call_user_func_array( | ||||
|                         array( $class, $method ), | ||||
|                         $params | ||||
|                     ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
|      * | ||||
|      * Initialize the Registry | ||||
|      * | ||||
|      * @param Ai1ec_Loader $ai1ec_loader Instance of Ai1EC classes loader | ||||
|      * | ||||
|      * @return void Constructor does not return | ||||
|      */ | ||||
|     public function __construct( $ai1ec_loader ) { | ||||
|         $this->_loader = $ai1ec_loader; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										13
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/exception/not-set.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/exception/not-set.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Exception thrown when reading from cache. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Exception | ||||
|  */ | ||||
| class Ai1ec_Cache_Not_Set_Exception extends Ai1ec_Exception { | ||||
|  | ||||
| } | ||||
							
								
								
									
										13
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/exception/write.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/exception/write.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Exception thrown when failing to write to cache. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Exception | ||||
|  */ | ||||
| class Ai1ec_Cache_Write_Exception extends Ai1ec_Exception { | ||||
|  | ||||
| } | ||||
							
								
								
									
										52
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/interface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/interface.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Interface for cache engines. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Cache | ||||
|  */ | ||||
| interface Ai1ec_Cache_Interface { | ||||
|  | ||||
|     /** | ||||
|      * Set entry to cache. | ||||
|      * | ||||
|      * @param string $key   Key for which value must be stored. | ||||
|      * @param mixed  $value Actual value to store. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function set( $key, $value ); | ||||
|  | ||||
|     /** | ||||
|      * Add entry to cache if one does not exist. | ||||
|      * | ||||
|      * @param string $key   Key for which value must be stored. | ||||
|      * @param mixed  $value Actual value to store. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function add( $key, $value ); | ||||
|  | ||||
|     /** | ||||
|      * Retrieve value from cache. | ||||
|      * | ||||
|      * @param string $key     Key for which to retrieve value. | ||||
|      * @param mixed  $default Value to return if none found. | ||||
|      * | ||||
|      * @return mixed Previously stored or $default value. | ||||
|      */ | ||||
|     public function get( $key, $default = NULL ); | ||||
|  | ||||
|     /** | ||||
|      * Delete value from cache. | ||||
|      * | ||||
|      * @param string $key Key for value to remove. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function delete( $key ); | ||||
|  | ||||
| } | ||||
							
								
								
									
										102
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/memory.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/memory.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * In-memory cache storage engine. | ||||
|  * | ||||
|  * Store values in memory, for use in a single session scope. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache | ||||
|  */ | ||||
| final class Ai1ec_Cache_Memory implements Ai1ec_Cache_Interface { | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of memory entries. | ||||
|      */ | ||||
|     protected $_entries          = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var int Number of entries to hold in map. | ||||
|      */ | ||||
|     protected $_limit            = 0; | ||||
|  | ||||
|     /** | ||||
|      * Constructor initiates stack (memory) length. | ||||
|      * | ||||
|      * @param int $limit Number of entries specific to this location. | ||||
|      * | ||||
|      * @return void Constructor does not return. | ||||
|      */ | ||||
|     public function __construct( $limit = 50 ) { | ||||
|         $limit = (int)$limit; | ||||
|         if ( $limit < 10 ) { | ||||
|             $limit = 10; | ||||
|         } | ||||
|         $this->_limit = $limit; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Write data to memory under given key. | ||||
|      * | ||||
|      * @param string $key   Key under which value must be written. | ||||
|      * @param mixed  $value Value to associate with given key. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function set( $key, $value ) { | ||||
|         if ( count( $this->_entries ) > $this->_limit ) { | ||||
|             array_shift( $this->_entries ); // discard | ||||
|         } | ||||
|         $this->_entries[$key] = $value; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add data to memory under given key, if it does not exist. | ||||
|      * | ||||
|      * @param string $key   Key under which value must be added. | ||||
|      * @param mixed  $value Value to associate with given key. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function add( $key, $value ) { | ||||
|         if ( isset( $this->_entries[$key] ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         return $this->set( $key, $value ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve data from memory, stored under specified key. | ||||
|      * | ||||
|      * @param string $key     Key under which value is expected to be. | ||||
|      * @param mixed  $default Value to return if nothing is found. | ||||
|      * | ||||
|      * @return mixed Found value or {$default}. | ||||
|      */ | ||||
|     public function get( $key, $default = NULL ) { | ||||
|         if ( ! isset( $this->_entries[$key] ) ) { | ||||
|             return $default; | ||||
|         } | ||||
|         return $this->_entries[$key]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Remove entry from cache table. | ||||
|      * | ||||
|      * @param string $key Key to be removed. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function delete( $key ) { | ||||
|         if ( ! isset( $this->_entries[$key] ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         unset( $this->_entries[$key] ); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										47
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/abstract.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/abstract.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Base class for caching strategy. | ||||
|  * | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Strategy | ||||
|  */ | ||||
| abstract class Ai1ec_Cache_Strategy extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Retrieves the data store for the passed key | ||||
|      * | ||||
|      * @param string $key | ||||
|      * @throws Ai1ec_Cache_Not_Set_Exception if the key was not set | ||||
|      */ | ||||
|     abstract public function get_data( $key ); | ||||
|  | ||||
|     /** | ||||
|      * Write the data to the persistence Layer | ||||
|      * | ||||
|      * @throws Ai1ec_Cache_Write_Exception | ||||
|      * @param string $key | ||||
|      * @param string $value | ||||
|      */ | ||||
|     abstract public function write_data( $key, $value ); | ||||
|  | ||||
|     /** | ||||
|      * Deletes the data associated with the key from the persistence layer. | ||||
|      * | ||||
|      * @param string $key | ||||
|      */ | ||||
|     abstract public function delete_data( $key ); | ||||
|  | ||||
|     /** | ||||
|      * Delete multiple cache entries matching given pattern | ||||
|      * | ||||
|      * @param string $pattern Scalar pattern, which shall match key | ||||
|      * | ||||
|      * @return int Count of entries deleted | ||||
|      */ | ||||
|     abstract public function delete_matching( $pattern ); | ||||
|  | ||||
| } | ||||
							
								
								
									
										118
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/apc.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/apc.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Concrete class for APC caching strategy. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Strategy | ||||
|  */ | ||||
| class Ai1ec_Cache_Strategy_Apc extends Ai1ec_Cache_Strategy { | ||||
|  | ||||
|     /** | ||||
|      * is_available method | ||||
|      * | ||||
|      * Checks if APC is available for use. | ||||
|      * Following pre-requisites are checked: APC functions availability, | ||||
|      * APC is enabled via configuration and PHP is not running in CGI. | ||||
|      * | ||||
|      * @return bool Availability | ||||
|      */ | ||||
|     static public function is_available() { | ||||
|         return function_exists( 'apc_store' ) && | ||||
|                function_exists( 'apc_fetch' ) && | ||||
|                ini_get( 'apc.enabled' ) && | ||||
|                ( false === strpos( php_sapi_name(), 'cgi' ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Get_Data_From_Cache::get_data() | ||||
|      * | ||||
|      */ | ||||
|     public function get_data( $dist_key ) { | ||||
|         $key  = $this->_key( $dist_key ); | ||||
|         $data = apc_fetch( $key ); | ||||
|         if ( false === $data ) { | ||||
|             throw new Ai1ec_Cache_Not_Set_Exception( "$dist_key not set" ); | ||||
|         } | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::write_data() | ||||
|      * | ||||
|      */ | ||||
|     public function write_data( $dist_key, $value ) { | ||||
|         $key          = $this->_key( $dist_key ); | ||||
|         $store_method = 'apc_add'; | ||||
|         if ( false !== ( $existing = apc_fetch( $key ) ) ) { | ||||
|             if ( $value === $existing ) { | ||||
|  | ||||
|                 return true; | ||||
|             } | ||||
|             $store_method = 'apc_store'; | ||||
|         } elseif ( false === function_exists( $store_method ) ) { | ||||
|  | ||||
|             $store_method = 'apc_store'; | ||||
|         } | ||||
|         if ( false === $store_method( $key, $value ) ) { | ||||
|             try { | ||||
|                 if ( $value !== $this->get_data( $key ) ) { | ||||
|                     throw new Ai1ec_Cache_Not_Set_Exception( 'Data mis-match' ); | ||||
|                 } | ||||
|             } catch ( Ai1ec_Cache_Not_Set_Exception $excpt ) { | ||||
|  | ||||
|                 throw new Ai1ec_Cache_Not_Set_Exception( | ||||
|                     'Failed to write ' . $dist_key . ' to APC cache' | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_data() | ||||
|      */ | ||||
|     public function delete_data( $key ) { | ||||
|         if ( false === apc_delete( $this->_key( $key ) ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_matching() | ||||
|      */ | ||||
|     public function delete_matching( $pattern ) { | ||||
|         // not implemented - concider flushing APC cache | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _key method | ||||
|      * | ||||
|      * Make sure we are on the safe side - in case of multi-instances | ||||
|      * environment some prefix is required. | ||||
|      * | ||||
|      * @param string $key Key to be used against APC cache | ||||
|      * | ||||
|      * @return string Key with prefix prepended | ||||
|      */ | ||||
|     protected function _key( $key ) { | ||||
|         static $prefix = null; | ||||
|         if ( NULL === $prefix ) { | ||||
|             $prefix = substr( md5( ai1ec_get_site_url() ), 0, 8 ); | ||||
|         } | ||||
|         if ( 0 !== strncmp( $key, $prefix, 8 ) ) { | ||||
|             $key = $prefix . $key; | ||||
|         } | ||||
|         return $key; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										102
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/db.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/db.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Concrete class for DB caching strategy. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Strategy | ||||
|  */ | ||||
| class Ai1ec_Cache_Strategy_Db extends Ai1ec_Cache_Strategy { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Option Instance of database adapter | ||||
|      */ | ||||
|     private $model_option; | ||||
|  | ||||
|     public function __construct( Ai1ec_Registry_Object $registry, Ai1ec_Option $option ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->model_option = $option; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Get_Data_From_Cache::get_data() | ||||
|      * | ||||
|      */ | ||||
|     public function get_data( $key ) { | ||||
|         $key  = $this->_key( $key ); | ||||
|         $data = $this->model_option->get( $key ); | ||||
|         if ( false === $data ) { | ||||
|             throw new Ai1ec_Cache_Not_Set_Exception( | ||||
|                 'No data under \'' . $key . '\' present' | ||||
|             ); | ||||
|         } | ||||
|         return maybe_unserialize( $data ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::write_data() | ||||
|      * | ||||
|      */ | ||||
|     public function write_data( $key, $value ) { | ||||
|         $result = $this->model_option->set( | ||||
|             $this->_key( $key ), | ||||
|             maybe_serialize( $value ) | ||||
|         ); | ||||
|         if ( false === $result ) { | ||||
|             throw new Ai1ec_Cache_Write_Exception( | ||||
|                 'An error occured while saving data to \'' . $key . '\'' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_data() | ||||
|      */ | ||||
|     public function delete_data( $key ) { | ||||
|         return $this->model_option->delete( | ||||
|             $this->_key( $key ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_matching() | ||||
|      */ | ||||
|     public function delete_matching( $pattern ) { | ||||
|         $db = $this->_registry->get( 'dbi.dbi' ); | ||||
|         $sql_query = $db->prepare( | ||||
|             'SELECT option_name FROM ' . $db->get_table_name( 'options' ) . | ||||
|             ' WHERE option_name LIKE %s', | ||||
|             '%%' . $pattern . '%%' | ||||
|         ); | ||||
|         $keys = $db->get_col( $sql_query ); | ||||
|         foreach ( $keys as $key ) { | ||||
|             $this->model_option->delete( $key ); | ||||
|         } | ||||
|         return count( $keys ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _key method | ||||
|      * | ||||
|      * Get safe key name to use within options API | ||||
|      * | ||||
|      * @param string $key Key to sanitize | ||||
|      * | ||||
|      * @return string Safe to use key | ||||
|      */ | ||||
|     protected function _key( $key ) { | ||||
|         if ( strlen( $key ) > 53 ) { | ||||
|             $hash = md5( $key ); | ||||
|             $key  = substr( $key, 0, 16 ) . '_' . $hash; | ||||
|         } | ||||
|         return $key; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										177
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/file.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										177
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/file.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,177 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Concrete class for file caching strategy. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Strategy | ||||
|  */ | ||||
| class Ai1ec_Cache_Strategy_File extends Ai1ec_Cache_Strategy { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $_cache_dir; | ||||
|  | ||||
|     private $_cache_url; | ||||
|  | ||||
|     public function __construct( Ai1ec_Registry_Object $registry, array $cache_dir ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_cache_dir = $cache_dir['path']; | ||||
|         $this->_cache_url = $cache_dir['url']; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Get_Data_From_Cache::get_data() | ||||
|      * | ||||
|      */ | ||||
|     public function get_data( $file ) { | ||||
|         $file = $this->_get_file_name( $file ); | ||||
|         if ( ! $file || ! file_exists( $this->_cache_dir . $file ) ) { | ||||
|             throw new Ai1ec_Cache_Not_Set_Exception( | ||||
|                 'File \'' . $file . '\' does not exist' | ||||
|             ); | ||||
|         } | ||||
|         return maybe_unserialize( | ||||
|             file_get_contents( $this->_cache_dir . $file ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::write_data() | ||||
|      * | ||||
|      */ | ||||
|     public function write_data( $filename, $value ) { | ||||
|         $filename = $this->_safe_file_name( $filename ); | ||||
|         $value    = maybe_serialize( $value ); | ||||
|  | ||||
|         $result = $this->_registry->get( 'filesystem.checker' )->put_contents( | ||||
|             $this->_cache_dir . $filename, | ||||
|             $value | ||||
|         ); | ||||
|         if ( false === $result ) { | ||||
|             $message = 'An error occured while saving data to \'' . | ||||
|                 $this->_cache_dir . $filename . '\''; | ||||
|             throw new Ai1ec_Cache_Write_Exception( $message ); | ||||
|         } | ||||
|         return array( | ||||
|             'path' => $this->_cache_dir . $filename, | ||||
|             'url'  => $this->_cache_url . $filename, | ||||
|             'file' => $filename, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_data() | ||||
|      */ | ||||
|     public function delete_data( $filename ) { | ||||
|         // Check if file exists. It might not exists if you switch themes | ||||
|         // twice without never rendering the CSS | ||||
|         $filename = $this->_safe_file_name( $filename ); | ||||
|         if ( | ||||
|             file_exists( $this->_cache_dir . $filename ) && | ||||
|             false === unlink( $this->_cache_dir . $filename ) | ||||
|         ) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_matching() | ||||
|      */ | ||||
|     public function delete_matching( $pattern ) { | ||||
|         $dirhandle = opendir( $this->_cache_dir ); | ||||
|         if ( false === $dirhandle ) { | ||||
|             return 0; | ||||
|         } | ||||
|         $count = 0; | ||||
|         while ( false !== ( $entry = readdir( $dirhandle ) ) ) { | ||||
|             if ( '.' !== $entry{0} && false !== strpos( $entry, $pattern ) ) { | ||||
|                 if ( unlink( $this->_cache_dir . $entry ) ) { | ||||
|                     ++$count; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         closedir( $dirhandle ); | ||||
|         return $count; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the extension for the file if required | ||||
|      * | ||||
|      * @param string $file | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function _get_extension_for_file( $file ) { | ||||
|         $extensions = array( | ||||
|             'ai1ec_parsed_css' => '.css' | ||||
|         ); | ||||
|         if ( isset( $extensions[$file] ) ) { | ||||
|             return $extensions[$file]; | ||||
|         } | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Tries to get the stored filename | ||||
|      * | ||||
|      * @param string $file | ||||
|      * | ||||
|      * @return boolean | string | ||||
|      */ | ||||
|     protected function _get_file_name( $file ) { | ||||
|         static $file_map = array( | ||||
|             'ai1ec_parsed_css' => 'ai1ec_filename_css', | ||||
|         ); | ||||
|         if ( isset ( $file_map[$file] ) ) { | ||||
|             return $this->_registry->get( 'model.option' )->get( $file_map[$file] ); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _safe_file_name method | ||||
|      * | ||||
|      * Generate safe file name for any storage case. | ||||
|      * | ||||
|      * @param string $file File name currently supplied | ||||
|      * | ||||
|      * @return string Sanitized file name | ||||
|      */ | ||||
|     protected function _safe_file_name( $file ) { | ||||
|         static $prefix = null; | ||||
|         $extension = $this->_get_extension_for_file( $file ); | ||||
|         if ( null === $prefix ) { | ||||
|             // always include site_url when there is more than one | ||||
|             $pref_string = ai1ec_site_url(); | ||||
|             if ( ! AI1EC_DEBUG ) { | ||||
|                 // address multiple re-saves for a single version | ||||
|                 // i.e. when theme settings are being edited | ||||
|                 $pref_string .= mt_rand(); | ||||
|             } | ||||
|             $prefix = substr( md5( $pref_string ), 0, 8 ); | ||||
|         } | ||||
|         $length = strlen( $file ); | ||||
|         if ( ! ctype_alnum( $file ) ) { | ||||
|             $file = preg_replace( | ||||
|                 '|_+|', | ||||
|                 '_', | ||||
|                 preg_replace( '|[^a-z0-9\-,_]|', '_', $file ) | ||||
|             ); | ||||
|         } | ||||
|         if ( 0 !== strncmp( $file, $prefix, 8 ) ) { | ||||
|             $file = $prefix . '_' . $file; | ||||
|         } | ||||
|         return $file . $extension; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										106
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/persistence-context.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/persistence-context.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The context class which handles the caching strategy. | ||||
|  * | ||||
|  * @instantiator Ai1ec_Factory_Strategy.create_persistence_context | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Strategy | ||||
|  */ | ||||
| class Ai1ec_Persistence_Context { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $key_for_persistance; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var Ai1ec_Cache_Strategy | ||||
|      */ | ||||
|     private $cache_strategy; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param string $key_for_peristance | ||||
|      * @param Ai1ec_Cache_Strategy $cache_strategy | ||||
|      */ | ||||
|     public function __construct( | ||||
|         $key_for_persistance, | ||||
|         Ai1ec_Cache_Strategy $cache_strategy | ||||
|     ) { | ||||
|         $this->cache_strategy = $cache_strategy; | ||||
|         $this->key_for_persistance = $key_for_persistance; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @throws Ai1ec_Cache_Not_Set_Exception | ||||
|      * @return string | ||||
|      */ | ||||
|     public function get_data_from_persistence() { | ||||
|         try { | ||||
|             $data = $this->cache_strategy->get_data( $this->key_for_persistance ); | ||||
|         } | ||||
|         catch ( Ai1ec_Cache_Not_Set_Exception $e ) { | ||||
|             throw $e; | ||||
|         } | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Are we using file cache? | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_file_cache() { | ||||
|         return $this->cache_strategy instanceof Ai1ec_Cache_Strategy_File; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * write_data_to_persistence method | ||||
|      * | ||||
|      * Write data to persistance layer. If that fails - false is returned. | ||||
|      * Exceptions are suspended, as cache write is not a fatal error by no | ||||
|      * mean, thus shall not be escalated further. If you want exception to | ||||
|      * be escalated - use lower layer method directly. | ||||
|      * | ||||
|      * @param mixed $data Unserialized data to write | ||||
|      * | ||||
|      * @return boll Success | ||||
|      */ | ||||
|     public function write_data_to_persistence( $data ) { | ||||
|         $return = true; | ||||
|         try { | ||||
|             $return = $this->cache_strategy->write_data( | ||||
|                 $this->key_for_persistance, | ||||
|                 $data | ||||
|             ); | ||||
|         } catch ( Ai1ec_Cache_Write_Exception $e ) { | ||||
|             $return = false; | ||||
|         } | ||||
|         return $return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Deletes the data stored in cache. | ||||
|      */ | ||||
|     public function delete_data_from_persistence() { | ||||
|         $this->cache_strategy->delete_data( $this->key_for_persistance ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * delete_matching_entries_from_persistence method | ||||
|      * | ||||
|      * Delete matching entries from persistance. | ||||
|      * | ||||
|      * @param string $pattern Expected pattern, to be contained within key | ||||
|      * | ||||
|      * @return int Count of entries deleted | ||||
|      */ | ||||
|     public function delete_matching_entries_from_persistence( $pattern ) { | ||||
|         return $this->cache_strategy->delete_matching( $pattern ); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										59
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/void.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cache/strategy/void.php
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Concrete class for void caching strategy. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Cache.Strategy | ||||
|  */ | ||||
| class Ai1ec_Cache_Strategy_Void extends Ai1ec_Cache_Strategy { | ||||
|  | ||||
|     /** | ||||
|      * Checks if engine is available | ||||
|      * | ||||
|      * @return bool Always true | ||||
|      */ | ||||
|     static public function is_available() { | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Get_Data_From_Cache::get_data() | ||||
|      * | ||||
|      */ | ||||
|     public function get_data( $dist_key ) { | ||||
|         throw new Ai1ec_Cache_Not_Set_Exception( "'$dist_key' not set" ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::write_data() | ||||
|      * | ||||
|      */ | ||||
|     public function write_data( $dist_key, $value ) { | ||||
|         throw new Ai1ec_Cache_Not_Set_Exception( | ||||
|             'Failed to write \'' . $dist_key . '\' to void cache' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_data() | ||||
|      */ | ||||
|     public function delete_data( $key ) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @see Ai1ec_Write_Data_To_Cache::delete_matching() | ||||
|      */ | ||||
|     public function delete_matching( $pattern ) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,253 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The abstract class for the Calendar feeds tab. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Calendar-feed | ||||
|  */ | ||||
| abstract class Ai1ec_Connector_Plugin extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * An associative array where the keys are the name of the variables stored in the Settings object | ||||
|      * while the values are the description in the admin Panel | ||||
|      * | ||||
|      * @var array; | ||||
|      * | ||||
|      */ | ||||
|     protected $settings =  array(); | ||||
|  | ||||
|     /** | ||||
|      * An array of variables used by the plugin. Some of this variables are required: | ||||
|      *   title => The name of the tab in the calendar feeds settings | ||||
|      *   id    => The id used in the href of the tab. Must be unique | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $variables = array(); | ||||
|  | ||||
|     /** | ||||
|      * Handles any action the plugin requires when the users makes a POST in the calendar feeds page. | ||||
|      */ | ||||
|     abstract public function handle_feeds_page_post(); | ||||
|  | ||||
|     /** | ||||
|      * Get title to be used for tab human-identification. | ||||
|      * | ||||
|      * @return string Localized string. | ||||
|      */ | ||||
|     abstract public function get_tab_title(); | ||||
|  | ||||
|     /** | ||||
|      * Renders the content of the tab, where all the action takes place. | ||||
|      * | ||||
|      */ | ||||
|     abstract public function render_tab_content(); | ||||
|  | ||||
|     /** | ||||
|      * Let the plugin display an admin notice if neede. | ||||
|      * | ||||
|      */ | ||||
|     abstract public function display_admin_notices(); | ||||
|  | ||||
|     /** | ||||
|      * Run the code that cleans up the DB and CRON functions the plugin has installed. | ||||
|      * | ||||
|      */ | ||||
|     abstract public function run_uninstall_procedures(); | ||||
|  | ||||
|     /** | ||||
|      * Renders the HTML for the tabbed navigation | ||||
|      * | ||||
|      * @return void | ||||
|      *   Echoes the HTML string that act as tab header for the plugin | ||||
|      */ | ||||
|     public function render_tab_header() { | ||||
|         // Use the standard view helper | ||||
|         $args = array( | ||||
|             'title'  => $this->get_tab_title(), | ||||
|             'id'     => $this->variables['id'], | ||||
|         ); | ||||
|         $loader = $this->_registry->get( 'theme.loader' ); | ||||
|         $file   = $loader->get_file( 'plugins/tab_header.php', $args, true ); | ||||
|         $file->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the settings for the Plugin from the settings object. | ||||
|      * | ||||
|      * @param string $class_name The name of the Plugin for which we are | ||||
|      *                           retrieving the settings. | ||||
|      * | ||||
|      * @return array An associative array with the settings stored in settings | ||||
|      *               object or an empty array if settings are not set. | ||||
|      */ | ||||
|     protected function get_plugin_settings( $class_name ) { | ||||
|         $plugins_options = $this->_registry->get( 'model.settings' ) | ||||
|             ->get( 'plugins_options' ); | ||||
|         return isset( $plugins_options[$class_name] ) | ||||
|             ? $plugins_options[$class_name] | ||||
|             : array(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate an arry which contains all settings data. | ||||
|      * | ||||
|      * Only data that will be processed by the admin view is considered. | ||||
|      * | ||||
|      * @return array An array of Associative arrays that hold everything that's | ||||
|      *               needed to render the settings field in the admin section. | ||||
|      */ | ||||
|     protected function generate_settings_array_for_admin_view() { | ||||
|         // Get the plugin settings | ||||
|         $plugin_settings = $this->get_plugin_settings( get_class( $this ) ); | ||||
|         // This is the array that will be returned | ||||
|         $result = array(); | ||||
|         // Iterate over the settings | ||||
|         foreach ( $this->settings as $setting ) { | ||||
|             if ( $setting['admin-page'] === TRUE ) { | ||||
|                 // For each setting get it's value, description and id | ||||
|                 $result[] = array ( | ||||
|                     "setting-description" => __( $setting['description'], AI1EC_PLUGIN_NAME ), | ||||
|                     "setting-value"       => $plugin_settings[$setting['id']], | ||||
|                     "setting-id"          => $setting['id'], | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return $result; | ||||
|     } | ||||
|     /** | ||||
|      * Check that at least one of the settings has ha value. | ||||
|      * | ||||
|      * @param array $settings | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     protected function at_least_one_config_field_is_set( array $settings ) { | ||||
|         foreach ( $settings as $setting ) { | ||||
|             if( ! empty( $setting['setting-value'] ) ) { | ||||
|                 return TRUE; | ||||
|             } | ||||
|         } | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * If the plugin settings are not set they will be initialized to '' | ||||
|      * | ||||
|      */ | ||||
|     public function initialize_settings_if_not_set() { | ||||
|         // Get the class name. | ||||
|         $class_name = get_class( $this ); | ||||
|         $settings = $this->_registry->get( 'model.settings' ); | ||||
|         $plugins_options = $settings->get( 'plugins_options' ); | ||||
|         // Check if the options have been set | ||||
|         if ( ! isset( $plugins_options[$class_name] ) ) { | ||||
|             // If not set them. The key is the class name, the value is an associative array | ||||
|             $plugins_options[$class_name] = array(); | ||||
|             foreach ( $this->settings as $setting ) { | ||||
|                 $plugins_options[$class_name][$setting['id']] = ''; | ||||
|             } | ||||
|         } | ||||
|         $settings->set( 'plugins_options', $plugins_options ); | ||||
|     } | ||||
|     /** | ||||
|      * Retrieves the specified plugin setting | ||||
|      * | ||||
|      * @param string $variable_name The name of the variable to be retrieved | ||||
|      * | ||||
|      * @return mixed The variable value or FALSE if it's not set | ||||
|      */ | ||||
|     protected function get_plugin_variable( $variable_name ) { | ||||
|         $plugin_settings = $this->get_plugin_settings( get_class( $this ) ); | ||||
|         return isset( $plugin_settings[$variable_name] ) ? $plugin_settings[$variable_name] : FALSE; | ||||
|     } | ||||
|     /** | ||||
|      * Saves the variable int he plugin settings. | ||||
|      * | ||||
|      * @param string $variable_name The name of the variable to save. | ||||
|      * | ||||
|      * @param mixed $value The value of the variable to save. | ||||
|      */ | ||||
|     protected function save_plugin_variable( $variable_name, $value ) { | ||||
|         $this->save_plugin_settings( array( $variable_name => $value ), TRUE ); | ||||
|     } | ||||
|     /** | ||||
|      * Saves the plugin settings in the settings object | ||||
|      * | ||||
|      * @param array $data | ||||
|      *   An associative array of data to be saved | ||||
|      * | ||||
|      * @param boolean $not_from_setting_page | ||||
|      *   True if the function is not called from the setting page and must trigger the saving, false otherwise | ||||
|      */ | ||||
|     public function save_plugin_settings( array $data, $not_from_setting_page = FALSE ) { | ||||
|         $settings = $this->_registry->get( 'model.settings' ); | ||||
|         $plugins_options = $settings->get( 'plugins_options' ); | ||||
|         // Get the class name. | ||||
|         $class_name = get_class( $this ); | ||||
|         // We need to save the old settings so that we can then let the Facebook plugin check if the user changed app-id / secret | ||||
|         $old_settings = $this->get_plugin_settings( get_class( $this ) ); | ||||
|  | ||||
|  | ||||
|         // Check if the options have been set | ||||
|         if ( isset( $plugins_options[$class_name] ) ) { | ||||
|             // If the options for the plugin are set, iterate over the settings | ||||
|             foreach ( $this->settings as $setting ) { | ||||
|                 // Always check that the key is set, data can come from $_POST or from an internal call | ||||
|                 if( isset( $data[$setting['id']] ) ) { | ||||
|                     $plugins_options[$class_name][$setting['id']] = $data[$setting['id']]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         $settings->set( 'plugins_options', $plugins_options ); | ||||
|         if ( $not_from_setting_page === TRUE ) { | ||||
|             $settings->persist( ); | ||||
|         } else { | ||||
|             $old_settings['page'] = $data['page']; | ||||
|             do_action( "ai1ec-$class_name-postsave-setting", $old_settings ); | ||||
|         } | ||||
|     } | ||||
|     /** | ||||
|      * Prints an error message with standard formatting | ||||
|      * | ||||
|      * @param string $message The error message to be echoed to the screen | ||||
|      * | ||||
|      * @param boolean $close_tab_div TRUE if after the error message we should close the tab div | ||||
|      */ | ||||
|     protected function render_error_page( $message, $close_tab_div = FALSE ) { | ||||
|         $args = array(); | ||||
|         $args['message'] = $message; | ||||
|         $loader = $this->_registry->get( 'theme.loader' ); | ||||
|         $file = $loader->get_file( 'plugins/display_error_message.php', $args, true ); | ||||
|         $file->render(); | ||||
|         if( $close_tab_div === TRUE ) { | ||||
|             $this->render_closing_div_of_tab(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Renders the opening div of the tab and set the active status if this tab is the active one | ||||
|      * | ||||
|      * @param string $active_feed the tab that should be active. | ||||
|      */ | ||||
|     protected function render_opening_div_of_tab() { | ||||
|         $args = array( | ||||
|             'id' => $this->variables['id'], | ||||
|         ); | ||||
|         $loader = $this->_registry->get( 'theme.loader' ); | ||||
|         $file = $loader->get_file( 'plugins/render_opening_div.php', $args, true ); | ||||
|         $file->render(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * This renders the closing div of the tab. | ||||
|      */ | ||||
|     protected function render_closing_div_of_tab(  ) { | ||||
|         echo '</div>'; | ||||
|     } | ||||
|  | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,146 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The class which handles manual Feeds import. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.4 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Calendar-feed | ||||
|  */ | ||||
| class Ai1ecImportConnectorPlugin extends Ai1ec_Connector_Plugin { | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      *   title: The title of the tab and the title of the configuration section | ||||
|      *   id: The id used in the generation of the tab | ||||
|      */ | ||||
|     protected $variables = array( | ||||
|         'id' => 'import', | ||||
|     ); | ||||
|  | ||||
|     public function get_tab_title() { | ||||
|         return Ai1ec_I18n::__( 'Import Feeds' ); | ||||
|     } | ||||
|  | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the translations array | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     private function get_translations() { | ||||
|         $categories = isset( $_POST['ai1ec_categories'] ) ? $_POST['ai1ec_categories'] : array(); | ||||
|         foreach ( $categories as &$cat ) { | ||||
|             $term = get_term( $cat, 'events_categories' ); | ||||
|             $cat = $term->name; | ||||
|         } | ||||
|         $translations = array( | ||||
|             '[feed_url]'   => $_POST['ai1ec_calendar_url'], | ||||
|             '[categories]' => implode( ', ', $categories ), | ||||
|             '[user_email]' => $_POST['ai1ec_submitter_email'], | ||||
|             '[site_title]' => get_bloginfo( 'name' ), | ||||
|             '[site_url]'   => ai1ec_site_url(), | ||||
|             '[feeds_url]'  => ai1ec_admin_url( | ||||
|                 AI1EC_FEED_SETTINGS_BASE_URL . '#ics' | ||||
|             ), | ||||
|         ); | ||||
|         return $translations; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::render_tab_content() | ||||
|      */ | ||||
|     public function render_tab_content() { | ||||
|         // Render the opening div | ||||
|         $this->render_opening_div_of_tab(); | ||||
|         // Render the body of the tab | ||||
|         $api           = $this->_registry->get( 'model.api.api-feeds' ); | ||||
|         $api_signed    = $api->is_signed(); | ||||
|         $settings      = $this->_registry->get( 'model.settings' ); | ||||
|         $factory       = $this->_registry->get( | ||||
|             'factory.html' | ||||
|         ); | ||||
|         $has_feature = $api->has_subscription_active( | ||||
|             Ai1ec_Api_Features::CODE_IMPORT_FEEDS | ||||
|             ); | ||||
|         $reached_limit = $api->subscription_has_reached_limit( | ||||
|             Ai1ec_Api_Features::CODE_IMPORT_FEEDS | ||||
|         ); | ||||
|         $select2_cats  = $factory->create_select2_multiselect( | ||||
|             array( | ||||
|                 'name' => 'ai1ec_feed_category[]', | ||||
|                 'id' => 'ai1ec_feed_category', | ||||
|                 'use_id' => true, | ||||
|                 'type' => 'category', | ||||
|                 'placeholder' => __( | ||||
|                     'Categories (optional)', | ||||
|                     AI1EC_PLUGIN_NAME | ||||
|                 ) | ||||
|             ), | ||||
|             get_terms( | ||||
|                 'events_categories', | ||||
|                 array( | ||||
|                     'hide_empty' => false | ||||
|                 ) | ||||
|             ) | ||||
|         ); | ||||
|         $select2_tags = $factory->create_select2_input( | ||||
|             array( 'id' => 'ai1ec_feed_tags') | ||||
|         ); | ||||
|         $loader    = $this->_registry->get( 'theme.loader' ); | ||||
|  | ||||
|         $args = array( | ||||
|             'event_categories' => $select2_cats, | ||||
|             'event_tags'       => $select2_tags, | ||||
|             'api_signed'       => $api->is_signed(), | ||||
|             'has_feature'      => $has_feature, | ||||
|             'reached_limit'    => $reached_limit, | ||||
|         ); | ||||
|  | ||||
|         $import_feed = $loader->get_file( | ||||
|             'plugins/ics/import_feed.php', | ||||
|             $args, | ||||
|             true | ||||
|         ); | ||||
|         $import_feed->render(); | ||||
|         $this->render_closing_div_of_tab(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::display_admin_notices() | ||||
|      */ | ||||
|     public function display_admin_notices() { | ||||
|         return; | ||||
|     } | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::run_uninstall_procedures() | ||||
|      */ | ||||
|     public function run_uninstall_procedures() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * add_ics_feed function | ||||
|      * | ||||
|      * Adds submitted ics feed to the database | ||||
|      * | ||||
|      * @return string JSON output | ||||
|      * | ||||
|      */ | ||||
|     public function add_ics_feed() { | ||||
|     } | ||||
|  | ||||
|     public function handle_feeds_page_post() { | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,139 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The class which handles suggested feeds tab. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.4 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Calendar-feed | ||||
|  */ | ||||
| class Ai1ecSuggestedConnectorPlugin extends Ai1ec_Connector_Plugin { | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      *   title: The title of the tab and the title of the configuration section | ||||
|      *   id: The id used in the generation of the tab | ||||
|      */ | ||||
|     protected $variables = array( | ||||
|         'id' => 'suggested' | ||||
|     ); | ||||
|  | ||||
|     public function get_tab_title() { | ||||
|         return Ai1ec_I18n::__( 'Discover Events' ); | ||||
|     } | ||||
|  | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::handle_feeds_page_post() | ||||
|      */ | ||||
|     public function handle_feeds_page_post() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::render_tab_content() | ||||
|      */ | ||||
|     public function render_tab_content() { | ||||
|         // Render the opening div | ||||
|         $this->render_opening_div_of_tab(); | ||||
|  | ||||
|         $loader        = $this->_registry->get( 'theme.loader' ); | ||||
|         $api           = $this->_registry->get( 'model.api.api-feeds' ); | ||||
|         $event_actions = $loader->get_file( | ||||
|             'plugins/suggested/event_actions.php', | ||||
|             array(), | ||||
|             true | ||||
|         ); | ||||
|         $display_feeds = $loader->get_file( | ||||
|             'plugins/suggested/display_feeds.php', | ||||
|             array( | ||||
|                 'event_actions'   => $event_actions, | ||||
|                 'api_signed'      => $api->is_signed() | ||||
|             ), | ||||
|             true | ||||
|         ); | ||||
|         $display_feeds->render(); | ||||
|  | ||||
|         // Render the body of the tab | ||||
|         $this->render_closing_div_of_tab(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::display_admin_notices() | ||||
|      */ | ||||
|     public function display_admin_notices() { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Events search | ||||
|      */ | ||||
|     public function search_events() { | ||||
|         $api           = $this->_registry->get( 'model.api.api-feeds' ); | ||||
|         $events        = $api->get_suggested_events(); | ||||
|         $imported      = $api->get_feed_subscriptions(); | ||||
|         $loader        = $this->_registry->get( 'theme.loader' ); | ||||
|         $event_actions = $loader->get_file( | ||||
|             'plugins/suggested/event_actions.php', | ||||
|             array(), | ||||
|             true | ||||
|         ); | ||||
|         if ( null === $events ) { | ||||
|             echo json_encode( | ||||
|                 array( | ||||
|                     'list'  => '', | ||||
|                     'total' => 0 | ||||
|                 ) | ||||
|             ); | ||||
|             exit( 0 ); | ||||
|         } | ||||
|         $page_links = paginate_links( array( | ||||
|             'base'      => add_query_arg( 'pagenum', '%#%' ), | ||||
|             'format'    => '', | ||||
|             'prev_text' => __( '«', AI1EC_PLUGIN_NAME ), | ||||
|             'next_text' => __( '»', AI1EC_PLUGIN_NAME ), | ||||
|             'total'     => $events->last_page, | ||||
|             'current'   => $events->current_page | ||||
|         ) ); | ||||
|         $avatar_url    = $loader->get_file( | ||||
|             'default-event-avatar.png', | ||||
|             array(), | ||||
|             false | ||||
|         )->get_url(); | ||||
|         $feeds_list    = $loader->get_file( | ||||
|             'plugins/suggested/feeds_list.php', | ||||
|             array( | ||||
|                 'suggested_feeds' => $events->data, | ||||
|                 'default_image'   => $avatar_url, | ||||
|                 'event_actions'   => $event_actions, | ||||
|                 'page_links'      => $page_links | ||||
|             ), | ||||
|             true | ||||
|         ); | ||||
|         $feeds_list = array( | ||||
|             'list'     => $feeds_list->get_content(), | ||||
|             'total'    => $events->total, | ||||
|             'imported' => $imported | ||||
|         ); | ||||
|         echo json_encode( $feeds_list ); | ||||
|         exit( 0 ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * (non-PHPdoc) | ||||
|      * | ||||
|      * @see Ai1ec_Connector_Plugin::run_uninstall_procedures() | ||||
|      */ | ||||
|     public function run_uninstall_procedures() { | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,66 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Calendar state container. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib.Calendar | ||||
|  */ | ||||
| class Ai1ec_Calendar_State extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Whether calendar is initializing router or not. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     private $_is_routing_initializing = false; | ||||
|  | ||||
|     /** | ||||
|      * Whether Html render strategy should append content in the_content | ||||
|      * filter hook. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     private $_append_content = true; | ||||
|  | ||||
|     /** | ||||
|      * Returns whether routing is during initialization phase or not. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function is_routing_initializing() { | ||||
|         return $this->_is_routing_initializing; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets state for routing initialization phase. | ||||
|      * | ||||
|      * @param bool $status State for initializing phase. | ||||
|      */ | ||||
|     public function set_routing_initialization( $status ) { | ||||
|         $this->_is_routing_initializing = $status; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether html render strategy should append content in the_content | ||||
|      * filter hook. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function append_content() { | ||||
|         return $this->_append_content; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets state for content appending in html renderer the_content hook. | ||||
|      * See Ai1ec_Render_Strategy_Html::append_content() | ||||
|      * | ||||
|      * @param bool $status Whether to append content or not. | ||||
|      */ | ||||
|     public function set_append_content( $status ) { | ||||
|         $this->_append_content = $status; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,148 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Calendar state container. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.3 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib.Calendar | ||||
|  */ | ||||
| class Ai1ec_Calendar_Updates extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Primary update endpoint. | ||||
|      * | ||||
|      * @const string | ||||
|      */ | ||||
|     const PRIMARY_END_POINT = 'https://update.time.ly/update'; | ||||
|  | ||||
|     /** | ||||
|      * Alternative update endpoint. | ||||
|      * | ||||
|      * @const string | ||||
|      */ | ||||
|     const SECONDARY_END_POINT = 'https://checkout.time.ly/update'; | ||||
|  | ||||
|     /** | ||||
|      * Check updates and return additional info. | ||||
|      * | ||||
|      * @param mixed $transient_data Current transient data. | ||||
|      * | ||||
|      * @return mixed Modified transient data. | ||||
|      */ | ||||
|     public function check_updates( $transient_data ) { | ||||
|         if ( empty( $transient_data ) ) { | ||||
|             return $transient_data; | ||||
|         } | ||||
|         $updates = $this->_download_updates(); | ||||
|         if ( empty( $updates ) ) { | ||||
|             return $transient_data; | ||||
|         } | ||||
|         $plugins = get_plugins(); | ||||
|         foreach ( $updates as $plugin => $update_data ) { | ||||
|             /** @var $plugin_data array */ | ||||
|             $plugin_data = isset( $plugins[$plugin] ) ? $plugins[$plugin] : null; | ||||
|             if ( | ||||
|                 empty( $plugin_data['Version'] ) || | ||||
|                 version_compare( $plugin_data['Version'], $update_data['new_version'], '>=' ) | ||||
|             ) { | ||||
|                 continue; | ||||
|             } | ||||
|             $transient_data->response[$plugin] = (object) $update_data; | ||||
|         } | ||||
|  | ||||
|         return $transient_data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get plugin data from retrieved and cached data. | ||||
|      * | ||||
|      * @param array      $data   Current data. | ||||
|      * @param string     $action Action name. | ||||
|      * @param array|null $args   Query arguments. | ||||
|      * | ||||
|      * @return mixed Plugin data. | ||||
|      */ | ||||
|     public function plugins_api_filter( $data, $action = '', $args = null ) { | ||||
|         /* | ||||
|         if ( | ||||
|             'plugin_information' !== $action || | ||||
|             empty( $args->slug ) || | ||||
|             'all-in-one-event-calendar' !== substr( $args->slug, 0, 25 ) | ||||
|         ) { | ||||
|             return $data; | ||||
|         } | ||||
|         $update_data       = get_site_transient( 'update_plugins' ); | ||||
|         $plugin_identifier = $args->slug . '/' . $args->slug . '.php'; | ||||
|         if ( empty( $update_data->response[$plugin_identifier] ) ) { | ||||
|             return $data; | ||||
|         } | ||||
|  | ||||
|         return $update_data->response[$plugin_identifier]; | ||||
|         */ | ||||
|         $updates = $this->_download_updates(); | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Clear updates related transients. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      */ | ||||
|     public function clear_transients() { | ||||
|         delete_site_transient( 'ai1ec_update_plugins' ); | ||||
|         delete_site_transient( 'update_plugins' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Download update info. Check local transient for cached data. | ||||
|      * | ||||
|      * @return array|mixed|null|object Update data. | ||||
|      */ | ||||
|     protected function _download_updates() { | ||||
|         $cached_updates = get_site_transient( 'ai1ec_update_plugins' ); | ||||
|         if ( $cached_updates ) { | ||||
|             return $cached_updates; | ||||
|         } | ||||
|         // try first endpoint | ||||
|         $response = $this->_get_data_from_endpoint( self::PRIMARY_END_POINT ); | ||||
|         if ( is_wp_error( $response ) ) { | ||||
|             $response = $this->_get_data_from_endpoint( self::SECONDARY_END_POINT ); | ||||
|         } | ||||
|         if ( is_wp_error( $response ) ) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         $data = json_decode( wp_remote_retrieve_body( $response ), true ); | ||||
|         set_site_transient( 'ai1ec_update_plugins', $data, 30 * MINUTE_IN_SECONDS ); | ||||
|  | ||||
|         return $data; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get update data from given endpoint. | ||||
|      * | ||||
|      * @param string $endpoint Endpoint URI. | ||||
|      * | ||||
|      * @return array|WP_Error Request result. | ||||
|      */ | ||||
|     protected function _get_data_from_endpoint( $endpoint ) { | ||||
|  | ||||
|         // Use ticketing token to check for subscriptions | ||||
|         $token = $this->_registry->get( 'model.api.api-registration' )->get_timely_token(); | ||||
|         if ( null === $token ) { | ||||
|             $token = ''; | ||||
|         } | ||||
|  | ||||
|         $request = array( | ||||
|             'method'    => 'GET', | ||||
|             'timeout'   => 15, | ||||
|             'sslverify' => false | ||||
|         ); | ||||
|  | ||||
|         return wp_remote_request( $endpoint . '/' . $token, $request ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,102 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Ai1ec_Captcha_Provider interface. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Captcha | ||||
|  */ | ||||
| abstract class Ai1ec_Captcha_Provider extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Settings object. | ||||
|      * | ||||
|      * @var Ai1ec_Settings | ||||
|      */ | ||||
|     protected $_settings = null; | ||||
|  | ||||
|     /** | ||||
|      * Theme loader object. | ||||
|      * | ||||
|      * @var Ai1ec_Theme_Loader | ||||
|      */ | ||||
|     protected $_theme_loader = null; | ||||
|  | ||||
|     /** | ||||
|      * Whether provider is configured or not. | ||||
|      * | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $_is_configured = null; | ||||
|  | ||||
|     /** | ||||
|      * Constructor. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      * | ||||
|      * @return Ai1ec_Captcha_Provider | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_settings     = $registry->get( 'model.settings' ); | ||||
|         $this->_theme_loader = $registry->get( 'theme.loader' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns settings array. | ||||
|      * | ||||
|      * @param bool $enable_rendering Whether setting HTML will be rendered or not. | ||||
|      * | ||||
|      * @return array Array of settings. | ||||
|      */ | ||||
|     abstract public function get_settings( $enable_rendering = true ); | ||||
|  | ||||
|     /** | ||||
|      * Returns captcha challenge. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     abstract public function get_challenge(); | ||||
|  | ||||
|     /** | ||||
|      * Validates challenge. | ||||
|      * | ||||
|      * @param array Challenge response data. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     abstract public function validate_challenge( array $data ); | ||||
|  | ||||
|     /** | ||||
|      * Returns provider name. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     abstract public function get_name(); | ||||
|  | ||||
|     /** | ||||
|      * Returns whether provider is properly configured or not. | ||||
|      * | ||||
|      * @return bool | ||||
|      */ | ||||
|     public function is_configured() { | ||||
|         if ( null !== $this->_is_configured ) { | ||||
|             return $this->_is_configured; | ||||
|         } | ||||
|         $this->_is_configured = true; | ||||
|         foreach ( $this->get_settings() as $key => $setting ) { | ||||
|             $value = $this->_settings->get( $key ); | ||||
|             if ( empty( $value ) ) { | ||||
|                 $this->_is_configured = false; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return $this->_is_configured; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,132 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Nocaptcha provider. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC. | ||||
|  */ | ||||
| class Ai1ec_Captcha_Nocaptcha_Provider extends Ai1ec_Captcha_Provider { | ||||
|  | ||||
|     /** | ||||
|      * Returns settings array. | ||||
|      * | ||||
|      * @param bool $enable_rendering Whether setting HTML will be rendered or not. | ||||
|      * | ||||
|      * @return array Array of settings. | ||||
|      */ | ||||
|     public function get_settings( $enable_rendering = true ) { | ||||
|         return array( | ||||
|             'google_nocaptcha_public_key'  => array( | ||||
|                 'type'     => 'string', | ||||
|                 'version'  => AI1ECFS_PLUGIN_NAME, | ||||
|                 'renderer' => array( | ||||
|                     'class'     => 'input', | ||||
|                     'tab'       => 'extensions', | ||||
|                     'item'      => 'interactive', | ||||
|                     'type'      => 'normal', | ||||
|                     'label'     => __( | ||||
|                         'reCAPTCHA V2 public key:', | ||||
|                         AI1ECFS_PLUGIN_NAME | ||||
|                     ), | ||||
|                     'condition' => $enable_rendering, | ||||
|                 ), | ||||
|                 'value'    => '', | ||||
|             ), | ||||
|             'google_nocaptcha_private_key' => array( | ||||
|                 'type'     => 'string', | ||||
|                 'version'  => AI1ECFS_PLUGIN_NAME, | ||||
|                 'renderer' => array( | ||||
|                     'class'     => 'input', | ||||
|                     'tab'       => 'extensions', | ||||
|                     'item'      => 'interactive', | ||||
|                     'type'      => 'normal', | ||||
|                     'label'     => __( | ||||
|                         'reCAPTCHA V2 private key:', | ||||
|                         AI1ECFS_PLUGIN_NAME | ||||
|                     ), | ||||
|                     'condition' => $enable_rendering, | ||||
|                 ), | ||||
|                 'value'    => '', | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns captcha challenge. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get_challenge() { | ||||
|         $args = array( | ||||
|             'nocaptcha_key' => $this->_settings->get( | ||||
|                 'google_nocaptcha_public_key' | ||||
|             ), | ||||
|         ); | ||||
|  | ||||
|         return $this->_theme_loader->get_file( | ||||
|             'captcha/nocaptcha/challenge.twig', | ||||
|             $args, | ||||
|             false | ||||
|         )->get_content(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates challenge. | ||||
|      * | ||||
|      * @param array Challenge response data. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function validate_challenge( array $data ) { | ||||
|  | ||||
|         $response['message'] = Ai1ec_I18n::__( | ||||
|             'Please try verifying you are human again.' | ||||
|         ); | ||||
|         $response['success'] = false; | ||||
|  | ||||
|         if ( empty( $data['g-recaptcha-response'] ) ) { | ||||
|             $response['message'] = Ai1ec_I18n::_( | ||||
|                 'There was an error reading the human verification data. Please try again.' | ||||
|             ); | ||||
|             $response['success'] = false; | ||||
|         } | ||||
|         $url       = add_query_arg( | ||||
|             array( | ||||
|                 'secret'   => $this->_settings->get( | ||||
|                     'google_nocaptcha_private_key' | ||||
|                 ), | ||||
|                 'response' => $data['g-recaptcha-response'], | ||||
|             ), | ||||
|             'https://www.google.com/recaptcha/api/siteverify' | ||||
|         ); | ||||
|         $json_resp = wp_remote_get( $url ); | ||||
|         if ( is_wp_error( $json_resp ) ) { | ||||
|             return $response; | ||||
|         } | ||||
|         $resp = json_decode( $json_resp['body'], true ); | ||||
|         if ( | ||||
|             isset( $resp['success'] ) && | ||||
|             $resp['success'] | ||||
|         ) { | ||||
|             $response = array( | ||||
|                 'success' => true, | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $response; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns provider name. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function get_name() { | ||||
|         return 'Google reCAPTCHA V2'; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,126 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * ReCaptcha provider. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Captcha.Provider | ||||
|  */ | ||||
| class Ai1ec_Captcha_Recaptcha_Provider extends Ai1ec_Captcha_Provider { | ||||
|  | ||||
|     /** | ||||
|      * Returns settings array. | ||||
|      * | ||||
|      * @param bool $enable_rendering Whether setting HTML will be rendered or not. | ||||
|      * | ||||
|      * @return array Array of settings. | ||||
|      */ | ||||
|     public function get_settings( $enable_rendering = true ) { | ||||
|  | ||||
|         return array( | ||||
|             'google_recaptcha_public_key'  => array( | ||||
|                 'type'     => 'string', | ||||
|                 'version'  => AI1ECFS_PLUGIN_NAME, | ||||
|                 'renderer' => array( | ||||
|                     'class'     => 'input', | ||||
|                     'tab'       => 'extensions', | ||||
|                     'item'      => 'interactive', | ||||
|                     'type'      => 'normal', | ||||
|                     'label'     => __( | ||||
|                         'reCAPTCHA public key:', | ||||
|                         AI1ECFS_PLUGIN_NAME | ||||
|                     ), | ||||
|                     'condition' => $enable_rendering, | ||||
|                 ), | ||||
|                 'value'    => '', | ||||
|             ), | ||||
|             'google_recaptcha_private_key' => array( | ||||
|                 'type'     => 'string', | ||||
|                 'version'  => AI1ECFS_PLUGIN_NAME, | ||||
|                 'renderer' => array( | ||||
|                     'class'     => 'input', | ||||
|                     'tab'       => 'extensions', | ||||
|                     'item'      => 'interactive', | ||||
|                     'type'      => 'normal', | ||||
|                     'label'     => __( | ||||
|                         'reCAPTCHA private key:', | ||||
|                         AI1ECFS_PLUGIN_NAME | ||||
|                     ), | ||||
|                     'condition' => $enable_rendering, | ||||
|                 ), | ||||
|                 'value'    => '', | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns captcha challenge. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function get_challenge() { | ||||
|         $args = array( | ||||
|             'verification_words' => Ai1ec_I18n::__( 'Human verification' ), | ||||
|             'loading_recaptcha'  => Ai1ec_I18n::__( 'Loading reCAPTCHA...' ), | ||||
|             'recaptcha_key'      => $this->_settings->get( | ||||
|                 'google_recaptcha_public_key' | ||||
|             ), | ||||
|         ); | ||||
|  | ||||
|         return $this->_theme_loader->get_file( | ||||
|             'captcha/recaptcha/challenge.twig', | ||||
|             $args, | ||||
|             false | ||||
|         )->get_content(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates challenge. | ||||
|      * | ||||
|      * @param array Challenge response data. | ||||
|      * | ||||
|      * @return mixed | ||||
|      */ | ||||
|     public function validate_challenge( array $data ) { | ||||
|         $response = array( 'success' => true ); | ||||
|         if ( | ||||
|             empty( $data['recaptcha_challenge_field'] ) || | ||||
|             empty( $data['recaptcha_response_field'] ) | ||||
|         ) { | ||||
|             $response['message'] = Ai1ec_I18n::_( | ||||
|                 'There was an error reading the human verification data. Please try again.' | ||||
|             ); | ||||
|             $response['success'] = false; | ||||
|         } | ||||
|  | ||||
|         $remoteAddress = isset( $_SERVER['HTTP_REFERER'] ) ? $_SERVER['HTTP_REFERER'] : null; | ||||
|  | ||||
|         require_once( AI1EC_VENDOR_PATH . 'recaptcha/recaptchalib.php' ); | ||||
|         $resp = recaptcha_check_answer( | ||||
|             $this->_settings->get( 'google_recaptcha_private_key' ), | ||||
|             $remoteAddress, | ||||
|             $data['recaptcha_challenge_field'], | ||||
|             $data['recaptcha_response_field'] | ||||
|         ); | ||||
|  | ||||
|         if ( ! $resp->is_valid ) { | ||||
|             $response['message'] = Ai1ec_I18n::__( | ||||
|                 'Please try verifying you are human again.' | ||||
|             ); | ||||
|             $response['success'] = false; | ||||
|         } | ||||
|         return $response; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns provider name. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function get_name() { | ||||
|         return 'Google reCAPTCHA'; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Captcha providers handler class. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Captcha | ||||
|  */ | ||||
| class Ai1ec_Captcha_Providers extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * List of available captcha providers. | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $_providers = null; | ||||
|  | ||||
|     /** | ||||
|      * Returns list of available providers. | ||||
|      * | ||||
|      * @return array List of providers. | ||||
|      */ | ||||
|     public function get_providers() { | ||||
|         if ( null !== $this->_providers ) { | ||||
|             return $this->_providers; | ||||
|         } | ||||
|         $built_in      = array( | ||||
|             'Ai1ec_Captcha_Recaptcha_Provider', | ||||
|             'Ai1ec_Captcha_Nocaptcha_Provider', | ||||
|         ); | ||||
|         $all_providers = apply_filters( 'ai1ec_captcha_providers', $built_in ); | ||||
|         if ( empty( $all_providers ) ) { | ||||
|             return array(); | ||||
|         } | ||||
|         $providers = array(); | ||||
|         foreach ( $all_providers as $provider_class ) { | ||||
|             $provider = new $provider_class( $this->_registry ); | ||||
|             if ( ! $provider instanceof Ai1ec_Captcha_Provider ) { | ||||
|                 continue; | ||||
|             } | ||||
|             $providers[] = $provider; | ||||
|         } | ||||
|  | ||||
|         return $providers; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns providers settings. | ||||
|      * | ||||
|      * @return array Providers settings. | ||||
|      */ | ||||
|     public function get_providers_as_settings() { | ||||
|         $all_providers = $this->get_providers(); | ||||
|         $settings      = array(); | ||||
|         foreach ( $all_providers as $provider ) { | ||||
|             $settings[] = array( | ||||
|                 'text'     => $provider->get_name(), | ||||
|                 'value'    => get_class( $provider ), | ||||
|                 'settings' => $provider->get_settings(), | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $settings; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,96 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Helps Rendering clone html. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Clone | ||||
|  */ | ||||
| class Ai1ec_Clone_Renderer_Helper extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * add clone bluk action in the dropdown | ||||
|      * | ||||
|      * @wp_hook admin_footer-edit.php | ||||
|      */ | ||||
|     public function duplicate_custom_bulk_admin_footer() { | ||||
|         $aco = $this->_registry->get( 'acl.aco' ); | ||||
|         if ( true === $aco->are_we_editing_our_post() ) { | ||||
|             ?> | ||||
|                 <script type="text/javascript"> | ||||
|                     jQuery(document).ready(function() { | ||||
|  | ||||
|                         jQuery('<option>').val('clone').text('<?php _e( 'Clone', AI1EC_PLUGIN_NAME )?>').appendTo("select[name='action']"); | ||||
|                         jQuery('<option>').val('clone').text('<?php _e( 'Clone', AI1EC_PLUGIN_NAME )?>').appendTo("select[name='action2']"); | ||||
|  | ||||
|                     }); | ||||
|                 </script> | ||||
|             <?php | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add the link to action list for post_row_actions | ||||
|      * | ||||
|      * @wp_hook post_row_action | ||||
|      * | ||||
|      */ | ||||
|     function ai1ec_duplicate_post_make_duplicate_link_row( $actions, $post ) { | ||||
|         if ( $post->post_type == "ai1ec_event" ) { | ||||
|             $actions['clone'] = '<a href="'.$this->ai1ec_duplicate_post_get_clone_post_link( $post->ID, 'display', false).'" title="' | ||||
|             . esc_attr(__("Make new copy of event", AI1EC_PLUGIN_NAME)) | ||||
|             . '">' .  __( 'Clone', AI1EC_PLUGIN_NAME ) . '</a>'; | ||||
|             $actions['edit_as_new_draft'] = '<a href="' . $this->ai1ec_duplicate_post_get_clone_post_link( $post->ID ) . '" title="' | ||||
|             . esc_attr(__( 'Copy to a new draft', AI1EC_PLUGIN_NAME )) | ||||
|             . '">' .  __( 'Clone to Draft', AI1EC_PLUGIN_NAME ) . '</a>'; | ||||
|         } | ||||
|         return $actions; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve duplicate post link for post. | ||||
|      * | ||||
|      * | ||||
|      * @param int $id Optional. Post ID. | ||||
|      * @param string $context Optional, default to display. How to write the '&', defaults to '&'. | ||||
|      * @param string $draft Optional, default to true | ||||
|      * @return string | ||||
|      */ | ||||
|     function ai1ec_duplicate_post_get_clone_post_link( $id = 0, $context = 'display', $draft = true ) { | ||||
|  | ||||
|         if ( ! $post = get_post( $id ) ) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         if ( $draft ) { | ||||
|             $action_name = "ai1ec_duplicate_post_save_as_new_post_draft"; | ||||
|         } else { | ||||
|             $action_name = "ai1ec_duplicate_post_save_as_new_post"; | ||||
|         } | ||||
|  | ||||
|         if ( 'display' == $context ) { | ||||
|             $action = '?action=' . $action_name . '&post=' . $post->ID; | ||||
|         } else { | ||||
|             $action = '?action=' . $action_name . '&post=' . $post->ID; | ||||
|         } | ||||
|  | ||||
|         $post_type_object = get_post_type_object( $post->post_type ); | ||||
|         if ( ! $post_type_object ) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         return apply_filters( | ||||
|             'ai1ec_duplicate_post_get_clone_post_link', | ||||
|             wp_nonce_url( | ||||
|                 ai1ec_admin_url( 'admin.php' . $action ), | ||||
|                 'ai1ec_clone_' . $post->ID | ||||
|             ), | ||||
|             $post->ID, | ||||
|             $context | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,117 @@ | ||||
| <?php | ||||
| /** | ||||
|  * The abstract command class. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| abstract class Ai1ec_Command { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object | ||||
|      */ | ||||
|     protected $_registry; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Request_Parser | ||||
|      */ | ||||
|     protected $_request; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Http_Response_Render_Strategy | ||||
|      */ | ||||
|     protected $_render_strategy; | ||||
|  | ||||
|     /** | ||||
|      * Public constructor. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      * @param Ai1ec_Request_Parser $request | ||||
|      */ | ||||
|     public function __construct( | ||||
|             Ai1ec_Registry_Object $registry, | ||||
|             Ai1ec_Request_Parser $request | ||||
|     ) { | ||||
|         $this->_registry = $registry; | ||||
|         $this->_request  = $request; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets parameters from the request object. | ||||
|      * | ||||
|      * @return array|boolean | ||||
|      */ | ||||
|     public function get_parameters() { | ||||
|         $plugin = $controller = $action = null; | ||||
|         $plugin     = Ai1ec_Request_Parser::get_param( 'plugin', $plugin ); | ||||
|         $controller = Ai1ec_Request_Parser::get_param( 'controller', $controller ); | ||||
|         $action     = Ai1ec_Request_Parser::get_param( 'action', $action ); | ||||
|         if ( | ||||
|             is_scalar( $plugin ) && | ||||
|             (string)AI1EC_PLUGIN_NAME === (string)$plugin && | ||||
|             null !== $controller && | ||||
|             null !== $action | ||||
|         ) { | ||||
|             return array( | ||||
|                 'controller' => $controller, | ||||
|                 'action'     => $action | ||||
|             ); | ||||
|         } | ||||
|         return false; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Execute the command. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function execute() { | ||||
|         // Set the render strategy | ||||
|         $this->set_render_strategy( $this->_request ); | ||||
|         // get the data from the concrete implementation | ||||
|         $data = $this->do_execute(); | ||||
|         // render it. | ||||
|         $this->_render_strategy->render( $data ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Defines whether to stop execution of command loop or not. | ||||
|      * | ||||
|      * @return bool True or false. | ||||
|      */ | ||||
|     public function stop_execution() { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The abstract method concrete command must implement. | ||||
|      * | ||||
|      * Retrieve whats needed and returns it | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     abstract public function do_execute(); | ||||
|  | ||||
|     /** | ||||
|      * Returns whether this is the command to be executed. | ||||
|      * | ||||
|      * I handle the logi of execution at this levele, which is not usual for | ||||
|      * The front controller pattern, because othe extensions need to inject | ||||
|      * logic into the resolver ( oAuth or ics export for instance ) | ||||
|      * and this seems to me to be the most logical way to do this. | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     abstract public function is_this_to_execute(); | ||||
|  | ||||
|     /** | ||||
|      * Sets the render strategy. | ||||
|      * | ||||
|      * @param Ai1ec_Request_Parser $request | ||||
|      */ | ||||
|     abstract public function set_render_strategy( Ai1ec_Request_Parser $request ); | ||||
| } | ||||
| @@ -0,0 +1,38 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that sends sign up data to API | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.4 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Api_Ticketing_Signup extends Ai1ec_Command_Save_Abstract { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|     */ | ||||
|     public function do_execute() { | ||||
|         $api      = $this->_registry->get( 'model.api.api-registration' ); | ||||
|         if ( true === isset($_POST['ai1ec_signout']) && '1' === $_POST['ai1ec_signout'] ) { | ||||
|             $api->signout(); | ||||
|         } else { | ||||
|             if ( '1' === $_POST['ai1ec_signing'] ) { | ||||
|                 $api->signup(); | ||||
|             } else { | ||||
|                 $api->signin(); | ||||
|             } | ||||
|         } | ||||
|         return array( | ||||
|             'url'        => ai1ec_admin_url( | ||||
|                 'edit.php?post_type=ai1ec_event&page=all-in-one-event-calendar-settings' | ||||
|             ), | ||||
|             'query_args' => array( | ||||
|                 'message' => '' | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,73 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that change active theme. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Change_Theme extends Ai1ec_Command { | ||||
|  | ||||
|     /** | ||||
|      * Executes the command to change the active theme. | ||||
|      * | ||||
|      * NOTE: {@see self::is_this_to_execute} must return true for this command | ||||
|      * to execute; we can trust that input has been checked for injections. | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         // Update the active theme in the options table. | ||||
|         $stylesheet = preg_replace( | ||||
|             '|[^a-z_\-]+|i', | ||||
|             '', | ||||
|             $_GET['ai1ec_stylesheet'] | ||||
|         ); | ||||
|         $this->_registry->get( 'theme.loader' )->switch_theme( array( | ||||
|             'theme_root' => realpath( $_GET['ai1ec_theme_root'] ), | ||||
|             'theme_dir'  => realpath( $_GET['ai1ec_theme_dir'] ), | ||||
|             'theme_url'  => $_GET['ai1ec_theme_url'], | ||||
|             'stylesheet' => $stylesheet, | ||||
|             'legacy'     => false | ||||
|         ) ); | ||||
|  | ||||
|         // Return user to themes list page with success message. | ||||
|         return array( | ||||
|             'url'        => ai1ec_admin_url( | ||||
|                 'edit.php?post_type=ai1ec_event&page=all-in-one-event-calendar-themes' | ||||
|             ), | ||||
|             'query_args' => array( | ||||
|                 'activated' => 1 | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command_Save_Abstract::set_render_strategy() | ||||
|      */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         $this->_render_strategy = $this->_registry->get( | ||||
|             'http.response.render.strategy.redirect' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|     */ | ||||
|     public function is_this_to_execute() { | ||||
|         if ( | ||||
|             isset( $_GET['ai1ec_action'] ) && | ||||
|             $_GET['ai1ec_action'] === 'activate_theme' && | ||||
|             current_user_can( 'switch_ai1ec_themes' ) && | ||||
|             is_dir( $_GET['ai1ec_theme_dir'] ) && | ||||
|             is_dir( $_GET['ai1ec_theme_root'] ) | ||||
|         ) { | ||||
|             check_admin_referer( | ||||
|                 'switch-ai1ec_theme_' . $_GET['ai1ec_stylesheet'] | ||||
|             ); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that compiles CSS. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.3 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Check_Updates extends Ai1ec_Command { | ||||
|  | ||||
|     /* | ||||
|      * (non-PHPdoc) @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         return isset( $_GET['ai1ec_force_updates'] ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|     */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         $this->_render_strategy = $this->_registry->get( | ||||
|             'http.response.render.strategy.redirect' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|     */ | ||||
|     public function do_execute() { | ||||
|         $this->_registry->get( 'calendar.updates' )->clear_transients(); | ||||
|  | ||||
|         return array ( | ||||
|             'url'        => ai1ec_admin_url( 'plugins.php' ), | ||||
|             'query_args' => array () | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,384 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that clone events. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Clone extends Ai1ec_Command { | ||||
|  | ||||
|     /** | ||||
|      * @var array The posts that must be cloned | ||||
|      */ | ||||
|     protected $_posts = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var bool Whether to redirect or not | ||||
|      */ | ||||
|     protected $_redirect = false; | ||||
|  | ||||
|     /** | ||||
|      * The abstract method concrete command must implement. | ||||
|      * | ||||
|      * Retrieve whats needed and returns it | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         $id = 0; | ||||
|         foreach ( $this->_posts as $post ) { | ||||
|             $id = $this->ai1ec_duplicate_post_create_duplicate( | ||||
|                 $post['post'], | ||||
|                 $post['status'] | ||||
|             ); | ||||
|         } | ||||
|         if ( true === $this->_redirect ) { | ||||
|             if ( '' === $post['status'] ) { | ||||
|                 return array( | ||||
|                     'url'        => ai1ec_admin_url( | ||||
|                         'edit.php?post_type=' . AI1EC_POST_TYPE | ||||
|                     ), | ||||
|                     'query_args' => array() | ||||
|                 ); | ||||
|             } else { | ||||
|                 return array( | ||||
|                     'url'        => ai1ec_admin_url( | ||||
|                         'post.php?action=edit&post=' . $id | ||||
|                     ), | ||||
|                     'query_args' => array() | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         // no redirect, just go on with the page | ||||
|         return array(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether this is the command to be executed. | ||||
|      * | ||||
|      * I handle the logi of execution at this levele, which is not usual for | ||||
|      * The front controller pattern, because othe extensions need to inject | ||||
|      * logic into the resolver ( oAuth or ics export for instance ) | ||||
|      * and this seems to me to be the most logical way to do this. | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         $current_action = $this->_registry->get( | ||||
|             'http.request' | ||||
|         )->get_current_action(); | ||||
|  | ||||
|         if ( | ||||
|             current_user_can( 'edit_ai1ec_events' ) && | ||||
|             'clone' === $current_action && | ||||
|             ! empty( $_REQUEST['post'] ) && | ||||
|             ! empty( $_REQUEST['_wpnonce'] ) && | ||||
|             wp_verify_nonce( $_REQUEST['_wpnonce'], 'bulk-posts' ) | ||||
|         ) { | ||||
|             foreach ( $_REQUEST['post'] as $post_id ) { | ||||
|                 $this->_posts[] = array( | ||||
|                     'status' => '', | ||||
|                     'post'   => get_post( $post_id ) | ||||
|                 ); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         // other actions need the nonce to be verified | ||||
|  | ||||
|         // duplicate single post | ||||
|         if ( | ||||
|             $current_action === 'ai1ec_duplicate_post_save_as_new_post' && | ||||
|             ! empty( $_REQUEST['post'] ) | ||||
|         ) { | ||||
|             check_admin_referer( 'ai1ec_clone_'. $_REQUEST['post'] ); | ||||
|  | ||||
|             $this->_posts[] = array( | ||||
|                 'status' => '', | ||||
|                 'post'   => get_post( $_REQUEST['post'] ) | ||||
|             ); | ||||
|             $this->_redirect = true; | ||||
|             return true; | ||||
|         } | ||||
|         // duplicate single post as draft | ||||
|         if ( | ||||
|             $current_action === 'ai1ec_duplicate_post_save_as_new_post_draft' && | ||||
|             ! empty( $_REQUEST['post'] ) | ||||
|         ) { | ||||
|             check_admin_referer( 'ai1ec_clone_'. $_REQUEST['post'] ); | ||||
|             $this->_posts[] = array( | ||||
|                 'status' => 'draft', | ||||
|                 'post'   => get_post( $_REQUEST['post'] ) | ||||
|             ); | ||||
|             $this->_redirect = true; | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the render strategy. | ||||
|      * | ||||
|      * @param Ai1ec_Request_Parser $request | ||||
|      */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         if ( true === $this->_redirect ) { | ||||
|             $this->_render_strategy = $this->_registry | ||||
|                 ->get( 'http.response.render.strategy.redirect' ); | ||||
|         } else { | ||||
|             $this->_render_strategy = $this->_registry | ||||
|                 ->get( 'http.response.render.strategy.void' ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create a duplicate from a posts' instance | ||||
|      */ | ||||
|     public function ai1ec_duplicate_post_create_duplicate( $post, $status = '' ) { | ||||
|         $post            = get_post( $post ); | ||||
|         $new_post_author = $this->_ai1ec_duplicate_post_get_current_user(); | ||||
|         $new_post_status = $status; | ||||
|         if ( empty( $new_post_status ) ) { | ||||
|             $new_post_status = $post->post_status; | ||||
|         } | ||||
|         $new_post_status = $this->_get_new_post_status( $new_post_status ); | ||||
|  | ||||
|         $new_post = array( | ||||
|             'menu_order'     => $post->menu_order, | ||||
|             'comment_status' => $post->comment_status, | ||||
|             'ping_status'    => $post->ping_status, | ||||
|             'pinged'         => $post->pinged, | ||||
|             'post_author'    => $new_post_author->ID, | ||||
|             'post_content'   => $post->post_content, | ||||
|             'post_date'      => $post->post_date, | ||||
|             'post_date_gmt'  => get_gmt_from_date( $post->post_date  ), | ||||
|             'post_excerpt'   => $post->post_excerpt, | ||||
|             'post_parent'    => $post->post_parent, | ||||
|             'post_password'  => $post->post_password, | ||||
|             'post_status'    => $new_post_status, | ||||
|             'post_title'     => $post->post_title, | ||||
|             'post_type'      => $post->post_type, | ||||
|             'to_ping'        => $post->to_ping, | ||||
|         ); | ||||
|  | ||||
|         $new_post_id    = wp_insert_post( $new_post ); | ||||
|         $edit_event_url = esc_attr( | ||||
|             ai1ec_admin_url( "post.php?post={$new_post_id}&action=edit" ) | ||||
|         ); | ||||
|         $message = sprintf( | ||||
|             __( '<p>The event <strong>%s</strong> was cloned succesfully. <a href="%s">Edit cloned event</a></p>', AI1EC_PLUGIN_NAME ), | ||||
|             $post->post_title, | ||||
|             $edit_event_url | ||||
|         ); | ||||
|         $notification   = $this->_registry->get( 'notification.admin' ); | ||||
|         $notification->store( $message ); | ||||
|         $this->_ai1ec_duplicate_post_copy_post_taxonomies( $new_post_id, $post ); | ||||
|         $this->_ai1ec_duplicate_post_copy_attachments(     $new_post_id, $post ); | ||||
|         $this->_ai1ec_duplicate_post_copy_post_meta_info(  $new_post_id, $post ); | ||||
|  | ||||
|         $api = $this->_registry->get( 'model.api.api-ticketing' ); | ||||
|         $api->clear_event_metadata( $new_post_id ); | ||||
|  | ||||
|         if ( $this->_registry->get( 'acl.aco' )->is_our_post_type( $post ) ) { | ||||
|             try { | ||||
|                 $old_event = $this->_registry->get( 'model.event', $post->ID ); | ||||
|                 $old_event->set( 'post_id',         $new_post_id ); | ||||
|                 $old_event->set( 'post',            null ); | ||||
|                 $old_event->set( 'ical_feed_url',   null ); | ||||
|                 $old_event->set( 'ical_source_url', null ); | ||||
|                 $old_event->set( 'ical_organizer',  null ); | ||||
|                 $old_event->set( 'ical_contact',    null ); | ||||
|                 $old_event->set( 'ical_uid',        null ); | ||||
|                 $old_event->save(); | ||||
|             } catch ( Ai1ec_Event_Not_Found_Exception $exception ) { | ||||
|                 /* ignore */ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $meta_post = $this->_registry->get( 'model.meta-post' ); | ||||
|         $meta_post->delete( $new_post_id, '_dp_original' ); | ||||
|         $meta_post->add(    $new_post_id, '_dp_original', $post->ID ); | ||||
|  | ||||
|         // If the copy gets immediately published, we have to set a proper slug. | ||||
|         if ( | ||||
|             $new_post_status == 'publish' || | ||||
|             $new_post_status == 'future' | ||||
|         ) { | ||||
|             $post_name = wp_unique_post_slug( | ||||
|                 $post->post_name, | ||||
|                 $new_post_id, | ||||
|                 $new_post_status, | ||||
|                 $post->post_type, | ||||
|                 $post->post_parent | ||||
|             ); | ||||
|  | ||||
|             $new_post = array(); | ||||
|             $new_post['ID']        = $new_post_id; | ||||
|             $new_post['post_name'] = $post_name; | ||||
|  | ||||
|             // Update the post into the database | ||||
|             wp_update_post( $new_post ); | ||||
|         } | ||||
|  | ||||
|         return $new_post_id; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Copy the meta information of a post to another post | ||||
|      */ | ||||
|     protected function _ai1ec_duplicate_post_copy_post_meta_info( $new_id, $post ) { | ||||
|         $post_meta_keys = get_post_custom_keys( $post->ID ); | ||||
|         if ( empty( $post_meta_keys ) ) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         foreach ( $post_meta_keys as $meta_key ) { | ||||
|             $meta_values = get_post_custom_values( $meta_key, $post->ID ); | ||||
|             foreach ( $meta_values as $meta_value ) { | ||||
|                 $meta_value = maybe_unserialize( $meta_value ); | ||||
|                 $meta_value = apply_filters( | ||||
|                     'ai1ec_duplicate_post_meta_value', | ||||
|                     $meta_value, | ||||
|                     $meta_key, | ||||
|                     $post, | ||||
|                     $new_id | ||||
|                 ); | ||||
|                 if ( null !== $meta_value ) { | ||||
|                     add_post_meta( $new_id, $meta_key, $meta_value ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Copy the attachments | ||||
|      * It simply copies the table entries, actual file won't be duplicated | ||||
|      */ | ||||
|     protected function _ai1ec_duplicate_post_copy_attachments( $new_id, $post ) { | ||||
|         //if (get_option('ai1ec_duplicate_post_copyattachments') == 0) return; | ||||
|  | ||||
|         // get old attachments | ||||
|         $attachments = get_posts( | ||||
|             array( | ||||
|                 'post_type'   => 'attachment', | ||||
|                 'numberposts' => -1, | ||||
|                 'post_status' => null, | ||||
|                 'post_parent' => $post->ID, | ||||
|             ) | ||||
|         ); | ||||
|         // clone old attachments | ||||
|         foreach ( $attachments as $att ) { | ||||
|             $new_att_author = $this->_ai1ec_duplicate_post_get_current_user(); | ||||
|  | ||||
|             $new_att = array( | ||||
|                 'menu_order'     => $att->menu_order, | ||||
|                 'comment_status' => $att->comment_status, | ||||
|                 'guid'           => $att->guid, | ||||
|                 'ping_status'    => $att->ping_status, | ||||
|                 'pinged'         => $att->pinged, | ||||
|                 'post_author'    => $new_att_author->ID, | ||||
|                 'post_content'   => $att->post_content, | ||||
|                 'post_date'      => $att->post_date, | ||||
|                 'post_date_gmt'  => get_gmt_from_date( $att->post_date ), | ||||
|                 'post_excerpt'   => $att->post_excerpt, | ||||
|                 'post_mime_type' => $att->post_mime_type, | ||||
|                 'post_parent'    => $new_id, | ||||
|                 'post_password'  => $att->post_password, | ||||
|                 'post_status'    => $this->_get_new_post_status( | ||||
|                     $att->post_status | ||||
|                 ), | ||||
|                 'post_title'     => $att->post_title, | ||||
|                 'post_type'      => $att->post_type, | ||||
|                 'to_ping'        => $att->to_ping, | ||||
|             ); | ||||
|  | ||||
|             $new_att_id = wp_insert_post( $new_att ); | ||||
|  | ||||
|             // get and apply a unique slug | ||||
|             $att_name = wp_unique_post_slug( | ||||
|                 $att->post_name, | ||||
|                 $new_att_id, | ||||
|                 $att->post_status, | ||||
|                 $att->post_type, | ||||
|                 $new_id | ||||
|             ); | ||||
|             $new_att = array(); | ||||
|             $new_att['ID']        = $new_att_id; | ||||
|             $new_att['post_name'] = $att_name; | ||||
|  | ||||
|             wp_update_post( $new_att ); | ||||
|  | ||||
|  | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Copy the taxonomies of a post to another post | ||||
|      */ | ||||
|     protected function _ai1ec_duplicate_post_copy_post_taxonomies( $new_id, $post ) { | ||||
|         $db = $this->_registry->get( 'dbi.dbi' ); | ||||
|         if ( $db->are_terms_set() ) { | ||||
|             // Clear default category (added by wp_insert_post) | ||||
|             wp_set_object_terms( $new_id, NULL, 'category' ); | ||||
|  | ||||
|             $post_taxonomies = get_object_taxonomies( $post->post_type ); | ||||
|  | ||||
|             $taxonomies_blacklist = array(); | ||||
|             $taxonomies = array_diff( $post_taxonomies, $taxonomies_blacklist ); | ||||
|             foreach ( $taxonomies as $taxonomy ) { | ||||
|                 $post_terms = wp_get_object_terms( | ||||
|                     $post->ID, | ||||
|                     $taxonomy, | ||||
|                     array( 'orderby' => 'term_order' ) | ||||
|                 ); | ||||
|                 $terms = array(); | ||||
|                 for ( $i = 0; $i < count( $post_terms ); $i++ ) { | ||||
|                     $terms[] = $post_terms[ $i ]->slug; | ||||
|                 } | ||||
|                 wp_set_object_terms( $new_id, $terms, $taxonomy ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the currently registered user | ||||
|      */ | ||||
|     protected function _ai1ec_duplicate_post_get_current_user() { | ||||
|         if ( function_exists( 'wp_get_current_user' ) ) { | ||||
|             return wp_get_current_user(); | ||||
|         } else { | ||||
|             $db = $this->_registry->get( 'dbi.dbi' ); | ||||
|             $query = $db->prepare( | ||||
|                 'SELECT * FROM ' . $wpdb->users . ' WHERE user_login = %s', | ||||
|                 $_COOKIE[ USER_COOKIE ] | ||||
|             ); | ||||
|             $current_user = $db->get_results( $query ); | ||||
|             return $current_user; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the status for `duplicate' post | ||||
|      * | ||||
|      * If user cannot publish post (event), and original post status is | ||||
|      * *publish*, then it will be duplicated with *pending* status. | ||||
|      * In other cases original status will remain. | ||||
|      * | ||||
|      * @param string $old_status Status of old post | ||||
|      * | ||||
|      * @return string Status for new post | ||||
|      */ | ||||
|     protected  function _get_new_post_status( $old_status ) { | ||||
|         if ( | ||||
|             'publish' === $old_status && | ||||
|             ! current_user_can( 'publish_ai1ec_events' ) | ||||
|         ) { | ||||
|             return 'pending'; | ||||
|         } | ||||
|         return $old_status; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,100 @@ | ||||
| <?php | ||||
| /** | ||||
|  * The concrete command that compiles CSS. | ||||
| * | ||||
| * @author     Time.ly Network Inc. | ||||
| * @since      2.1 | ||||
| * | ||||
| * @package    AI1EC | ||||
| * @subpackage AI1EC.Command | ||||
| */ | ||||
| class Ai1ec_Command_Compile_Core_Css extends Ai1ec_Command { | ||||
|  | ||||
|     /* | ||||
|      * (non-PHPdoc) @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         if ( isset( $_GET['ai1ec_compile_css'] ) && | ||||
|             AI1EC_DEBUG | ||||
|         ) { | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|     */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         $this->_render_strategy = $this->_registry->get( | ||||
|             'http.response.render.strategy.void' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|     */ | ||||
|     public function do_execute() { | ||||
|         $message = $this->_process_files(); | ||||
|         echo $message; | ||||
|         return Ai1ec_Http_Response_Helper::stop( 0 ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns calendar theme structure. | ||||
|      * | ||||
|      * @param string $stylesheet Calendar stylesheet. Expects one of | ||||
|      *                           ['vortex','plana','umbra','gamma']. | ||||
|      * @return array Calendar themes. | ||||
|      * | ||||
|      * @throws Ai1ec_Invalid_Argument_Exception | ||||
|      */ | ||||
|     protected function _get_theme( $stylesheet ) { | ||||
|         return $this->_registry->get( | ||||
|             'filesystem.misc' | ||||
|         )->build_theme_structure( $stylesheet ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns PHP code with hashmap array. | ||||
|      * | ||||
|      * @param $hashmap Array with compilation hashes. | ||||
|      * | ||||
|      * @return string PHP code. | ||||
|      */ | ||||
|     protected function _get_hashmap_array( $hashmap ) { | ||||
|         return '<?php return ' . var_export( $hashmap, true ) . ';'; | ||||
|     } | ||||
|  | ||||
|     protected function _process_files() { | ||||
|         $less   = $frontend = $this->_registry->get( 'less.lessphp' ); | ||||
|         $option = $this->_registry->get( 'model.option' ); | ||||
|         $theme  = $this->_get_theme( $_GET['theme'] ); | ||||
|  | ||||
|         if ( isset( $_GET['switch'] ) ) { | ||||
|             $option->delete( 'ai1ec_less_variables' ); | ||||
|             $option->set( 'ai1ec_current_theme', $theme ); | ||||
|             return 'Theme switched to "' . $theme['stylesheet'] . '".'; | ||||
|         } | ||||
|  | ||||
|         $css      = $less->parse_less_files( null, true ); | ||||
|         $hashmap  = $less->get_less_hashmap(); | ||||
|         $hashmap  = $this->_get_hashmap_array( $hashmap ); | ||||
|         $filename = $theme['theme_dir'] . DIRECTORY_SEPARATOR . | ||||
|                     'css' . DIRECTORY_SEPARATOR . 'ai1ec_parsed_css.css'; | ||||
|         $hashmap_file = $theme['theme_dir'] . DIRECTORY_SEPARATOR . | ||||
|                     'less.sha1.map.php'; | ||||
|  | ||||
|         $css_written     = file_put_contents( $filename, $css ); | ||||
|         $hashmap_written = file_put_contents( $hashmap_file, $hashmap ); | ||||
|         if ( | ||||
|             false === $css_written || | ||||
|             false === $hashmap_written | ||||
|         ) { | ||||
|             return 'There has been an error writing theme CSS'; | ||||
|         } | ||||
|  | ||||
|         return 'Theme CSS compiled succesfully and written in ' . | ||||
|                     $filename . ' and classmap stored in ' . $hashmap_file; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * (Re)compile themes for shipping. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.1 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Compile_Themes extends Ai1ec_Command { | ||||
|  | ||||
|     /* | ||||
|      * (non-PHPdoc) @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         return ( | ||||
|             AI1EC_DEBUG && | ||||
|             isset( $_GET['ai1ec_recompile_templates'] ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|     */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         $this->_render_strategy = $this->_registry->get( | ||||
|             'http.response.render.strategy.void' | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|     */ | ||||
|     public function do_execute() { | ||||
|         $this->_registry->get( 'theme.compiler' )->generate(); | ||||
|         return Ai1ec_Http_Response_Helper::stop( 0 ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,47 @@ | ||||
| <?php | ||||
| /** | ||||
|  * The concrete command that disabel gzip. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Disable_Gzip extends Ai1ec_Command { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         if ( isset( $_GET['ai1ec_disable_gzip_compression'] ) ) { | ||||
|             check_admin_referer( 'ai1ec_disable_gzip_compression' ); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         $this->_registry->get( 'model.settings' ) | ||||
|             ->set( 'disable_gzip_compression', true ); | ||||
|         return array( | ||||
|             'url'        => ai1ec_admin_url( 'edit.php' ), | ||||
|             'query_args' => array( | ||||
|                 'post_type' => 'ai1ec_event', | ||||
|                 'page'      => 'all-in-one-event-calendar-settings', | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|     */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         $this->_render_strategy = $this->_registry->get( | ||||
|             'http.response.render.strategy.redirect' | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,159 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that export events. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Export_Events extends Ai1ec_Command { | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * @var string The name of the old exporter controller. | ||||
|      */ | ||||
|     const EXPORT_CONTROLLER = 'ai1ec_exporter_controller'; | ||||
|  | ||||
|     /** | ||||
|      * @var string The name of the old export method. | ||||
|      */ | ||||
|     const EXPORT_METHOD = 'export_events'; | ||||
|  | ||||
|     /** | ||||
|      * @var array Request parameters | ||||
|      */ | ||||
|     protected $_params; | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         $params = $this->get_parameters(); | ||||
|         if ( false === $params ) { | ||||
|             return false; | ||||
|         } | ||||
|         if ( $params['action'] === self::EXPORT_METHOD && | ||||
|              $params['controller'] === self::EXPORT_CONTROLLER ) { | ||||
|             $params['tag_ids'] = Ai1ec_Request_Parser::get_param( | ||||
|                 'ai1ec_tag_ids', | ||||
|                 false | ||||
|             ); | ||||
|             $params['cat_ids'] = Ai1ec_Request_Parser::get_param( | ||||
|                 'ai1ec_cat_ids', | ||||
|                 false | ||||
|             ); | ||||
|             $params['post_ids'] = Ai1ec_Request_Parser::get_param( | ||||
|                 'ai1ec_post_ids', | ||||
|                 false | ||||
|             ); | ||||
|             $params['lang'] = Ai1ec_Request_Parser::get_param( | ||||
|                 'lang', | ||||
|                 false | ||||
|             ); | ||||
|             $params['no_html'] = (bool)Ai1ec_Request_Parser::get_param( | ||||
|                 'no_html', | ||||
|                 false | ||||
|             ); | ||||
|             $params['xml'] = (bool)Ai1ec_Request_Parser::get_param( | ||||
|                 'xml', | ||||
|                 false | ||||
|             ); | ||||
|             $this->_params = $params; | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|      */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         if ( isset( $_GET['xml']) ) { | ||||
|             $this->_render_strategy = $this->_registry->get( | ||||
|                 'http.response.render.strategy.xcal' | ||||
|             ); | ||||
|         } else { | ||||
|             $this->_render_strategy = $this->_registry->get( | ||||
|                 'http.response.render.strategy.ical' | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         $ai1ec_cat_ids  = $this->_params['cat_ids']; | ||||
|         $ai1ec_tag_ids  = $this->_params['tag_ids']; | ||||
|         $ai1ec_post_ids = $this->_params['post_ids']; | ||||
|         if ( ! empty( $this->_params['lang'] ) ) { | ||||
|             $loc_helper = $this->_registry->get( 'p28n.wpml' ); | ||||
|             $loc_helper->set_language( $this->_params['lang'] ); | ||||
|         } | ||||
|         $args = array( 'do_not_export_as_calendar' => false ); | ||||
|         $filter = array(); | ||||
|         if ( $ai1ec_cat_ids ) { | ||||
|             $filter['cat_ids']  = Ai1ec_Primitive_Int::convert_to_int_list( | ||||
|                 ',', | ||||
|                 $ai1ec_cat_ids | ||||
|             ); | ||||
|         } | ||||
|         if ( $ai1ec_tag_ids ) { | ||||
|             $filter['tag_ids']  = Ai1ec_Primitive_Int::convert_to_int_list( | ||||
|                 ',', | ||||
|                 $ai1ec_tag_ids | ||||
|             ); | ||||
|         } | ||||
|         if ( $ai1ec_post_ids ) { | ||||
|             $args['do_not_export_as_calendar'] = true; | ||||
|             $filter['post_ids'] = Ai1ec_Primitive_Int::convert_to_int_list( | ||||
|                 ',', | ||||
|                 $ai1ec_post_ids | ||||
|             ); | ||||
|         } | ||||
|         $filter = apply_filters( 'ai1ec_export_filter', $filter ); | ||||
|         $start  = $this->_registry->get( 'date.time', '-3 years' ); | ||||
|         $end    = $this->_registry->get( 'date.time', '+3 years' ); | ||||
|         $search = $this->_registry->get( 'model.search' ); | ||||
|         $params = array( | ||||
|             'no_html' => $this->_params['no_html'], | ||||
|             'xml'     => $this->_params['xml'], | ||||
|         ); | ||||
|         $export_controller = $this->_registry->get( | ||||
|             'controller.import-export', | ||||
|             array( 'ics' ), | ||||
|             $params | ||||
|         ); | ||||
|  | ||||
|         $args['events'] = $this->unique_events( | ||||
|             $search->get_events_between( $start, $end, $filter ) | ||||
|         ); | ||||
|         $ics = $export_controller->export_events( 'ics', $args ); | ||||
|         return array( 'data' => $ics ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return unique events list. | ||||
|      * | ||||
|      * @param array $events List of Ai1ec_Event objects. | ||||
|      * | ||||
|      * @return array Unique Ai1ec_Events from input. | ||||
|      */ | ||||
|     public function unique_events( array $events ) { | ||||
|         $ids    = array(); | ||||
|         $output = array(); | ||||
|         foreach ( $events as $event ) { | ||||
|             $id = (int)$event->get( 'post_id' ); | ||||
|             if ( ! isset( $ids[$id] ) ) { | ||||
|                 $output[] = $event; | ||||
|                 $ids[$id] = true; | ||||
|             } | ||||
|         } | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,83 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that renders the calendar. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Render_Calendar extends Ai1ec_Command { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $_request_type; | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         $settings          = $this->_registry->get( 'model.settings' ); | ||||
|         $calendar_page_id  = $settings->get( 'calendar_page_id' ); | ||||
|         if ( empty( $calendar_page_id ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         $localization      = $this->_registry->get( 'p28n.wpml' ); | ||||
|         $aco               = $this->_registry->get( 'acl.aco' ); | ||||
|         $page_ids_to_match = array( $calendar_page_id ) + | ||||
|         $localization->get_translations_of_page( | ||||
|                 $calendar_page_id | ||||
|         ); | ||||
|         foreach ( $page_ids_to_match as $page_id ) { | ||||
|  | ||||
|             if ( is_page( $page_id ) ) { | ||||
|                 $this->_request->set_current_page( $page_id ); | ||||
|                 if ( ! post_password_required( $page_id ) ) { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|      */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         try { | ||||
|             $this->_request_type    = $request->get( 'request_type' ); | ||||
|             $this->_render_strategy = $this->_registry->get( | ||||
|                 'http.response.render.strategy.' . $this->_request_type | ||||
|             ); | ||||
|         } catch ( Ai1ec_Bootstrap_Exception $e ) { | ||||
|             $this->_request_type    = 'html'; | ||||
|             $this->_render_strategy = $this->_registry->get( | ||||
|                 'http.response.render.strategy.' . $this->_request_type | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         // get the calendar html | ||||
|         $calendar = $this->_registry->get( 'view.calendar.page' ); | ||||
|         $css      = $this->_registry->get( 'css.frontend' ) | ||||
|             ->add_link_to_html_for_frontend(); | ||||
|         $js       = $this->_registry->get( 'controller.javascript' ) | ||||
|             ->load_frontend_js( true ); | ||||
|         return array( | ||||
|             'data'     => $calendar->get_content( $this->_request ), | ||||
|             'callback' => Ai1ec_Request_Parser::get_param( | ||||
|                 'callback', | ||||
|                 null | ||||
|             ), | ||||
|             'caller'   => 'calendar', | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,75 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that renders the event. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Render_Event extends Ai1ec_Command_Render_Calendar { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|      */ | ||||
|     public function is_this_to_execute() { | ||||
|         global $post; | ||||
|         if ( | ||||
|             ! isset( $post ) || | ||||
|             ! is_object( $post ) || | ||||
|             (int)$post->ID <= 0 || | ||||
|             post_password_required( $post->ID ) | ||||
|         ) { | ||||
|             return false; | ||||
|         } | ||||
|         return $this->_registry->get( 'acl.aco' )->is_our_post_type(); | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         // If not on the single event page, return nothing. | ||||
|         if ( ! is_single() ) { | ||||
|             return array( | ||||
|                 'data'     => '', | ||||
|                 'is_event' => true, | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         // Else proceed with rendering valid event. Fetch all relevant details. | ||||
|         $instance      = -1; | ||||
|         if ( isset( $_REQUEST['instance_id'] ) ) { | ||||
|             $instance    = (int)$_REQUEST['instance_id']; | ||||
|         } | ||||
|         $event         = $this->_registry->get( | ||||
|             'model.event', | ||||
|             get_the_ID(), | ||||
|             $instance | ||||
|         ); | ||||
|         $event_page    = $this->_registry->get( 'view.event.single' ); | ||||
|         $footer_html   = $event_page->get_footer( $event ); | ||||
|         $css = $this->_registry->get( 'css.frontend' ) | ||||
|             ->add_link_to_html_for_frontend(); | ||||
|         $js  = $this->_registry->get( 'controller.javascript' ) | ||||
|             ->load_frontend_js( false ); | ||||
|  | ||||
|         // If requesting event by JSON (remotely), return fully rendered event. | ||||
|         if ( 'html' !== $this->_request_type ) { | ||||
|             return array( | ||||
|                 'data'     => array( | ||||
|                     'html'   => $event_page->get_full_article( $event, $footer_html ) | ||||
|                 ), | ||||
|                 'callback' => Ai1ec_Request_Parser::get_param( 'callback', null ), | ||||
|             ); | ||||
|         } | ||||
|         // Else return event details as components. | ||||
|         return array( | ||||
|             'data'     => $event_page->get_content( $event ), | ||||
|             'is_event' => true, | ||||
|             'footer'   => $footer_html, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,154 @@ | ||||
| <?php | ||||
| /** | ||||
|  * The command resolver class that handles command. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Resolver { | ||||
|  | ||||
|     /** | ||||
|      * @var array The available commands. | ||||
|      */ | ||||
|     private $_commands = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object The Object registry. | ||||
|      */ | ||||
|     private $_registry; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Request_Parser The Request parser. | ||||
|      */ | ||||
|     private $_request; | ||||
|  | ||||
|     /** | ||||
|      * Public constructor | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      * @param Ai1ec_Request_Parser $request | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         Ai1ec_Request_Parser $request | ||||
|     ) { | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.compile-themes', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.disable-gzip', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.export-events', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.render-event', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.render-calendar', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.change-theme', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.save-settings', | ||||
|                 $request, | ||||
|                 array( | ||||
|                     'action' => 'ai1ec_save_settings', | ||||
|                     'nonce_action' => Ai1ec_View_Admin_Settings::NONCE_ACTION, | ||||
|                     'nonce_name' => Ai1ec_View_Admin_Settings::NONCE_NAME, | ||||
|                 ) | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.save-theme-options', | ||||
|                 $request, | ||||
|                 array( | ||||
|                     'action' => 'ai1ec_save_theme_options', | ||||
|                     'nonce_action' => Ai1ec_View_Theme_Options::NONCE_ACTION, | ||||
|                     'nonce_name' => Ai1ec_View_Theme_Options::NONCE_NAME, | ||||
|                 ) | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.api-ticketing-signup', | ||||
|                 $request, | ||||
|                 array( | ||||
|                     'action'       => 'ai1ec_api_ticketing_signup', | ||||
|                     'nonce_action' => Ai1ec_View_Tickets::NONCE_ACTION, | ||||
|                     'nonce_name'   => Ai1ec_View_Tickets::NONCE_NAME, | ||||
|                 ) | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.clone', $request | ||||
|             ) | ||||
|         ); | ||||
|         $this->add_command( | ||||
|             $registry->get( | ||||
|                 'command.compile-core-css', $request | ||||
|             ) | ||||
|         ); | ||||
|         if ( | ||||
|             is_admin() && | ||||
|             current_user_can( 'activate_plugins' ) | ||||
|         ) { | ||||
|             $this->add_command( | ||||
|                 $registry->get( | ||||
|                     'command.check-updates', $request | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|         $request->parse(); | ||||
|         $this->_registry = $registry; | ||||
|         $this->_request  = $request; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add a command. | ||||
|      * | ||||
|      * @param Ai1ec_Command $command | ||||
|      * | ||||
|      * @return Ai1ec_Comment_Resolver Self for calls chaining | ||||
|      */ | ||||
|     public function add_command( Ai1ec_Command $command ) { | ||||
|         $this->_commands[] = $command; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the command to execute or false. | ||||
|      * | ||||
|      * @return Ai1ec_Command|null | ||||
|      */ | ||||
|     public function get_commands() { | ||||
|         $commands = array(); | ||||
|         foreach ( $this->_commands as $command ) { | ||||
|             if ( $command->is_this_to_execute() ) { | ||||
|                 $commands[] = $command; | ||||
|             } | ||||
|         } | ||||
|         return $commands; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,74 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The abstract command that save something in the admin. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| abstract class Ai1ec_Command_Save_Abstract extends Ai1ec_Command { | ||||
|  | ||||
|     protected $_controller = 'front'; | ||||
|  | ||||
|     protected $_action; | ||||
|  | ||||
|     protected $_nonce_name; | ||||
|  | ||||
|     protected $_nonce_action; | ||||
|  | ||||
|     /** | ||||
|      * Public constructor, set the strategy according to the type. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      * @param Ai1ec_Request_Parser $request | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         Ai1ec_Request_Parser $request, | ||||
|         array $args | ||||
|     ) { | ||||
|         parent::__construct( $registry, $request ); | ||||
|         if ( ! is_array( $args['action'] ) ) { | ||||
|             $args['action'] = array( | ||||
|                 $args['action'] => true, | ||||
|             ); | ||||
|         } | ||||
|         $this->_action = $args['action']; | ||||
|         $this->_nonce_action = $args['nonce_action']; | ||||
|         $this->_nonce_name = $args['nonce_name']; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|     */ | ||||
|     public function is_this_to_execute() { | ||||
|         $params = $this->get_parameters(); | ||||
|         if ( false === $params ) { | ||||
|             return false; | ||||
|         } | ||||
|         if ( $params['controller'] === $this->_controller && | ||||
|             isset( $this->_action[$params['action']] ) ) { | ||||
|             $pass = wp_verify_nonce( | ||||
|                 $_POST[$this->_nonce_name], | ||||
|                 $this->_nonce_action | ||||
|             ); | ||||
|             if ( ! $pass ) { | ||||
|                 wp_die( "Failed security check" ); | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::set_render_strategy() | ||||
|     */ | ||||
|     public function set_render_strategy( Ai1ec_Request_Parser $request ) { | ||||
|         $this->_render_strategy = $this->_registry->get( | ||||
|             'http.response.render.strategy.redirect' | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,175 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that save settings. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Save_Settings extends Ai1ec_Command_Save_Abstract { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::do_execute() | ||||
|      */ | ||||
|     public function do_execute() { | ||||
|         $settings = $this->_registry->get( 'model.settings' ); | ||||
|         $options  = $settings->get_options(); | ||||
|         $_POST['default_tags_categories'] = ( | ||||
|             isset( $_POST['default_tags_categories_default_categories'] ) || | ||||
|             isset( $_POST['default_tags_categories_default_tags'] ) | ||||
|         ); | ||||
|         // set some a variable to true to trigger the saving. | ||||
|         $_POST['enabled_views'] = true; | ||||
|         // let other plugin modify the post | ||||
|         $_POST = apply_filters( 'ai1ec_before_save_settings', $_POST ); | ||||
|         foreach ( $options as $name => $data ) { | ||||
|             $value = null; | ||||
|             if ( isset( $_POST[$name] ) ) { | ||||
|                 // if a validator is pecified, use it. | ||||
|                 if ( isset( $data['renderer']['validator'] ) ) { | ||||
|                     $validator = $this->_registry->get( | ||||
|                         'validator.' . $data['renderer']['validator'], | ||||
|                         $_POST[$name] | ||||
|                     ); | ||||
|                     try { | ||||
|                         $value = $validator->validate(); | ||||
|                     } catch ( Ai1ec_Value_Not_Valid_Exception $e ) { | ||||
|                         // don't save | ||||
|                         continue; | ||||
|                     } | ||||
|                 } else { | ||||
|                     switch ( $data['type'] ) { | ||||
|                         case 'bool': | ||||
|                             $value  = true; | ||||
|                             break; | ||||
|                         case 'int': | ||||
|                             $value  = (int)$_POST[$name]; | ||||
|                             break; | ||||
|                         case 'string': | ||||
|                             $value  = (string)$_POST[$name]; | ||||
|                             break; | ||||
|                         case 'array': | ||||
|                             $method = '_handle_saving_' . $name; | ||||
|                             $value  = null; | ||||
|                             if ( method_exists( $this, $method ) ) { | ||||
|                                 $value = $this->$method(); | ||||
|                             } | ||||
|                             $value = apply_filters( | ||||
|                                 'ai1ec' . $method, | ||||
|                                 $value, | ||||
|                                 $_REQUEST | ||||
|                             ); | ||||
|                             break; | ||||
|                         case 'mixed': | ||||
|                             $method = '_handle_saving_' . $name; | ||||
|                             $value  = null; | ||||
|                             if ( method_exists( $this, $method ) ) { | ||||
|                                 $value = $this->$method( $_POST[$name] ); | ||||
|                             } | ||||
|                             $value = apply_filters( | ||||
|                                 'ai1ec' . $method, | ||||
|                                 $value, | ||||
|                                 $_REQUEST | ||||
|                             ); | ||||
|                             break; | ||||
|                         case 'wp_option': // set the corresponding WP option | ||||
|                             $this->_registry->get( 'model.option' ) | ||||
|                                 ->set( $name, $_POST[$name], true ); | ||||
|                             $value = (string)$_POST[$name]; | ||||
|                     } | ||||
|                 } | ||||
|             } else { | ||||
|                 if ( isset( $data['type'] ) && 'bool' === $data['type'] ) { | ||||
|                     $value = false; | ||||
|                 } | ||||
|             } | ||||
|             if ( null !== $value ) { | ||||
|                 $settings->set( $name, stripslashes_deep( $value ) ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $new_options = $settings->get_options(); | ||||
|         // let extension manipulate things if needed. | ||||
|         do_action( 'ai1ec_settings_updated', $options, $new_options ); | ||||
|  | ||||
|         $settings->persist(); | ||||
|  | ||||
|         $api = $this->_registry->get( 'model.api.api-registration' ); | ||||
|         $api->check_settings( true ); | ||||
|  | ||||
|         return array( | ||||
|             'url'        => ai1ec_admin_url( | ||||
|                 'edit.php?post_type=ai1ec_event&page=all-in-one-event-calendar-settings' | ||||
|             ), | ||||
|             'query_args' => array( | ||||
|                 'updated' => 1 | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle saving enabled_views. | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function _handle_saving_enabled_views() { | ||||
|         $settings      = $this->_registry->get( 'model.settings' ); | ||||
|         $enabled_views = $settings->get( 'enabled_views' ); | ||||
|         foreach ( $enabled_views as $view => &$options ) { | ||||
|             $options['enabled'] = isset( $_POST['view_' . $view . '_enabled'] ); | ||||
|             $options['default'] = isset( $_POST['default_calendar_view'] ) | ||||
|                 ? $_POST['default_calendar_view'] === $view | ||||
|                 : false; | ||||
|             $options['enabled_mobile'] = | ||||
|                 isset( $_POST['view_' . $view . '_enabled_mobile'] ); | ||||
|             $options['default_mobile'] = | ||||
|                 isset( $_POST['default_calendar_view_mobile'] ) && | ||||
|                 $_POST['default_calendar_view_mobile'] === $view; | ||||
|         } | ||||
|         return $enabled_views; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle saving default_tag_categories option | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     protected function _handle_saving_default_tags_categories() { | ||||
|         return array( | ||||
|             'tags' => isset( $_POST['default_tags_categories_default_tags'] ) ? | ||||
|                 $_POST['default_tags_categories_default_tags'] : | ||||
|                 array(), | ||||
|             'categories' => isset( $_POST['default_tags_categories_default_categories'] ) ? | ||||
|                 $_POST['default_tags_categories_default_categories'] : | ||||
|                 array(), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates the calendar page if a string is passed. | ||||
|      * | ||||
|      * @param int|string $calendar_page | ||||
|      * | ||||
|      * @return int | ||||
|      */ | ||||
|     protected function _handle_saving_calendar_page_id( $calendar_page ) { | ||||
|         if ( | ||||
|             ! is_numeric( $calendar_page ) && | ||||
|             preg_match( '#^__auto_page:(.*?)$#', $calendar_page, $matches ) | ||||
|         ) { | ||||
|             return wp_insert_post( | ||||
|                 array( | ||||
|                     'post_title'     => $matches[1], | ||||
|                     'post_type'      => 'page', | ||||
|                     'post_status'    => 'publish', | ||||
|                     'comment_status' => 'closed' | ||||
|                 ) | ||||
|             ); | ||||
|         } else { | ||||
|             return (int)$calendar_page; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,70 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The concrete command that save theme options. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Command | ||||
|  */ | ||||
| class Ai1ec_Command_Save_Theme_Options extends Ai1ec_Command_Save_Abstract { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Command::is_this_to_execute() | ||||
|     */ | ||||
|     public function do_execute() { | ||||
|         $variables = array(); | ||||
|  | ||||
|         // Handle updating of theme options. | ||||
|         if ( isset( $_POST[Ai1ec_View_Theme_Options::SUBMIT_ID] ) ) { | ||||
|             $_POST     = stripslashes_deep( $_POST ); | ||||
|             $lessphp   = $this->_registry->get( 'less.lessphp' ); | ||||
|             $variables = $lessphp->get_saved_variables(); | ||||
|             foreach ( $variables as $variable_name => $variable_params ) { | ||||
|                 if ( isset( $_POST[$variable_name] ) ) { | ||||
|                     // Avoid problems for those who are foolish enough to leave php.ini | ||||
|                     // settings at their defaults, which has magic quotes enabled. | ||||
|                     if ( get_magic_quotes_gpc() ) { | ||||
|                         $_POST[$variable_name] = stripslashes( $_POST[$variable_name] ); | ||||
|                     } | ||||
|                     if ( | ||||
|                         Ai1ec_Less_Variable_Font::CUSTOM_FONT === $_POST[$variable_name] | ||||
|                     ) { | ||||
|                         $_POST[$variable_name] = $_POST[$variable_name . | ||||
|                             Ai1ec_Less_Variable_Font::CUSTOM_FONT_ID_SUFFIX]; | ||||
|                     } | ||||
|                     // update the original array | ||||
|                     $variables[$variable_name]['value'] = $_POST[$variable_name]; | ||||
|                 } | ||||
|             } | ||||
|             $_POST = add_magic_quotes( $_POST ); | ||||
|  | ||||
|         } | ||||
|         // Handle reset of theme options. | ||||
|         elseif ( isset( $_POST[Ai1ec_View_Theme_Options::RESET_ID] ) ) { | ||||
|             $option = $this->_registry->get( 'model.option' ); | ||||
|             $option->delete( 'ai1ec_less_variables' ); | ||||
|             $option->delete( 'ai1ec_render_css' ); | ||||
|             do_action( 'ai1ec_reset_less_variables' ); | ||||
|         } | ||||
|  | ||||
|         $css = $this->_registry->get( 'css.frontend' ); | ||||
|  | ||||
|         $css->update_variables_and_compile_css( | ||||
|             $variables, | ||||
|             isset( | ||||
|                 $_POST[Ai1ec_View_Theme_Options::RESET_ID] | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         return array( | ||||
|             'url'        => ai1ec_admin_url( | ||||
|                 'edit.php?post_type=ai1ec_event&page=all-in-one-event-calendar-edit-css' | ||||
|             ), | ||||
|             'query_args' => array(), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,133 @@ | ||||
| <?php | ||||
| /** | ||||
|  * Theme backward compatibility check. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib.Compatibility | ||||
|  */ | ||||
| class Ai1ec_Compatibility_Check extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var null|bool Calculated response. | ||||
|      */ | ||||
|     protected $_use_backward_compatibility = null; | ||||
|  | ||||
|     /** | ||||
|      * Returns whether calendar is using backward compatibility or not. | ||||
|      * | ||||
|      * @return bool|null Result | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      */ | ||||
|     public function use_backward_compatibility() { | ||||
|         if ( null === $this->_use_backward_compatibility ) { | ||||
|             $this->_use_backward_compatibility = ( | ||||
|                 AI1EC_THEME_COMPATIBILITY_FER && | ||||
|                 ! $this->_registry->get( | ||||
|                     'model.settings' | ||||
|                 )->get( 'ai1ec_use_frontend_rendering', false ) | ||||
|             ); | ||||
|         } | ||||
|         return $this->_use_backward_compatibility; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Observes settings changes. | ||||
|      * | ||||
|      * If setting ai1ec_use_frontend_rendering is changed and set to true | ||||
|      * perfoms theme check. | ||||
|      * | ||||
|      * Checks if Date format was changed, and update dates to the new format | ||||
|      * | ||||
|      * @param array $old_options Old options array. | ||||
|      * @param array $new_options New options array. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      */ | ||||
|     public function ai1ec_settings_observer( $old_options, $new_options ) { | ||||
|  | ||||
|         // Date format change checker | ||||
|         $old_date_format_value = isset( $old_options['input_date_format'] ) | ||||
|             ? $old_options['input_date_format']['value'] | ||||
|             : null; | ||||
|         $new_date_format_value = isset( $new_options['input_date_format'] ) | ||||
|             ? $new_options['input_date_format']['value'] | ||||
|             : null; | ||||
|         if ( | ||||
|             null !== $old_date_format_value&& | ||||
|             null !== $new_date_format_value && | ||||
|             $old_date_format_value !== $new_date_format_value | ||||
|         ) { | ||||
|             // Get "Default calendar start date" | ||||
|             $exact_date = isset( $old_options['exact_date'] ) | ||||
|                 ? $old_options['exact_date']['value'] | ||||
|                 : ''; | ||||
|             if ( '' !== $exact_date ) { | ||||
|                 $date_system = $this->_registry->get( 'date.system' ); | ||||
|  | ||||
|                 // Change "Default calendar start date" format | ||||
|                 $new_exact_date = $date_system->convert_date_format( | ||||
|                     $exact_date, | ||||
|                     $old_date_format_value, | ||||
|                     $new_date_format_value | ||||
|                 ); | ||||
|  | ||||
|                 // Save new value | ||||
|                 $settings = $this->_registry->get( 'model.settings' ); | ||||
|                 $settings->set( 'exact_date', $new_exact_date ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Frontend rendering checker | ||||
|         $old_value = isset( $old_options['ai1ec_use_frontend_rendering'] ) | ||||
|             ? (bool)$old_options['ai1ec_use_frontend_rendering']['value'] | ||||
|             : null; | ||||
|         $new_value = isset( $new_options['ai1ec_use_frontend_rendering'] ) | ||||
|             ? (bool)$new_options['ai1ec_use_frontend_rendering']['value'] | ||||
|             : null; | ||||
|         if ( | ||||
|             $old_value === $new_value || | ||||
|             ! $new_value | ||||
|         ) { | ||||
|             return; | ||||
|         } | ||||
|         if ( $this->is_current_theme_outside_core() ) { | ||||
|             $this->_registry->get( 'notification.admin' )->store( | ||||
|                 Ai1ec_I18n::__( 'You have turned on Frontend Rendering and you are using a custom calendar theme. If your theme does not support Frontend Rendering, your calendar may not work correctly.' ), | ||||
|                 'error', | ||||
|                 0, | ||||
|                 array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                 true | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns whether current calendar theme is located under core directory | ||||
|      * or not. | ||||
|      * | ||||
|      * @return bool Result | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      */ | ||||
|     public function is_current_theme_outside_core() { | ||||
|         $option     = $this->_registry->get( 'model.option' ); | ||||
|         $cur_theme  = $option->get( 'ai1ec_current_theme', array() ); | ||||
|         $theme_root = dirname( AI1EC_DEFAULT_THEME_ROOT ); | ||||
|         return ( | ||||
|             isset( $cur_theme['theme_root'] ) && | ||||
|             ( | ||||
|                 $theme_root !== dirname( $cur_theme['theme_root'] ) && | ||||
|                 false === strpos( | ||||
|                     $cur_theme['theme_root'], | ||||
|                     'all-in-one-event-calendar-saas-theme' | ||||
|                 ) | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Command line compatibility options. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.1 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Compatibility | ||||
|  */ | ||||
| class Ai1ec_Compatibility_Cli { | ||||
|  | ||||
|     /** | ||||
|      * @var bool Whereas current session is command line. | ||||
|      */ | ||||
|     protected $_is_cli = false; | ||||
|  | ||||
|     /** | ||||
|      * Check current SAPI. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct() { | ||||
|         $this->_is_cli = 'cli' === php_sapi_name(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if running command line session. | ||||
|      * | ||||
|      * @return bool Yes/No | ||||
|      */ | ||||
|     public function is_cli() { | ||||
|         return $this->_is_cli; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Disable DB debug when in command line session. | ||||
|      * | ||||
|      * @param bool $debug Current value. | ||||
|      * | ||||
|      * @return bool Optionally modified value. | ||||
|      */ | ||||
|     public function disable_db_debug( $debug ) { | ||||
|         if ( $this->_is_cli ) { | ||||
|             return false; | ||||
|         } | ||||
|         return $debug; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Memory related methods. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.1 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib | ||||
|  */ | ||||
| class Ai1ec_Compatibility_Memory extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Checks if there is enough available free memory. | ||||
|      * | ||||
|      * @param string $required_limit String memory value i.e '24M' | ||||
|      * | ||||
|      * @return bool True or false. | ||||
|      */ | ||||
|     public function check_available_memory( $required_limit = 0 ) { | ||||
|         if ( 0 === $required_limit ) { | ||||
|             return true; | ||||
|         } | ||||
|         $required = $this->_string_to_bytes( $required_limit ); | ||||
|         $limit    = $this->_string_to_bytes( ini_get( 'memory_limit' ) ); | ||||
|         $used     = $this->get_usage(); | ||||
|         return ( $limit - $used ) >= $required; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns current memory usage if available - otherwise 0. | ||||
|      * | ||||
|      * @return int Memory usage. | ||||
|      */ | ||||
|     public function get_usage() { | ||||
|         if ( is_callable( 'memory_get_usage' ) ) { | ||||
|             return memory_get_usage(); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Converts string value to int. | ||||
|      * | ||||
|      * @param string $v String value. | ||||
|      * | ||||
|      * @return int Number. | ||||
|      */ | ||||
|     protected function _string_to_bytes( $v ) { | ||||
|         $letter     = substr( $v, -1 ); | ||||
|         $value      = (int)substr( $v, 0, -1 ); | ||||
|         $powers     = array( | ||||
|             'K' => 10, | ||||
|             'M' => 20, | ||||
|             'G' => 30, | ||||
|             'T' => 40, | ||||
|             'P' => 50, | ||||
|         ); | ||||
|         $multiplier = 1; | ||||
|         if ( isset( $powers[$letter] ) ) { | ||||
|             $multiplier = pow( 2, $powers[$letter] ); | ||||
|         } | ||||
|         if ( 1 === $multiplier ) { | ||||
|             return (int)$v; | ||||
|         } | ||||
|         return $value * $multiplier; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,142 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Wrapper for all the output buffer calls (ob_*) | ||||
|  */ | ||||
| class Ai1ec_Compatibility_OutputBuffer extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Wrap the ob_end_flush() method: | ||||
|      * Flush (send) the output buffer and turn off output buffering | ||||
|      * | ||||
|      * @return bool Returns TRUE on success or FALSE on failure | ||||
|      */ | ||||
|     public function end_flush() { | ||||
|         return ob_end_flush(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Wrap the ob_get_contents() method: | ||||
|      * Return the contents of the output buffer | ||||
|      * | ||||
|      * @retrun string This will return the contents of the output buffer or | ||||
|      * FALSE, if output buffering isn't active. | ||||
|      */ | ||||
|     public function get_contents() { | ||||
|         return ob_get_contents(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Wrap the ob_get_level() method: | ||||
|      * Returns the nesting level of the output buffering mechanism. | ||||
|      * | ||||
|      * @return int Returns the level of nested output buffering handlers or zero | ||||
|      * if output buffering is not active. | ||||
|      */ | ||||
|     public function get_level() { | ||||
|         return ob_get_level(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Wrap the ob_start() method: turn output buffering on. | ||||
|      * | ||||
|      * @param callback      $output_callback Method to be called on finish. | ||||
|      * @param int           $chunk_size      Buffer size limite. | ||||
|      * @param int|bool|null $flags           Control performable operations. | ||||
|      * | ||||
|      * @return bool Returns TRUE on success or FALSE on failure. | ||||
|      */ | ||||
|     public function start( | ||||
|         $output_callback = null, | ||||
|         $chunk_size      = 0, | ||||
|         $flags           = null | ||||
|     ) { | ||||
|         if ( 'ob_gzhandler' === $output_callback && $this->is_zlib_active() ) { | ||||
|             $output_callback = null; // do not compress again | ||||
|         } | ||||
|         if ( null === $flags ) { | ||||
|             if ( defined( 'PHP_OUTPUT_HANDLER_STDFLAGS' ) ) { | ||||
|                 $flags = PHP_OUTPUT_HANDLER_STDFLAGS; | ||||
|             } else { | ||||
|                 $flags = true; | ||||
|             } | ||||
|         } | ||||
|         return ob_start( $output_callback, $chunk_size, $flags ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gzip the content if possible. | ||||
|      * | ||||
|      * @param string $string | ||||
|      */ | ||||
|     public function gzip_if_possible( $string ) { | ||||
|         $gzip = $this->_registry->get( 'http.request' )->client_use_gzip(); | ||||
|         // only use output buffering for gzip. | ||||
|         if ( $gzip ) { | ||||
|             $this->start( 'ob_gzhandler' ); | ||||
|             header( 'Content-Encoding: gzip' ); | ||||
|         } | ||||
|         echo $string; | ||||
|         if ( $gzip ) { | ||||
|             $this->end_flush(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if zlib compression is activated. | ||||
|      * | ||||
|      * @return bool Activation status. | ||||
|      */ | ||||
|     public function is_zlib_active() { | ||||
|         $zlib = ini_get( 'zlib.output_compression' ); | ||||
|         if ( 'off' !== strtolower( $zlib ) && ! empty( $zlib ) ) { | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Wrap ob_end_clean() and check the zip level to avoid crashing: | ||||
|      * Clean (erase) the output buffer and turn off output buffering | ||||
|      * | ||||
|      * @return bool Returns TRUE on success or FALSE on failure | ||||
|      */ | ||||
|     public function end_clean() { | ||||
|         return ob_end_clean(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handle the closing of the object buffer when more then one object buffer | ||||
|      * is opened. This cause an error if it's not correctly handled | ||||
|      * | ||||
|      * @return bool Returns TRUE on success or FALSE on failure | ||||
|      */ | ||||
|     public function end_clean_all() { | ||||
|         if ( ini_get( 'zlib.output_compression' ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         $level   = $this->get_level(); | ||||
|         $success = true; | ||||
|         while ( $level ) { | ||||
|             $this->end_clean(); | ||||
|             $new_level = $this->get_level(); | ||||
|             if ( $new_level === $level ) { | ||||
|                 $success = false; | ||||
|                 break; | ||||
|             } | ||||
|             $level = $new_level; | ||||
|         } | ||||
|         return $success; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Wrap the ob_get_clean() method: | ||||
|      * Gets the current buffer contents and delete current output buffer. | ||||
|      * | ||||
|      * @return string Returns the contents of the output buffer and end output | ||||
|      * buffering. If output buffering isn't active then FALSE is returned. | ||||
|      */ | ||||
|     public function get_clean(){ | ||||
|         return ob_get_clean(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,100 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Execution guard. | ||||
|  * | ||||
|  * Guards process execution for multiple runs at the same moment of time. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Compatibility | ||||
|  */ | ||||
| class Ai1ec_Compatibility_Xguard extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Return time of last acquisition. | ||||
|      * | ||||
|      * If execution guard with that name was never acquired it returns 0 (zero). | ||||
|      * If acquisition fails it returns false. | ||||
|      * | ||||
|      * @param string $name    Name of guard to be acquired. | ||||
|      * @param int    $timeout Timeout, how long lock is held after acquisition. | ||||
|      * | ||||
|      * @return bool Success to acquire lock for given period. | ||||
|      */ | ||||
|     public function acquire( $name, $timeout = 86400 ) { | ||||
|         $name  = $this->safe_name( $name ); | ||||
|         $dbi   = $this->_registry->get( 'dbi.dbi' ); | ||||
|         $entry = array( | ||||
|             'time' => time(), | ||||
|             'pid'  => getmypid(), | ||||
|         ); | ||||
|         $table = $dbi->get_table_name( 'options' ); | ||||
|         $dbi->query( 'START TRANSACTION' ); | ||||
|         $query = $dbi->prepare( | ||||
|             'SELECT option_value FROM ' . $table . | ||||
|             ' WHERE option_name = %s', | ||||
|             $name | ||||
|         ); | ||||
|         $prev = $dbi->get_var( $query ); | ||||
|         if ( ! empty( $prev ) ) { | ||||
|             $prev = json_decode( $prev, true ); | ||||
|         } | ||||
|         if ( | ||||
|             ! empty( $prev ) && | ||||
|             ( (int)$prev['time'] + (int)$timeout ) >= $entry['time'] | ||||
|         ) { | ||||
|             $dbi->query( 'ROLLBACK' ); | ||||
|             return false; | ||||
|         } | ||||
|         $query = ''; | ||||
|         if ( empty( $prev ) ) { | ||||
|             $query = 'INSERT INTO'; | ||||
|         } else { | ||||
|             $query = 'UPDATE'; | ||||
|         } | ||||
|         $query .= ' `' . $table . '` SET `option_name` = %s, `option_value` = %s, `autoload` = 0'; | ||||
|         if ( ! empty( $prev ) ) { | ||||
|             $query .= ' WHERE `option_name` = %s'; | ||||
|         } | ||||
|         $query   = $dbi->prepare( $query, $name, json_encode( $entry ), $name ); | ||||
|         $success = $dbi->query( $query ); | ||||
|         if ( ! $success ) { | ||||
|             $dbi->query( 'ROLLBACK' ); | ||||
|             return false; | ||||
|         } | ||||
|         $dbi->query( 'COMMIT' ); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method release logs execution guard release phase. | ||||
|      * | ||||
|      * @param string $name Name of acquisition. | ||||
|      * | ||||
|      * @return bool Not expected to fail. | ||||
|      */ | ||||
|     public function release( $name ) { | ||||
|         return false !== $this->_registry->get( 'dbi.dbi' )->delete( | ||||
|             'options', | ||||
|             array( 'option_name' => $this->safe_name( $name ) ), | ||||
|             array( '%s' ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prepare safe file names. | ||||
|      * | ||||
|      * @param string $name Name of acquisition | ||||
|      * | ||||
|      * @return string Actual safeguard name to use. | ||||
|      */ | ||||
|     protected function safe_name( $name ) { | ||||
|         $name = preg_replace( '/[^A-Za-z_0-9\-]/', '_', $name ); | ||||
|         $name = trim( preg_replace( '/_+/', '_', $name ), '_' ); | ||||
|         $name = 'ai1ec_xlock_' . $name; | ||||
|         return substr( $name, 0, 50 ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,87 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Content filtering. | ||||
|  * | ||||
|  * Guards process execution for multiple runs at the same moment of time. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.1 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Content | ||||
|  */ | ||||
| class Ai1ec_Content_Filters extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Stored original the_content filters. | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $_filters_the_content = array(); | ||||
|  | ||||
|     /** | ||||
|      * Flag if filters are cleared. | ||||
|      * @var bool | ||||
|      */ | ||||
|     protected $_filters_the_content_cleared = false; | ||||
|  | ||||
|     /** | ||||
|      * Clears all the_content filters excluding few defaults. | ||||
|      * | ||||
|      * @global array $wp_filter | ||||
|      * | ||||
|      * @return Ai1ec_Content_Filters This class. | ||||
|      */ | ||||
|     public function clear_the_content_filters() { | ||||
|         global $wp_filter; | ||||
|         if ( $this->_filters_the_content_cleared ) { | ||||
|             return $this; | ||||
|         } | ||||
|         if ( isset( $wp_filter['the_content'] ) ) { | ||||
|             $this->_filters_the_content = $wp_filter['the_content']; | ||||
|         } | ||||
|         remove_all_filters( 'the_content' ); | ||||
|         add_filter( 'the_content', 'wptexturize' ); | ||||
|         add_filter( 'the_content', 'convert_smilies' ); | ||||
|         add_filter( 'the_content', 'convert_chars' ); | ||||
|         add_filter( 'the_content', 'wpautop' ); | ||||
|         $this->_filters_the_content_cleared = true; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Restores the_content filters. | ||||
|      * | ||||
|      * @global array $wp_filter | ||||
|      * | ||||
|      * @return Ai1ec_Content_Filters This class. | ||||
|      */ | ||||
|     public function restore_the_content_filters() { | ||||
|         global $wp_filter; | ||||
|         if ( | ||||
|             ! $this->_filters_the_content_cleared || | ||||
|             empty( $this->_filters_the_content ) | ||||
|         ) { | ||||
|             return $this; | ||||
|         } | ||||
|         $wp_filter['the_content'] = $this->_filters_the_content; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if event edit page should display "Move to Trash" button. | ||||
|      * | ||||
|      * @param array $allcaps An array of all the user's capabilities. | ||||
|      * @param array $caps    Actual capabilities for meta capability. | ||||
|      * | ||||
|      * @return array Capabilities or empty array. | ||||
|      */ | ||||
|     public function display_trash_link( $allcaps, $caps ) { | ||||
|         if ( | ||||
|             isset( $_GET['instance'] ) && | ||||
|             in_array( 'delete_published_ai1ec_events', $caps ) | ||||
|         ) { | ||||
|             return array(); | ||||
|         } | ||||
|         return $allcaps; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										111
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cookie/dto.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/cookie/dto.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The cookie Data Transfer Object. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Cookie | ||||
|  */ | ||||
| class Ai1ec_Cookie_Present_Dto { | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_cookie_set_for_calendar_page = false; | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_cookie_set_for_shortcode = false; | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     private $shortcode_cookie; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $calendar_cookie; | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_a_cookie_set_for_this_page = false; | ||||
|  | ||||
|     /** | ||||
|      * @return the $is_a_cookie_set_for_this_page | ||||
|      */ | ||||
|     public function get_is_a_cookie_set_for_this_page() { | ||||
|         return $this->is_a_cookie_set_for_this_page; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $is_a_cookie_set_for_this_page | ||||
|      */ | ||||
|     public function set_is_a_cookie_set_for_this_page( | ||||
|         $is_a_cookie_set_for_this_page | ||||
|     ) { | ||||
|         $this->is_a_cookie_set_for_this_page = $is_a_cookie_set_for_this_page; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return boolean the $is_calendar_page | ||||
|      */ | ||||
|     public function get_is_cookie_set_for_calendar_page() { | ||||
|         return $this->is_cookie_set_for_calendar_page; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return boolean the $is_cookie_set | ||||
|      */ | ||||
|     public function get_is_cookie_set_for_shortcode() { | ||||
|         return $this->is_cookie_set_for_shortcode; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return array the $shortcode_cookie | ||||
|      */ | ||||
|     public function get_shortcode_cookie() { | ||||
|         return $this->shortcode_cookie; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string the $calendar_cookie | ||||
|      */ | ||||
|     public function get_calendar_cookie() { | ||||
|         return $this->calendar_cookie; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $is_calendar_page | ||||
|      */ | ||||
|     public function set_is_cookie_set_for_calendar_page( $is_cookie_set_for_calendar_page ) { | ||||
|         $this->is_cookie_set_for_calendar_page = $is_cookie_set_for_calendar_page; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $is_cookie_set | ||||
|      */ | ||||
|     public function set_is_cookie_set_for_shortcode( $is_cookie_set ) { | ||||
|         $this->is_cookie_set_for_shortcode = $is_cookie_set; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param multitype: $shortcode_cookie | ||||
|      */ | ||||
|     public function set_shortcode_cookie( $shortcode_cookie ) { | ||||
|         $this->shortcode_cookie = $shortcode_cookie; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $calendar_cookie | ||||
|      */ | ||||
|     public function set_calendar_cookie( $calendar_cookie ) { | ||||
|         $this->calendar_cookie = $calendar_cookie; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,70 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * An utility class for cookies. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Cookie | ||||
|  */ | ||||
| class Ai1ec_Cookie_Utility extends Ai1ec_Base { | ||||
|  | ||||
|     static public $types = array( | ||||
|         'cat_ids', | ||||
|         'tag_ids', | ||||
|         'auth_ids', | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      *  Check if a cookie is set for the current page | ||||
|      * | ||||
|      * @return Ai1ec_Cookie_Present_Dto | ||||
|      */ | ||||
|     public function is_cookie_set_for_current_page() { | ||||
|         $cookie_dto = $this->_registry->get( 'cookie.dto' ); | ||||
|         $settings = $this->_registry->get( 'model.settings' ); | ||||
|         $calendar_url       = get_page_link( $settings->get( 'calendar_page_id' ) ); | ||||
|         $requested_page_url = Ai1ec_Wp_Uri_Helper::get_current_url( true ); | ||||
|         $cookie_set = isset( $_COOKIE['ai1ec_saved_filter'] ); | ||||
|         if( false !== $cookie_set ) { | ||||
|             $cookie = json_decode( stripslashes( $_COOKIE['ai1ec_saved_filter'] ), true ); | ||||
|             if ( | ||||
|                 $calendar_url === $requested_page_url && | ||||
|                 isset( $cookie['calendar_page'] ) && | ||||
|                 $cookie['calendar_page'] !== $calendar_url | ||||
|             ) { | ||||
|                 $cookie_dto->set_calendar_cookie( $cookie['calendar_page'] ); | ||||
|                 $cookie_dto->set_is_cookie_set_for_calendar_page( true ); | ||||
|                 $cookie_dto->set_is_a_cookie_set_for_this_page( true ); | ||||
|             } else if ( isset( $cookie[$requested_page_url] ) ) { | ||||
|                 $cookie_dto->set_shortcode_cookie( $cookie[$requested_page_url] ); | ||||
|                 $cookie_dto->set_is_cookie_set_for_shortcode( true ); | ||||
|                 $cookie_dto->set_is_a_cookie_set_for_this_page( true ); | ||||
|             } else if ( | ||||
|                 // we must make the is_page( $ai1ec_settings->calendar_page_id ) for a really edge case | ||||
|                 // when for example the calendar page is http://localhost/wordpress_pro/?page_id=1 | ||||
|                 // and the requested page is http://localhost/wordpress_pro/?page_id=1234 | ||||
|                 strpos( $requested_page_url, $calendar_url ) === 0 && | ||||
|                 isset( $cookie['calendar_page'] ) && | ||||
|                 is_page( $settings->get( 'calendar_page_id' ) ) | ||||
|                  ) { | ||||
|                 // This is the case after a redirect from the calendar page | ||||
|                 $cookie_dto->set_is_a_cookie_set_for_this_page( true ); | ||||
|                 $cookie_dto->set_calendar_cookie( $cookie['calendar_page'] ); | ||||
|             } | ||||
|         } | ||||
|         return $cookie_dto; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns path for cookies. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function get_path_for_cookie() { | ||||
|         $parsed = parse_url( ai1ec_site_url() ); | ||||
|         return isset( $parsed['path'] ) ? $parsed['path'] : '/'; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										117
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/css/admin.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/css/admin.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The class which handles Admin CSS. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Css | ||||
|  */ | ||||
| class Ai1ec_Css_Admin  extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Enqueue any scripts and styles in the admin side, depending on context. | ||||
|      * | ||||
|      * @wp_hook admin_enqueue_scripts | ||||
|      * | ||||
|      */ | ||||
|     public function admin_enqueue_scripts( $hook_suffix ) { | ||||
|         $settings    = $this->_registry->get( 'model.settings' ); | ||||
|         $enqueuables = array( | ||||
|             'widgets.php'                     => array( | ||||
|                 array( 'style',  'widget.css', ), | ||||
|             ), | ||||
|             'edit-tags.php'                   => array( | ||||
|                 array( 'style',  'colorpicker.css', ), | ||||
|                 array( 'style',  'bootstrap.min.css', ), | ||||
|                 array( 'style',  'taxonomies.css', ), | ||||
|             ), | ||||
|             'term.php'                        => array( | ||||
|                 array( 'style',  'colorpicker.css', ), | ||||
|                 array( 'style',  'bootstrap.min.css', ), | ||||
|                 array( 'style',  'taxonomies.css', ), | ||||
|             ), | ||||
|             $settings->get( 'settings_page' ) => array( | ||||
|                 array( 'script', 'common', ), | ||||
|                 array( 'script', 'wp-lists', ), | ||||
|                 array( 'script', 'postbox', ), | ||||
|                 array( 'style',  'settings.css', ), | ||||
|                 array( 'style',  'bootstrap.min.css', ), | ||||
|             ), | ||||
|             $settings->get( 'feeds_page' )    => array( | ||||
|                 array( 'script', 'common', ), | ||||
|                 array( 'script', 'wp-lists', ), | ||||
|                 array( 'script', 'postbox', ), | ||||
|                 array( 'style',  'settings.css', ), | ||||
|                 array( 'style',  'bootstrap.min.css', ), | ||||
|                 array( 'style',  'plugins/plugins-common.css', ), | ||||
|             ), | ||||
|             $settings->get( 'less_variables_page' ) => array( | ||||
|                 array( 'style', 'settings.css', ), | ||||
|                 array( 'style', 'bootstrap.min.css', ), | ||||
|                 array( 'style', 'bootstrap_colorpicker.css', ), | ||||
|             ), | ||||
|         ); | ||||
|  | ||||
|         if ( isset( $enqueuables[$hook_suffix] ) ) { | ||||
|             return $this->process_enqueue( $enqueuables[$hook_suffix] ); | ||||
|         } | ||||
|  | ||||
|         $aco        = $this->_registry->get( 'acl.aco' ); | ||||
|         $post_pages = array( 'post.php' => true, 'post-new.php' => true ); | ||||
|  | ||||
|         if ( | ||||
|             isset( $post_pages[$hook_suffix] ) || | ||||
|             $aco->are_we_editing_our_post() | ||||
|         ) { | ||||
|             return $this->process_enqueue( | ||||
|                 array( | ||||
|                     array( 'style', 'bootstrap.min.css', ), | ||||
|                     array( 'style', 'add_new_event.css', ), | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Enqueue scripts and styles. | ||||
|      * | ||||
|      * @param array $item_list List of scripts/styles to enqueue. | ||||
|      * | ||||
|      * @return bool Always true | ||||
|      */ | ||||
|     public function process_enqueue( array $item_list ) { | ||||
|         foreach ( $item_list as $item ) { | ||||
|             if ( 'script' === $item[0] ) { | ||||
|                 wp_enqueue_script( $item[1] ); | ||||
|             } else { | ||||
|                 wp_enqueue_style( | ||||
|                     $this->gen_style_hook( $item[1] ), | ||||
|                     AI1EC_ADMIN_THEME_CSS_URL . $item[1], | ||||
|                     array(), | ||||
|                     AI1EC_VERSION | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate a style hook for use with WordPress. | ||||
|      * | ||||
|      * @param string $script Name of enqueable script. | ||||
|      * | ||||
|      * @return string Hook to use with WordPress. | ||||
|      */ | ||||
|     public function gen_style_hook( $script ) { | ||||
|         return 'ai1ec_' . preg_replace( | ||||
|             '|[^a-z]+|', | ||||
|             '_', | ||||
|             basename( $script, '.css' ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,376 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The class which handles Frontend CSS. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Css | ||||
|  */ | ||||
| class Ai1ec_Css_Frontend extends Ai1ec_Base { | ||||
|  | ||||
|     const QUERY_STRING_PARAM                = 'ai1ec_render_css'; | ||||
|  | ||||
|     // This is for testing purpose, set it to AI1EC_PARSE_LESS_FILES_AT_EVERY_REQUEST value. | ||||
|     const PARSE_LESS_FILES_AT_EVERY_REQUEST = AI1EC_PARSE_LESS_FILES_AT_EVERY_REQUEST; | ||||
|  | ||||
|     const KEY_FOR_PERSISTANCE               = 'ai1ec_parsed_css'; | ||||
|     /** | ||||
|      * @var Ai1ec_Persistence_Context | ||||
|      */ | ||||
|     private $persistance_context; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Less_Lessphp | ||||
|      */ | ||||
|     private $lessphp_controller; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Option | ||||
|      */ | ||||
|     private $db_adapter; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Template_Adapter | ||||
|      */ | ||||
|     private $template_adapter; | ||||
|  | ||||
|     /** | ||||
|      * Possible paths/url for file cache | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $_cache_paths = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array which have been checked and are not writable | ||||
|      */ | ||||
|     protected $_folders_not_writable = array(); | ||||
|  | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry | ||||
|     ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_cache_paths[] = array( | ||||
|             'path' => AI1EC_CACHE_PATH, | ||||
|             'url'  => AI1EC_CACHE_URL | ||||
|         ); | ||||
|         if ( apply_filters( 'ai1ec_check_static_dir', true ) ) { | ||||
|             $filesystem = $this->_registry->get( 'filesystem.checker' ); | ||||
|             $wp_static_folder = $filesystem->get_ai1ec_static_dir_if_available(); | ||||
|             if ( '' !== $wp_static_folder ) { | ||||
|                 $this->_cache_paths[] = array( | ||||
|                     'path' => $wp_static_folder, | ||||
|                     'url'  => content_url() . '/uploads/ai1ec_static/' | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         $this->persistance_context = $this->_registry->get( | ||||
|             'cache.strategy.persistence-context', | ||||
|             self::KEY_FOR_PERSISTANCE, | ||||
|             $this->_cache_paths, | ||||
|             true | ||||
|         ); | ||||
|         if ( ! $this->persistance_context->is_file_cache() ) { | ||||
|              /* @TODO: move this to Settings -> Advanced -> Cache */ | ||||
|         } | ||||
|         $this->lessphp_controller  = $this->_registry->get( 'less.lessphp' ); | ||||
|         $this->db_adapter          = $this->_registry->get( 'model.option' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * Get if file cache is enabled | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_file_cache_enabled() { | ||||
|         return $this->persistance_context->is_file_cache(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get folders which are not writable | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function get_folders_not_writable() { | ||||
|         return $this->_folders_not_writable; | ||||
|     } | ||||
|     /** | ||||
|      * Renders the css for our frontend. | ||||
|      * | ||||
|      * Sets etags to avoid sending not needed data | ||||
|      */ | ||||
|     public function render_css() { | ||||
|         header( 'HTTP/1.1 200 OK' ); | ||||
|         header( 'Content-Type: text/css', true, 200 ); | ||||
|         // Aggressive caching to save future requests from the same client. | ||||
|         $etag = '"' . md5( __FILE__ . $_GET[self::QUERY_STRING_PARAM] ) . '"'; | ||||
|         header( 'ETag: ' . $etag ); | ||||
|         $max_age = 31536000; | ||||
|         $time_sys = $this->_registry->get( 'date.system' ); | ||||
|         header( | ||||
|             'Expires: ' . | ||||
|             gmdate( | ||||
|                 'D, d M Y H:i:s', | ||||
|                 $time_sys->current_time() + $max_age | ||||
|             ) . | ||||
|             ' GMT' | ||||
|         ); | ||||
|         header( 'Cache-Control: public, max-age=' . $max_age ); | ||||
|         if ( | ||||
|             empty( $_SERVER['HTTP_IF_NONE_MATCH'] ) || | ||||
|             $etag !== stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) | ||||
|         ) { | ||||
|             // compress data if possible | ||||
|             $this->_registry->get( 'compatibility.ob' ) | ||||
|                 ->gzip_if_possible( $this->get_compiled_css() ); | ||||
|         } else { | ||||
|             // Not modified! | ||||
|             status_header( 304 ); | ||||
|         } | ||||
|         // We're done! | ||||
|         Ai1ec_Http_Response_Helper::stop( 0 ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param string $css | ||||
|      * @throws Ai1ec_Cache_Write_Exception | ||||
|      */ | ||||
|     public function update_persistence_layer( $css ) { | ||||
|         $filename = $this->persistance_context->write_data_to_persistence( $css ); | ||||
|         $this->db_adapter->set( | ||||
|             'ai1ec_filename_css', | ||||
|             isset( $filename['file'] ) ? $filename['file'] : false, | ||||
|             true | ||||
|         ); | ||||
|         $this->save_less_parse_time( $filename['url'] ); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Get the url to retrieve the css | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function get_css_url() { | ||||
|         // get what's saved. I t could be false, a int or a string. | ||||
|         // if it's false or a int, use PHP to render CSS | ||||
|         $saved_par = $this->db_adapter->get( self::QUERY_STRING_PARAM ); | ||||
|         // if it's empty it's a new install probably. Return static css. | ||||
|         // if it's numeric, just consider it a new install | ||||
|         if ( empty( $saved_par ) ) { | ||||
|             $theme = $this->_registry->get( | ||||
|                 'model.option' | ||||
|             )->get( 'ai1ec_current_theme' ); | ||||
|             return Ai1ec_Http_Response_Helper::remove_protocols( | ||||
|                 apply_filters( | ||||
|                     'ai1ec_frontend_standard_css_url', | ||||
|                     $theme['theme_url'] . '/css/ai1ec_parsed_css.css' | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|         if ( is_numeric( $saved_par ) ) { | ||||
|             if ( $this->_registry->get( 'model.settings' )->get( 'render_css_as_link' ) ) { | ||||
|                 $time = (int) $saved_par; | ||||
|                 $template_helper = $this->_registry->get( 'template.link.helper' ); | ||||
|                 return Ai1ec_Http_Response_Helper::remove_protocols( | ||||
|                     add_query_arg( | ||||
|                         array( self::QUERY_STRING_PARAM => $time, ), | ||||
|                         trailingslashit( ai1ec_get_site_url() ) | ||||
|                     ) | ||||
|                 ); | ||||
|             } else { | ||||
|                 add_action( 'wp_head', array( $this, 'echo_css' ) ); | ||||
|                 return ''; | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         // otherwise return the string | ||||
|         return Ai1ec_Http_Response_Helper::remove_protocols( | ||||
|             $saved_par | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create the link that will be added to the frontend | ||||
|      */ | ||||
|     public function add_link_to_html_for_frontend() { | ||||
|         $url = $this->get_css_url(); | ||||
|         if ( '' !== $url && ! is_admin() ) { | ||||
|             wp_enqueue_style( 'ai1ec_style', $url, array(), AI1EC_VERSION ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public function echo_css() { | ||||
|         echo '<style>'; | ||||
|         echo $this->get_compiled_css(); | ||||
|         echo '</style>'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Invalidate the persistence layer only after a successful compile of the | ||||
|      * LESS files. | ||||
|      * | ||||
|      * @param  array   $variables          LESS variable array to use | ||||
|      * @param  boolean $update_persistence Whether the persist successful compile | ||||
|      * | ||||
|      * @return boolean                     Whether successful | ||||
|      */ | ||||
|     public function invalidate_cache( | ||||
|         array $variables    = null, | ||||
|         $update_persistence = false | ||||
|     ) { | ||||
|         if ( ! $this->lessphp_controller->is_compilation_needed( $variables ) ) { | ||||
|             $this->_registry->get( | ||||
|                 'model.option' | ||||
|             )->delete( 'ai1ec_render_css' ); | ||||
|             return true; | ||||
|         } | ||||
|         $notification = $this->_registry->get( 'notification.admin' ); | ||||
|         if ( | ||||
|             ! $this->_registry->get( | ||||
|                 'compatibility.memory' | ||||
|             )->check_available_memory( AI1EC_LESS_MIN_AVAIL_MEMORY ) | ||||
|         ) { | ||||
|             $message = sprintf( | ||||
|                 Ai1ec_I18n::__( | ||||
|                     'CSS compilation failed because you don\'t have enough free memory (a minimum of %s is needed). Your calendar will not render or function properly without CSS. Please read <a href="https://time.ly/document/user-guide/getting-started/pre-sale-questions/">this article</a> to learn how to increase your PHP memory limit.' | ||||
|                 ), | ||||
|                 AI1EC_LESS_MIN_AVAIL_MEMORY | ||||
|             ); | ||||
|             $notification->store( | ||||
|                 $message, | ||||
|                 'error', | ||||
|                 1, | ||||
|                 array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                 true | ||||
|             ); | ||||
|             return; | ||||
|         } | ||||
|         try { | ||||
|             // Try to parse the css | ||||
|             $css = $this->lessphp_controller->parse_less_files( $variables ); | ||||
|             // Reset the parse time to force a browser reload of the CSS, whether we are | ||||
|             // updating persistence or not. Do it here to be sure files compile ok. | ||||
|             $this->save_less_parse_time(); | ||||
|             if ( $update_persistence ) { | ||||
|                 $this->update_persistence_layer( $css ); | ||||
|             } else { | ||||
|                 $this->persistance_context->delete_data_from_persistence(); | ||||
|             } | ||||
|         } catch ( Ai1ec_Cache_Write_Exception $e ) { | ||||
|             // This means successful during parsing but problems persisting the CSS. | ||||
|             $message = '<p>' . Ai1ec_I18n::__( "The LESS file compiled correctly but there was an error while saving the generated CSS to persistence." ) . '</p>'; | ||||
|             $notification->store( $message, 'error' ); | ||||
|             return false; | ||||
|         } catch ( Exception $e ) { | ||||
|             // An error from lessphp. | ||||
|             $message = sprintf( | ||||
|                 Ai1ec_I18n::__( '<p><strong>There was an error while compiling CSS.</strong> The message returned was: <em>%s</em></p>' ), | ||||
|                 $e->getMessage() | ||||
|             ); | ||||
|             $notification->store( $message, 'error', 1 ); | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Update the less variables on the DB and recompile the CSS | ||||
|      * | ||||
|      * @param array $variables | ||||
|      * @param boolean $resetting are we resetting or updating variables? | ||||
|      */ | ||||
|     public function update_variables_and_compile_css( array $variables, $resetting ) { | ||||
|         $no_parse_errors = $this->invalidate_cache( $variables, true ); | ||||
|         $notification    = $this->_registry->get( 'notification.admin' ); | ||||
|  | ||||
|         if ( $no_parse_errors ) { | ||||
|             $this->db_adapter->set( | ||||
|                 Ai1ec_Less_Lessphp::DB_KEY_FOR_LESS_VARIABLES, | ||||
|                 $variables | ||||
|             ); | ||||
|  | ||||
|             if ( true === $resetting ) { | ||||
|                 $message = sprintf( | ||||
|                     '<p>' . Ai1ec_I18n::__( | ||||
|                         "Theme options were successfully reset to their default values. <a href='%s'>Visit site</a>" | ||||
|                     ) . '</p>', | ||||
|                     ai1ec_get_site_url() | ||||
|                 ); | ||||
|             } else { | ||||
|                 $message = sprintf( | ||||
|                     '<p>' .Ai1ec_I18n::__( | ||||
|                         "Theme options were updated successfully. <a href='%s'>Visit site</a>" | ||||
|                     ) . '</p>', | ||||
|                     ai1ec_get_site_url() | ||||
|                 ); | ||||
|             } | ||||
|  | ||||
|             $notification->store( $message ); | ||||
|         } | ||||
|     } | ||||
|     /** | ||||
|      * Try to get the CSS from cache. | ||||
|      * If it's not there re-generate it and save it to cache | ||||
|      * If we are in preview mode, recompile the css using the theme present in the url. | ||||
|      * | ||||
|      */ | ||||
|     public function get_compiled_css() { | ||||
|         try { | ||||
|             // If we want to force a recompile, we throw an exception. | ||||
|             if( self::PARSE_LESS_FILES_AT_EVERY_REQUEST === true ) { | ||||
|                 throw new Ai1ec_Cache_Not_Set_Exception(); | ||||
|             }else { | ||||
|                 // This throws an exception if the key is not set | ||||
|                 $css = $this->persistance_context->get_data_from_persistence(); | ||||
|                 return $css; | ||||
|             } | ||||
|         } catch ( Ai1ec_Cache_Not_Set_Exception $e ) { | ||||
|             $css = $this->lessphp_controller->parse_less_files(); | ||||
|             try { | ||||
|                 $this->update_persistence_layer( $css ); | ||||
|                 return $css; | ||||
|             } catch ( Ai1ec_Cache_Write_Exception $e ) { | ||||
|                 if ( ! self::PARSE_LESS_FILES_AT_EVERY_REQUEST ) { | ||||
|                     $this->_registry->get( 'notification.admin' ) | ||||
|                         ->store( | ||||
|                             sprintf( | ||||
|                                 __( | ||||
|                                     'Your CSS is being compiled on every request, which causes your calendar to perform slowly. The following error occurred: %s', | ||||
|                                     AI1EC_PLUGIN_NAME | ||||
|                                 ), | ||||
|                                 $e->getMessage() | ||||
|                             ), | ||||
|                             'error', | ||||
|                             2, | ||||
|                             array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                             true | ||||
|                         ); | ||||
|                 } | ||||
|  | ||||
|                 // If something is really broken, still return the css. | ||||
|                 // This means we parse it every time. This should never happen. | ||||
|                 return $css; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Save the path to the CSS file or false to load standard CSS | ||||
|      */ | ||||
|     private function save_less_parse_time( $data = false ) { | ||||
|         $to_save = is_string( $data ) ? | ||||
|                     $data : | ||||
|                     $this->_registry->get( 'date.system' )->current_time(); | ||||
|         $this->db_adapter->set( | ||||
|             self::QUERY_STRING_PARAM, | ||||
|             $to_save, | ||||
|             true | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,220 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * A model to manage database changes | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Database | ||||
|  */ | ||||
| class Ai1ec_Database_Applicator extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Dbi Instance of wpdb object | ||||
|      */ | ||||
|     protected $_db = NULL; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Database Instance of Ai1ec_Database object | ||||
|      */ | ||||
|     protected $_database = NULL; | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
|      * | ||||
|      * Initialize object, by storing instance of `wpdb` in local variable | ||||
|      * | ||||
|      * @return void Constructor does not return | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_db       = $registry->get( 'dbi.dbi' ); | ||||
|         $this->_database = $registry->get( 'database.helper' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * remove_instance_duplicates method | ||||
|      * | ||||
|      * Remove duplicate instances, from `event_instances` table | ||||
|      * | ||||
|      * @param int $depth Private argument, denoting number of iterations to | ||||
|      *                   try, before reverting to slow approach | ||||
|      * | ||||
|      * @return bool Success | ||||
|      */ | ||||
|     public function remove_instance_duplicates( $depth = 5 ) { | ||||
|         $use_field  = 'id'; | ||||
|         if ( $depth < 0 ) { | ||||
|             $use_field = 'post_id'; | ||||
|         } | ||||
|         $table      = $this->_table( 'event_instances' ); | ||||
|         if ( false === $this->_database->table_exists( $table ) ) { | ||||
|             return true; | ||||
|         } | ||||
|         $duplicates = $this->find_duplicates( | ||||
|                 $table, | ||||
|                 $use_field, | ||||
|                 array( 'post_id', 'start' ) | ||||
|         ); | ||||
|         $count      = count( $duplicates ); | ||||
|         if ( $count > 0 ) { | ||||
|             $sql_query  = 'DELETE FROM ' . $table . | ||||
|             ' WHERE ' . $use_field . ' IN ( ' . | ||||
|             implode( ', ', $duplicates ) . ' )'; | ||||
|             $this->_db->query( $sql_query ); | ||||
|         } | ||||
|         if ( 'post_id' === $use_field ) { // slow branch | ||||
|             $event_instance_model = $this->_registry->get( | ||||
|                 'model.event.instance' | ||||
|             ); | ||||
|             foreach ( $duplicates as $post_id ) { | ||||
|                 try { | ||||
|                     $event_instance_model->recreate( | ||||
|                         $this->_registry->get( 'model.event', $post_id ) | ||||
|                     ); | ||||
|                 } catch ( Ai1ec_Exception $excpt ) { | ||||
|                     // discard any internal errors | ||||
|                 } | ||||
|             } | ||||
|         } else if ( $count > 0 ) { // retry | ||||
|             return $this->remove_instance_duplicates( --$depth ); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * find_duplicates method | ||||
|      * | ||||
|      * Find a list of duplicates in table, given search key and groupping fields | ||||
|      * | ||||
|      * @param string $table   Name of table, to search duplicates in | ||||
|      * @param string $primary Column, to return values for | ||||
|      * @param array  $group   List of fields, to group values on | ||||
|      * | ||||
|      * @return array List of primary field values | ||||
|      */ | ||||
|     public function find_duplicates( $table, $primary, array $group ) { | ||||
|         $sql_query = ' | ||||
|             SELECT | ||||
|                 MIN( {{primary}} ) AS dup_primary -- pop oldest | ||||
|             FROM {{table}} | ||||
|             GROUP BY {{group}} | ||||
|             HAVING COUNT( {{primary}} ) > 1 | ||||
|         '; | ||||
|         $sql_query = str_replace( | ||||
|                 array( | ||||
|                     '{{table}}', | ||||
|                     '{{primary}}', | ||||
|                     '{{group}}', | ||||
|                 ), | ||||
|                 array( | ||||
|                     $this->_table( $table ), | ||||
|                     $this->_escape_column( $primary ), | ||||
|                     implode( | ||||
|                             ', ', | ||||
|                             array_map( array( $this, '_escape_column' ), $group ) | ||||
|                     ), | ||||
|                 ), | ||||
|                 $sql_query | ||||
|         ); | ||||
|         $result = $this->_db->get_col( $sql_query ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check list of tables for consistency. | ||||
|      * | ||||
|      * @return array List of inconsistencies. | ||||
|      */ | ||||
|     public function check_db_consistency_for_date_migration() { | ||||
|         $db_migration = $this->_registry->get( 'database.datetime-migration' ); | ||||
|         /* @var $db_migration Ai1ecdm_Datetime_Migration */ | ||||
|         $tables       = $db_migration->get_tables(); | ||||
|         if ( ! is_array( $tables ) ) { | ||||
|             return array(); | ||||
|         } | ||||
|  | ||||
|         // for date migration purposes we can assume | ||||
|         // that all columns need to be the same type | ||||
|         $info = array(); | ||||
|         foreach( $tables as $t_name => $t_columns ) { | ||||
|             if ( count( $t_columns ) < 2 ) { | ||||
|                 continue; | ||||
|             } | ||||
|             $tbl_error = $this->_check_single_table( | ||||
|                 $t_name, | ||||
|                 $db_migration->get_columns( $t_name ), | ||||
|                 $t_columns | ||||
|             ); | ||||
|             if ( null !== $tbl_error ) { | ||||
|                 $info[] = $tbl_error; | ||||
|             } | ||||
|         } | ||||
|         return $info; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if single table columns are the same type. | ||||
|      * | ||||
|      * @param string $t_name    Table name for details purposes. | ||||
|      * @param array  $db_cols   Columns from database. | ||||
|      * @param array  $t_columns Columns to check from DDL. | ||||
|      * | ||||
|      * @return string|null Inconsistency description, if any. | ||||
|      */ | ||||
|     protected function _check_single_table( | ||||
|         $t_name, | ||||
|         array $db_cols, | ||||
|         array $t_columns | ||||
|     ) { | ||||
|         $type = null; | ||||
|         foreach ( $db_cols as $c_field => $c_type ) { | ||||
|             if ( ! in_array( $c_field, $t_columns ) ) { | ||||
|                 continue; | ||||
|             } | ||||
|             if ( null === $type ) { | ||||
|                 $type = strtolower( $c_type ); | ||||
|             } | ||||
|             if ( strtolower( $c_type ) !== $type ) { | ||||
|                 return sprintf( | ||||
|                     Ai1ec_I18n::__( | ||||
|                         'Date columns in table %s have different types.' | ||||
|                     ), | ||||
|                     $t_name | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get fully qualified table name, to use in queries. | ||||
|      * | ||||
|      * @param string $table Name of table, to convert. | ||||
|      * | ||||
|      * @return string Qualified table name. | ||||
|      */ | ||||
|     protected function _table( $table ) { | ||||
|         $prefix = $this->_db->get_table_name( 'ai1ec_' ); | ||||
|         if ( substr( $table, 0, strlen( $prefix ) ) !== $prefix ) { | ||||
|             $table = $prefix . $table; | ||||
|         } | ||||
|         return $table; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _escape_column method | ||||
|      * | ||||
|      * Escape column, enquoting it in MySQL specific characters | ||||
|      * | ||||
|      * @param string $name Name of column to quote | ||||
|      * | ||||
|      * @return string Escaped column name | ||||
|      */ | ||||
|     protected function _escape_column( $name ) { | ||||
|         return '`' . $name . '`'; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,473 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The date-time migration utility layer. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Database | ||||
|  */ | ||||
| class Ai1ecdm_Datetime_Migration { | ||||
|  | ||||
|     /** | ||||
|      * @var wpdb Instance of wpdb or it's extension. | ||||
|      */ | ||||
|     protected $_dbi           = null; | ||||
|  | ||||
|     /** | ||||
|      * @var array List of tables to be processed. | ||||
|      */ | ||||
|     protected $_tables        = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of indices on selected tables. | ||||
|      */ | ||||
|     protected $_indices       = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var string Table suffix used in migration process. | ||||
|      */ | ||||
|     protected $_table_suffix  = '_dt_ui_mig'; | ||||
|  | ||||
|     /** | ||||
|      * @var string Column suffix used in data transformation. | ||||
|      */ | ||||
|     protected $_column_suffix = '_transformation'; | ||||
|  | ||||
|     /** | ||||
|      * Output debug statements. | ||||
|      * | ||||
|      * @var mixed $arg1 Number of arguments to output. | ||||
|      * | ||||
|      * @return bool True when debug is in action. | ||||
|      */ | ||||
|     static public function debug( /** polymorphic arg list **/ ) { | ||||
|         if ( ! defined( 'AI1EC_DEBUG' ) || ! AI1EC_DEBUG ) { | ||||
|             return false; | ||||
|         } | ||||
|         $argv = func_get_args(); | ||||
|         foreach ( $argv as $value ) { | ||||
|             echo '<pre class="timely-debug">', | ||||
|                 '<small>', microtime( true ), '</small>', "\n"; | ||||
|             var_export( $value ); | ||||
|             echo '</pre>'; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Acquire references of global variables and define non-scalar values. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         $this->_dbi = $registry->get( 'dbi.dbi' ); | ||||
|         $this->_tables = array( | ||||
|             $this->_dbi->get_table_name( 'ai1ec_events' )                => array( | ||||
|                 'start', | ||||
|                 'end', | ||||
|             ), | ||||
|             $this->_dbi->get_table_name( 'ai1ec_event_instances' )       => array( | ||||
|                 'start', | ||||
|                 'end', | ||||
|             ), | ||||
|             $this->_dbi->get_table_name( 'ai1ec_facebook_users_events' ) => array( | ||||
|                 'start', | ||||
|             ), | ||||
|         ); | ||||
|         $this->_indices = array( | ||||
|             $this->_dbi->get_table_name( 'ai1ec_event_instances' ) => array( | ||||
|                 'evt_instance' => array( | ||||
|                     'unique'  => true, | ||||
|                     'columns' => array( 'post_id', 'start' ), | ||||
|                     'name'      => 'evt_instance', | ||||
|                 ), | ||||
|             ), | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Interface to underlying methods to use as a filter callback. | ||||
|      * | ||||
|      * @wp_hook ai1ec_perform_scheme_update | ||||
|      * | ||||
|      * @return bool True when database is up to date. | ||||
|      */ | ||||
|     public function filter_scheme_update() { | ||||
|         return ( ! $this->is_change_required() || $this->execute() ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve columns for a given table. | ||||
|      * | ||||
|      * Checks if table exists before attempting to retrieve it. | ||||
|      * | ||||
|      * @param string $table Name of table to retrieve columns for. | ||||
|      * | ||||
|      * @return array Map of column names and their types. | ||||
|      */ | ||||
|     public function get_columns( $table ) { | ||||
|         if ( ! $this->_is_table( $table ) ) { | ||||
|             return array(); | ||||
|         } | ||||
|         $list = $this->_dbi->get_results( | ||||
|             'SHOW COLUMNS FROM `' . $table . '`' | ||||
|         ); | ||||
|         $columns = array(); | ||||
|         foreach ( $list as $column ) { | ||||
|             $columns[$column->Field] = strtolower( $column->Type ); | ||||
|         } | ||||
|         return $columns; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve list of indices for a given table. | ||||
|      * | ||||
|      * Checks if table exists before attempting to retrieve it. | ||||
|      * | ||||
|      * @param string $table Name of table to retrieve indices for. | ||||
|      * | ||||
|      * @return array Map of index names. | ||||
|      */ | ||||
|     public function get_indices( $table ) { | ||||
|         if ( ! $this->_is_table( $table ) ) { | ||||
|             return array(); | ||||
|         } | ||||
|         $list = $this->_dbi->get_results( | ||||
|             'SHOW INDEX FROM `' . $table . '`' | ||||
|         ); | ||||
|         $columns = array(); | ||||
|         foreach ( $list as $column ) { | ||||
|             $columns[ strtolower( $column->Key_name ) ] = $column->Key_name; | ||||
|         } | ||||
|         return $columns; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if database change is required. | ||||
|      * | ||||
|      * @return bool True if any changes are required. | ||||
|      */ | ||||
|     public function is_change_required() { | ||||
|         foreach ( $this->_tables as $table => $columns ) { | ||||
|             $existing = $this->get_columns( $table ); | ||||
|             foreach ( $existing as $column => $type ) { | ||||
|                 if ( | ||||
|                     false === array_search( $column, $columns ) || | ||||
|                     0 !== stripos( $type, 'datetime' ) | ||||
|                 ) { | ||||
|                     unset( $existing[$column] ); | ||||
|                 } | ||||
|             } | ||||
|             if ( empty( $existing ) ) { | ||||
|                 unset( $this->_tables[$table] ); | ||||
|             } | ||||
|         } | ||||
|         if ( ! empty( $this->_tables ) ) { | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Single stop for executing database changes. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function execute() { | ||||
|         return $this->create_copies() | ||||
|             && $this->apply_changes_to_copies() | ||||
|             && $this->swap_tables(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create copies of tables to be transformed. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function create_copies() { | ||||
|         $tables = array_keys( $this->_tables ); | ||||
|         foreach ( $tables as $table ) { | ||||
|             $suffixed = $table . $this->_table_suffix; | ||||
|             if ( | ||||
|                 ! $this->drop( $suffixed ) || | ||||
|                 ! $this->copy( $table, $suffixed ) | ||||
|             ) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         self::debug( | ||||
|             'Copies of following tables created successfully:', | ||||
|             $tables | ||||
|         ); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Transform columns on copied tables. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function apply_changes_to_copies() { | ||||
|         foreach ( $this->_tables as $table => $columns ) { | ||||
|             $name = $table . $this->_table_suffix; | ||||
|             if ( | ||||
|                 ! ( | ||||
|                     $this->drop_indices( $table, $name ) | ||||
|                     && $this->out_of_bounds_fix( $table, $name ) | ||||
|                     && $this->add_columns( $name, $columns ) | ||||
|                     && $this->transform_dates( $name, $columns ) | ||||
|                     && $this->replace_columns( $name, $columns ) | ||||
|                     && $this->restore_indices( $table, $name ) | ||||
|                 ) | ||||
|             ) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         self::debug( | ||||
|             'Table copies successfully modified:', | ||||
|             $this->_tables | ||||
|         ); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Keep old table under unique name and move modified into it's place. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function swap_tables() { | ||||
|         $tables  = array_keys( $this->_tables ); | ||||
|         $renames = array(); | ||||
|         foreach ( $tables as $table ) { | ||||
|             $modified  = $table . $this->_table_suffix; | ||||
|             $backup    = $table . '_' . date( 'Y_m_d' ) . '_' . getmypid(); | ||||
|             $renames[] = '`' . $table    . '` TO `' . $backup . '`'; | ||||
|             $renames[] = '`' . $modified . '` TO `' . $table  . '`'; | ||||
|         } | ||||
|         $sql_query = 'RENAME TABLE ' . implode( ', ', $renames ); | ||||
|         if ( false === $this->_dbi->query( $sql_query ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         self::debug( | ||||
|             'Tables successfully swaped:', | ||||
|             $this->_tables | ||||
|         ); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Drop given table indices. | ||||
|      * | ||||
|      * @param string $name  Original table name. | ||||
|      * @param string $table Table to actually perform changes upon. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function drop_indices( $name, $table ) { | ||||
|         self::debug( __METHOD__ ); | ||||
|         if ( ! isset( $this->_indices[$name] ) ) { | ||||
|             return true; | ||||
|         } | ||||
|         $existing = $this->get_indices( $table ); | ||||
|         foreach ( $this->_indices[$name] as $index => $options ) { | ||||
|             if ( isset( $existing[$index] ) ) { | ||||
|                 $sql_query = 'ALTER TABLE `' . $table . '` DROP INDEX `' . | ||||
|                     $index . '`'; | ||||
|                 if ( false === $this->_dbi->query( $sql_query ) ) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add intermediate columns to a table. | ||||
|      * | ||||
|      * @param string $table   Name of table to modify. | ||||
|      * @param array  $columns List of column names to be added. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function add_columns( $table, $columns ) { | ||||
|         self::debug( __METHOD__ ); | ||||
|         $column_particles = array(); | ||||
|         foreach ( $columns as $column ) { | ||||
|             $name = $column . $this->_column_suffix; | ||||
|             $column_particles[] = 'ADD COLUMN ' . $name . | ||||
|                 ' INT(10) UNSIGNED NOT NULL'; | ||||
|         } | ||||
|         $sql_query = 'ALTER TABLE `' . $table . '` ' . | ||||
|             implode( ', ', $column_particles ); | ||||
|         return ( false !== $this->_dbi->query( $sql_query ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Copy date values from `DATETIME` to `INT(10)` columns. | ||||
|      * | ||||
|      * @param string $table   Name of table to modify. | ||||
|      * @param array  $columns List of column names to be copied. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function transform_dates( $table, $columns ) { | ||||
|         self::debug( __METHOD__ ); | ||||
|         $update_particles = array(); | ||||
|         foreach ( $columns as $column ) { | ||||
|             $name      = $column . $this->_column_suffix; | ||||
|             $new_value = '\'1970-01-01 00:00:00\''; | ||||
|             if ( 'end' === $column && in_array( 'start', $columns ) ) { | ||||
|                 $new_value = 'IFNULL(`start`, ' . $new_value . ')'; | ||||
|             } | ||||
|             $update_particles[] = '`' . $name . | ||||
|                 '` = UNIX_TIMESTAMP( IFNULL(`' . $column . '`, ' . $new_value . ' ))'; | ||||
|         } | ||||
|         $sql_query = 'UPDATE `' . $table . '` SET ' . | ||||
|             implode( ', ', $update_particles ); | ||||
|         return ( false !== $this->_dbi->query( $sql_query ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Drop old columns and move intermediate columns into their place. | ||||
|      * | ||||
|      * @param string $table   Name of table to modify. | ||||
|      * @param array  $columns List of column names to be replaced. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function replace_columns( $table, $columns ) { | ||||
|         self::debug( __METHOD__ ); | ||||
|         $snippets = array(); | ||||
|         foreach ( $columns as $column ) { | ||||
|             $snippets[] = 'DROP COLUMN `' . $column . '`'; | ||||
|             $snippets[] = 'CHANGE COLUMN `' . $column . $this->_column_suffix . | ||||
|                 '` `' . $column . '` INT(10) UNSIGNED NOT NULL'; | ||||
|         } | ||||
|         $sql_query = 'ALTER TABLE `' . $table . '` ' . | ||||
|             implode( ', ', $snippets ); | ||||
|         return ( false !== $this->_dbi->query( $sql_query ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Restore indices for table processed. | ||||
|      * | ||||
|      * @param string $name  Original table name. | ||||
|      * @param string $table Table to actually perform changes upon. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function restore_indices( $name, $table ) { | ||||
|         self::debug( __METHOD__ ); | ||||
|         if ( ! isset( $this->_indices[$name] ) ) { | ||||
|             return true; | ||||
|         } | ||||
|         foreach ( $this->_indices[$name] as $index => $options ) { | ||||
|             $sql_query = 'ALTER TABLE `' . $table . '` ADD'; | ||||
|             if ( $options['unique'] ) { | ||||
|                 $sql_query .= ' UNIQUE'; | ||||
|             } | ||||
|             $sql_query .= ' INDEX `' . | ||||
|                 $index . '` (`' . | ||||
|                 implode( '`, `', $options['columns'] ) . | ||||
|                 '`)'; | ||||
|             if ( false === $this->_dbi->query( $sql_query ) ) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Drop table. | ||||
|      * | ||||
|      * @param string $table Name of table to drop. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function drop( $table ) { | ||||
|         $sql_query = 'DROP TABLE IF EXISTS ' . $table; | ||||
|         return ( false !== $this->_dbi->query( $sql_query ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create table copy with full data set. | ||||
|      * | ||||
|      * @param string $existing  Name of table to copy. | ||||
|      * @param string $new_table Name of table to create. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function copy( $existing, $new_table ) { | ||||
|         $queries = array( | ||||
|             'CREATE TABLE ' . $new_table . ' LIKE '          . $existing, | ||||
|             'INSERT INTO '  . $new_table . ' SELECT * FROM ' . $existing, | ||||
|         ); | ||||
|         foreach ( $queries as $query ) { | ||||
|             self::debug( $query ); | ||||
|             if ( false === $this->_dbi->query( $query ) ) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         $count_new = $this->_dbi->get_var( | ||||
|             'SELECT COUNT(*) FROM ' . $new_table | ||||
|         ); | ||||
|         $count_old = $this->_dbi->get_var( | ||||
|             'SELECT COUNT(*) FROM ' . $existing | ||||
|         ); | ||||
|         // check if difference between tables records doesn't exceed | ||||
|         // several least significant bits of old table entries count | ||||
|         if ( absint( $count_new - $count_old ) > ( $count_old >> 4 ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return list of tables to be processed | ||||
|      * | ||||
|      * @return array List of tables to be processed | ||||
|      */ | ||||
|     public function get_tables() { | ||||
|         return $this->_tables; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Delete events dated before or at `1970-01-01 00:00:00`. | ||||
|      * | ||||
|      * @param string $table Original table. | ||||
|      * @param string $name  Temporary table to replay changes onto. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function out_of_bounds_fix( $table, $name ) { | ||||
|         static $instances = null; | ||||
|         if ( null === $instances ) { | ||||
|             $instances = $this->_dbi->get_table_name( 'ai1ec_event_instances' ); | ||||
|         } | ||||
|         if ( $instances !== $table ) { | ||||
|             return true; | ||||
|         } | ||||
|         $query = 'DELETE FROM `' . | ||||
|             $this->_dbi->get_table_name( $name ) . | ||||
|             '` WHERE `start` <= \'1970-01-01 00:00:00\''; | ||||
|         return ( false !== $this->_dbi->query( $query ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if given table exists. | ||||
|      * | ||||
|      * @param string $table Name of table to check. | ||||
|      * | ||||
|      * @return bool Existence. | ||||
|      */ | ||||
|     protected function _is_table( $table ) { | ||||
|         $name = $this->_dbi->get_var( | ||||
|             $this->_dbi->prepare( 'SHOW TABLES LIKE %s', $table ) | ||||
|         ); | ||||
|         return ( (string)$table === (string)$name ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| <?php | ||||
| /** | ||||
|  * In case of database update failure this exception is thrown | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Database.Exception | ||||
|  */ | ||||
| class Ai1ec_Database_Error extends Ai1ec_Exception { | ||||
|  | ||||
|     /** | ||||
|      * Override parent method to include tip. | ||||
|      * | ||||
|      * @return string Message to render. | ||||
|      */ | ||||
|     public function get_html_message() { | ||||
|         $message = '<p>' . Ai1ec_I18n::__( | ||||
|             'Database update has failed. Please make sure, that database user, defined in <em>wp-config.php</em> has permissions, to make changes (<strong>ALTER TABLE</strong>) to the database.' | ||||
|         ) . | ||||
|         '</p><p>' . sprintf( | ||||
|             Ai1ec_I18n::__( 'Error encountered: %s' ), | ||||
|             $this->getMessage() | ||||
|         ) . '</p>'; | ||||
|         return $message; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * In case of database schema modification failure this exception is thrown | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Database.Exception | ||||
|  */ | ||||
| class Ai1ec_Database_Schema_Exception extends Ai1ec_Exception | ||||
| { | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
| /** | ||||
|  * In case of database update failure this exception is thrown | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Database.Exception | ||||
|  */ | ||||
| class Ai1ec_Database_Update_Exception extends Ai1ec_Exception { | ||||
| } | ||||
| @@ -0,0 +1,756 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Ai1ec_Database class | ||||
|  * | ||||
|  * Class responsible for generic database operations | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Database | ||||
|  */ | ||||
| class Ai1ec_Database_Helper { | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of tables and their parsed definitions | ||||
|      */ | ||||
|     protected $_schema_delta = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array List of valid table prefixes | ||||
|      */ | ||||
|     protected $_prefixes     = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var wpdb Localized instance of wpdb object | ||||
|      */ | ||||
|     protected $_db = NULL; | ||||
|  | ||||
|     /** | ||||
|      * @var bool If set to true - no operations will be performed | ||||
|      */ | ||||
|     protected $_dry_run  = false; | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         $this->_db       = $registry->get( 'dbi.dbi' ); | ||||
|         $this->_prefixes = array( | ||||
|             $this->_db->get_table_name( 'ai1ec_' ), | ||||
|             $this->_db->get_table_name(), | ||||
|             '', | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if dry run is enabled | ||||
|      * | ||||
|      * @param bool $dry Change dryness [optional=NULL] | ||||
|      * | ||||
|      * @return bool Dryness of run or previous value | ||||
|      */ | ||||
|     public function is_dry( $dry = NULL ) { | ||||
|         if ( NULL !== $dry ) { | ||||
|             $previous = $this->_dry_run; | ||||
|             $this->_dry_run = (bool)$dry; | ||||
|             return $previous; | ||||
|         } | ||||
|         return $this->_dry_run; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get fully-qualified table name given it's abbreviated form | ||||
|      * | ||||
|      * @param string $name         Name (abbreviation) of table to check | ||||
|      * @param bool   $ignore_check Return longest name if no table exist [false] | ||||
|      * | ||||
|      * @return string Fully-qualified table name | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Schema_Exception If no table matches | ||||
|      */ | ||||
|     public function table( $name, $ignore_check = false ) { | ||||
|         $existing  = $this->get_all_tables(); | ||||
|         $table     = NULL; | ||||
|         $candidate = NULL; | ||||
|         foreach ( $this->_prefixes as $prefix ) { | ||||
|             $candidate = $prefix . $name; | ||||
|             $index     = strtolower( $candidate ); | ||||
|             if ( isset( $existing[$index] ) ) { | ||||
|                 $table = $existing[$index]; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         if ( NULL === $table ) { | ||||
|             if ( true === $ignore_check ) { | ||||
|                 return $candidate; | ||||
|             } | ||||
|             throw new Ai1ec_Database_Schema_Exception( | ||||
|                 'Table \'' . $name . '\' does not exist' | ||||
|             ); | ||||
|         } | ||||
|         return $table; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Drop given indices from table | ||||
|      * | ||||
|      * @param string       $table   Name of table to modify | ||||
|      * @param string|array $indices List, or single, of indices to remove | ||||
|      * | ||||
|      * @return bool Success | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Schema_Exception If table is not found | ||||
|      */ | ||||
|     public function drop_indices( $table, $indices ) { | ||||
|         if ( ! is_array( $indices ) ) { | ||||
|             $indices = array( (string)$indices ); | ||||
|         } | ||||
|         $table    = $this->table( $table ); | ||||
|         $existing = $this->get_indices( $table ); | ||||
|         $removed  = 0; | ||||
|         foreach ( $indices as $index ) { | ||||
|             if ( | ||||
|                 ! isset( $existing[$index] ) || | ||||
|                 $this->_dry_query( | ||||
|                     'ALTER TABLE ' . $table . ' DROP INDEX ' . $index | ||||
|                 ) | ||||
|             ) { | ||||
|                 ++$removed; | ||||
|             } | ||||
|         } | ||||
|         return ( count( $indices ) === $removed ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create indices for given table | ||||
|      * | ||||
|      * Input ({@see $indices}) must be the same, as output of | ||||
|      * method {@see self::get_indices()}. | ||||
|      * | ||||
|      * @param string $table   Name of table to create indices for | ||||
|      * @param array  $indices Indices representation to be created | ||||
|      * | ||||
|      * @return bool Success | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Schema_Exception If table is not found | ||||
|      */ | ||||
|     public function create_indices( $table, array $indices ) { | ||||
|         $table = $this->table( $table ); | ||||
|         foreach ( $indices as $name => $definition ) { | ||||
|             $query = 'ALTER TABLE ' . $table . ' ADD '; | ||||
|             if ( $definition['unique'] ) { | ||||
|                 $query .= 'UNIQUE '; | ||||
|             } | ||||
|             $query .= 'KEY ' . $name . ' (' . | ||||
|                 implode( ', ', $definition['columns'] ) . | ||||
|                 ')'; | ||||
|             if ( ! $this->_dry_query( $query ) ) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * get_indices method | ||||
|      * | ||||
|      * Get map of indices defined for table. | ||||
|      * | ||||
|      * @NOTICE: no optimization will be performed here, and response will not | ||||
|      * be cached, to allow checking result of DDL statements. | ||||
|      * | ||||
|      * Returned array structure (example): | ||||
|      * array( | ||||
|      *     'index_name' => array( | ||||
|      *         'name'    => 'index_name', | ||||
|      *         'columns' => array( | ||||
|      *             'column1', | ||||
|      *             'column2', | ||||
|      *             'column3', | ||||
|      *         ), | ||||
|      *         'unique'  => true, | ||||
|      *     ), | ||||
|      * ) | ||||
|      * | ||||
|      * @param string $table Name of table to retrieve index names for | ||||
|      * | ||||
|      * @return array Map of index names and their representation | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Schema_Exception If table is not found | ||||
|      */ | ||||
|     public function get_indices( $table ) { | ||||
|         $sql_query = 'SHOW INDEXES FROM ' . $this->table( $table ); | ||||
|         $result    = $this->_db->get_results( $sql_query ); | ||||
|         $indices   = array(); | ||||
|         foreach ( $result as $index ) { | ||||
|             $name = $index->Key_name; | ||||
|             if ( ! isset( $indices[$name] ) ) { | ||||
|                 $indices[$name] = array( | ||||
|                     'name'    => $name, | ||||
|                     'columns' => array(), | ||||
|                     'unique'  => ! (bool)intval( $index->Non_unique ), | ||||
|                 ); | ||||
|             } | ||||
|             $indices[$name]['columns'][$index->Column_name] = $index->Sub_part; | ||||
|         } | ||||
|         return $indices; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform query, unless `dry_run` is selected. In later case just output | ||||
|      * the final query and return true. | ||||
|      * | ||||
|      * @param string $query SQL Query to execute | ||||
|      * | ||||
|      * @return mixed Query state, or true in dry run mode | ||||
|      */ | ||||
|     public function _dry_query( $query ) { | ||||
|         if ( $this->is_dry() ) { | ||||
|             pr( $query ); | ||||
|             return true; | ||||
|         } | ||||
|         $result = $this->_db->query( $query ); | ||||
|         if ( AI1EC_DEBUG ) { | ||||
|             echo '<h4>', $query, '</h4><pre>', var_export( $result, true ), '</pre>'; | ||||
|         } | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if given table exists | ||||
|      * | ||||
|      * @param string $table Name of table to check | ||||
|      * | ||||
|      * @return bool Existance | ||||
|      */ | ||||
|     public function table_exists( $table ) { | ||||
|         $map = $this->get_all_tables(); | ||||
|         return isset( $map[strtolower( $table )] ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return a list of all tables currently present | ||||
|      * | ||||
|      * @return array Map of tables present | ||||
|      */ | ||||
|     public function get_all_tables() { | ||||
|         /** | ||||
|          * @TODO: refactor using dbi.dbi::get_tables | ||||
|          */ | ||||
|         $sql_query = 'SHOW TABLES LIKE \'' . | ||||
|             $this->_db->get_table_name() . | ||||
|             '%\''; | ||||
|         $result    = $this->_db->get_col( $sql_query ); | ||||
|         $tables    = array(); | ||||
|         foreach ( $result as $table ) { | ||||
|             $tables[strtolower( $table )] = $table; | ||||
|         } | ||||
|         return $tables; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * apply_delta method | ||||
|      * | ||||
|      * Attempt to parse and apply given database tables definition, as a delta. | ||||
|      * Some validation is made prior to calling DB, and fields/indexes are also | ||||
|      * checked for consistency after sending queries to DB. | ||||
|      * | ||||
|      * NOTICE: only "CREATE TABLE" statements are handled. Others will, likely, | ||||
|      * be ignored, if passed through this method. | ||||
|      * | ||||
|      * @param string|array $query Single or multiple queries to perform on DB | ||||
|      * | ||||
|      * @return bool Success | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     public function apply_delta( $query ) { | ||||
|         if ( ! function_exists( 'dbDelta' ) ) { | ||||
|             require_once ABSPATH . 'wp-admin' . DIRECTORY_SEPARATOR . | ||||
|                 'includes' . DIRECTORY_SEPARATOR . 'upgrade.php'; | ||||
|         } | ||||
|         $success = false; | ||||
|         $this->_schema_delta = array(); | ||||
|         $queries = $this->_prepare_delta( $query ); | ||||
|         $result  = dbDelta( $queries ); | ||||
|         $success = $this->_check_delta(); | ||||
|         return $success; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * get_notices_helper method | ||||
|      * | ||||
|      * DIP implementing method, to give access to Ai1ec_Deferred_Rendering_Helper. | ||||
|      * | ||||
|      * @param Ai1ec_Deferred_Rendering_Helper $replacement Notices implementor | ||||
|      * | ||||
|      * @return Ai1ec_Deferred_Rendering_Helper Instance of notices implementor | ||||
|      */ | ||||
|     public function get_notices_helper( | ||||
|         Ai1ec_Deferred_Rendering_Helper $replacement = NULL | ||||
|     ) { | ||||
|         static $helper = NULL; | ||||
|         if ( NULL !== $replacement ) { | ||||
|             $helper = $replacement; | ||||
|         } | ||||
|         if ( NULL === $helper ) { | ||||
|             $helper = Ai1ec_Deferred_Rendering_Helper::get_instance(); | ||||
|         } | ||||
|         return $helper; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _prepare_delta method | ||||
|      * | ||||
|      * Prepare statements for execution. | ||||
|      * Attempt to parse various SQL definitions and compose the one, that is | ||||
|      * most likely to be accepted by delta engine. | ||||
|      * | ||||
|      * @param string|array $queries Single or multiple queries to perform on DB | ||||
|      * | ||||
|      * @return bool Success | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     protected function _prepare_delta( $queries ) { | ||||
|         if ( ! is_array( $queries ) ) { | ||||
|             $queries = explode( ';', $queries ); | ||||
|             $queries = array_filter( $queries ); | ||||
|         } | ||||
|         $current_table = NULL; | ||||
|         $ctable_regexp = '# | ||||
|             \s*CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?`?([^ ]+)`?\s* | ||||
|             \((.+)\) | ||||
|             ([^()]*) | ||||
|             #six'; | ||||
|         foreach ( $queries as $query ) { | ||||
|             if ( preg_match( $ctable_regexp, $query, $matches ) ) { | ||||
|                 $this->_schema_delta[$matches[1]] = array( | ||||
|                     'tblname' => $matches[1], | ||||
|                     'cryptic'  => NULL, | ||||
|                     'creator'  => '', | ||||
|                     'columns' => array(), | ||||
|                     'indexes' => array(), | ||||
|                     'content' => preg_replace( '#`#', '', $matches[2] ), | ||||
|                     'clauses' => $matches[3], | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         $this->_parse_delta(); | ||||
|         $sane_queries = array(); | ||||
|         foreach ( $this->_schema_delta as $table => $definition ) { | ||||
|             $create = 'CREATE TABLE ' . $table . " (\n"; | ||||
|             foreach ( $definition['columns'] as $column ) { | ||||
|                 $create .= '    ' . $column['create'] . ",\n"; | ||||
|             } | ||||
|             foreach ( $definition['indexes'] as $index ) { | ||||
|                 $create .= '    ' . $index['create'] . ",\n"; | ||||
|             } | ||||
|             $create = substr( $create, 0, -2 ) . "\n"; | ||||
|             $create .= ')' . $definition['clauses']; | ||||
|             $this->_schema_delta[$table]['creator'] = $create; | ||||
|             $this->_schema_delta[$table]['cryptic'] = md5( $create ); | ||||
|             $sane_queries[] = $create; | ||||
|         } | ||||
|         return $sane_queries; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _parse_delta method | ||||
|      * | ||||
|      * Parse table application (creation) statements into atomical particles. | ||||
|      * Here "atomical particles" stands for either columns, or indexes. | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     protected function _parse_delta() { | ||||
|         foreach ( $this->_schema_delta as $table => $definitions ) { | ||||
|             $listing = explode( "\n", $definitions['content'] ); | ||||
|             $listing = array_filter( $listing, array( $this, '_is_not_empty_line' ) ); | ||||
|             $lines   = count( $listing ); | ||||
|             $lineno  = 0; | ||||
|             foreach ( $listing as $line ) { | ||||
|                 ++$lineno; | ||||
|                 $line = trim( preg_replace( '#\s+#', ' ', $line ) ); | ||||
|                 $line_new = rtrim( $line, ',' ); | ||||
|                 if ( | ||||
|                     $lineno < $lines && $line === $line_new || | ||||
|                     $lineno == $lines && $line !== $line_new | ||||
|                 ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'Missing comma in line \'' . $line . '\'' | ||||
|                     ); | ||||
|                 } | ||||
|                 $line = $line_new; | ||||
|                 unset( $line_new ); | ||||
|                 $type = 'indexes'; | ||||
|                 if ( false === ( $record = $this->_parse_index( $line ) ) ) { | ||||
|                     $type   = 'columns'; | ||||
|                     $record = $this->_parse_column( $line ); | ||||
|                 } | ||||
|                 if ( isset( | ||||
|                         $this->_schema_delta[$table][$type][$record['name']] | ||||
|                 ) ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'For table `' . $table . '` entry ' . $type . | ||||
|                         ' named `' . $record['name'] . '` was declared twice' . | ||||
|                         ' in ' . $definitions | ||||
|                     ); | ||||
|                 } | ||||
|                 $this->_schema_delta[$table][$type][$record['name']] = $record; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _parse_index method | ||||
|      * | ||||
|      * Given string attempts to detect, if it is an index, and if yes - parse | ||||
|      * it to more navigable index definition for future validations. | ||||
|      * Creates modified index create line, for delta application. | ||||
|      * | ||||
|      * @param string $description Single "line" of CREATE TABLE statement body | ||||
|      * | ||||
|      * @return array|bool Index definition, or false if input does not look like index | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     protected function _parse_index( $description ) { | ||||
|         $description = preg_replace( | ||||
|             '#^CONSTRAINT(\s+`?[^ ]+`?)?\s+#six', | ||||
|             '', | ||||
|             $description | ||||
|         ); | ||||
|         $details     = explode( ' ', $description ); | ||||
|         $index       = array( | ||||
|             'name'    => NULL, | ||||
|             'content' => array(), | ||||
|             'create'  => '', | ||||
|         ); | ||||
|         $details[0]  = strtoupper( $details[0] ); | ||||
|         switch ( $details[0] ) { | ||||
|             case 'PRIMARY': | ||||
|                 $index['name']   = 'PRIMARY'; | ||||
|                 $index['create'] = 'PRIMARY KEY '; | ||||
|                 break; | ||||
|  | ||||
|             case 'UNIQUE': | ||||
|                 $name = $details[1]; | ||||
|                 if ( | ||||
|                     0 === strcasecmp( 'KEY',   $name ) || | ||||
|                     0 === strcasecmp( 'INDEX', $name ) | ||||
|                 ) { | ||||
|                     $name = $details[2]; | ||||
|                 } | ||||
|                 $index['name']   = $name; | ||||
|                 $index['create'] = 'UNIQUE KEY ' . $name; | ||||
|                 break; | ||||
|  | ||||
|             case 'KEY': | ||||
|             case 'INDEX': | ||||
|                 $index['name']   = $details[1]; | ||||
|                 $index['create'] = 'KEY ' . $index['name']; | ||||
|                 break; | ||||
|  | ||||
|             default: | ||||
|                 return false; | ||||
|         } | ||||
|         $index['content'] = $this->_parse_index_content( $description ); | ||||
|         $index['create'] .= ' ('; | ||||
|         foreach ( $index['content'] as $column => $length ) { | ||||
|             $index['create'] .= $column; | ||||
|             if ( NULL !== $length ) { | ||||
|                 $index['create'] .= '(' . $length . ')'; | ||||
|             } | ||||
|             $index['create'] .= ','; | ||||
|         } | ||||
|         $index['create'] = substr( $index['create'], 0, -1 ); | ||||
|         $index['create'] .= ')'; | ||||
|         return $index; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _parse_column method | ||||
|      * | ||||
|      * Parse column to parseable definition. | ||||
|      * Some valid definitions may still be not recognizes (namely SET and ENUM) | ||||
|      * thus one shall beware, when attempting to create such. | ||||
|      * Create alternative create table entry line for delta application. | ||||
|      * | ||||
|      * @param string $description Single "line" of CREATE TABLE statement body | ||||
|      * | ||||
|      * @return array Column definition | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     protected function _parse_column( $description ) { | ||||
|         $column_regexp = '#^ | ||||
|             ([a-z][a-z_]+)\s+ | ||||
|             ( | ||||
|                 [A-Z]+ | ||||
|                 (?:\s*\(\s*\d+(?:\s*,\s*\d+\s*)?\s*\))? | ||||
|                 (?:\s+UNSIGNED)? | ||||
|                 (?:\s+ZEROFILL)? | ||||
|                 (?:\s+BINARY)? | ||||
|                 (?: | ||||
|                     \s+CHARACTER\s+SET\s+[a-z][a-z_]+ | ||||
|                     (?:\s+COLLATE\s+[a-z][a-z0-9_]+)? | ||||
|                 )? | ||||
|             ) | ||||
|             ( | ||||
|                 \s+(?:NOT\s+)?NULL | ||||
|             )? | ||||
|             ( | ||||
|                 \s+DEFAULT\s+[^\s]+ | ||||
|             )? | ||||
|             (\s+ON\s+UPDATE\s+CURRENT_(?:TIMESTAMP|DATE))? | ||||
|             (\s+AUTO_INCREMENT)? | ||||
|             \s*,?\s* | ||||
|         $#six'; | ||||
|         if ( ! preg_match( $column_regexp, $description, $matches ) ) { | ||||
|             throw new Ai1ec_Database_Error( | ||||
|                 'Invalid column description ' . $description | ||||
|             ); | ||||
|         } | ||||
|         $column = array( | ||||
|             'name'    => $matches[1], | ||||
|             'content' => array(), | ||||
|             'create'  => '', | ||||
|         ); | ||||
|         if ( 0 === strcasecmp( 'boolean', $matches[2] ) ) { | ||||
|             $matches[2] = 'tinyint(1)'; | ||||
|         } | ||||
|         $column['content']['type'] = $matches[2]; | ||||
|         $column['content']['null'] = ( | ||||
|             ! isset( $matches[3] ) || | ||||
|             0 !== strcasecmp( 'NOT NULL', trim( $matches[3] ) ) | ||||
|         ); | ||||
|         $column['create'] = $column['name'] . ' ' . $column['content']['type']; | ||||
|         if ( isset( $matches[3] ) ) { | ||||
|             $column['create'] .= ' ' . | ||||
|                 implode( | ||||
|                     ' ', | ||||
|                     array_map( | ||||
|                         'trim', | ||||
|                         array_slice( $matches, 3 ) | ||||
|                     ) | ||||
|                 ); | ||||
|         } | ||||
|         return $column; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _parse_index_content method | ||||
|      * | ||||
|      * Parse index content, to a map of columns and their length. | ||||
|      * All index (content) cases shall be covered, although it is only tested. | ||||
|      * | ||||
|      * @param string Single line of CREATE TABLE statement, containing index definition | ||||
|      * | ||||
|      * @return array Map of columns and their length, as per index definition | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     protected function _parse_index_content( $description ) { | ||||
|         if ( ! preg_match( '#^[^(]+\((.+)\)$#', $description, $matches ) ) { | ||||
|             throw new Ai1ec_Database_Error( | ||||
|                 'Invalid index description ' . $description | ||||
|             ); | ||||
|         } | ||||
|         $columns = array(); | ||||
|         $textual = explode( ',', $matches[1] ); | ||||
|         $column_regexp = '#\s*([^(]+)(?:\s*\(\s*(\d+)\s*\))?\s*#sx'; | ||||
|         foreach ( $textual as $column ) { | ||||
|             if ( | ||||
|                 ! preg_match( $column_regexp, $column, $matches ) || ( | ||||
|                       isset( $matches[2] ) && | ||||
|                       (string)$matches[2] !== (string)intval( $matches[2] ) | ||||
|                 ) | ||||
|             ) { | ||||
|                 throw new Ai1ec_Database_Error( | ||||
|                     'Invalid index (columns) description ' . $description . | ||||
|                     ' as per \'' . $column . '\'' | ||||
|                 ); | ||||
|             } | ||||
|             $matches[1] = trim( $matches[1] ); | ||||
|             $columns[$matches[1]] = NULL; | ||||
|             if ( isset( $matches[2] ) ) { | ||||
|                 $columns[$matches[1]] = (int)$matches[2]; | ||||
|             } | ||||
|         } | ||||
|         return $columns; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _check_delta method | ||||
|      * | ||||
|      * Given parsed schema definitions (in {@see self::$_schema_delta} map) this | ||||
|      * method performs checks, to ensure that table exists, columns are of | ||||
|      * expected type, and indexes match their definition in original query. | ||||
|      * | ||||
|      * @return bool Success | ||||
|      * | ||||
|      * @throws Ai1ec_Database_Error In case of any error | ||||
|      */ | ||||
|     protected function _check_delta() { | ||||
|         if ( empty( $this->_schema_delta ) ) { | ||||
|             return true; | ||||
|         } | ||||
|         foreach ( $this->_schema_delta as $table => $description ) { | ||||
|  | ||||
|             $columns = $this->_db->get_results( 'SHOW FULL COLUMNS FROM ' . $table ); | ||||
|             if ( empty( $columns ) ) { | ||||
|                 throw new Ai1ec_Database_Error( | ||||
|                     'Required table `' . $table . '` was not created' | ||||
|                 ); | ||||
|             } | ||||
|             $db_column_names = array(); | ||||
|             foreach ( $columns as $column ) { | ||||
|                 if ( ! isset( $description['columns'][$column->Field] ) ) { | ||||
|                     if ( $this->_db->query( | ||||
|                         'ALTER TABLE `' . $table . | ||||
|                         '` DROP COLUMN `' . $column->Field . '`' | ||||
|                      ) ) { | ||||
|                         continue; | ||||
|                     } | ||||
|                     continue; // ignore so far | ||||
|                     //throw new Ai1ec_Database_Error( | ||||
|                     //    'Unknown column `' . $column->Field . | ||||
|                     //    '` is present in table `' . $table . '`' | ||||
|                     //); | ||||
|                 } | ||||
|                 $db_column_names[$column->Field] = $column->Field; | ||||
|                 $type_db = $column->Type; | ||||
|                 $collation = ''; | ||||
|                 if ( $column->Collation ) { | ||||
|                     $collation = ' CHARACTER SET ' . | ||||
|                         substr( | ||||
|                             $column->Collation, | ||||
|                             0, | ||||
|                             strpos( $column->Collation, '_' ) | ||||
|                         ) . ' COLLATE ' . $column->Collation; | ||||
|                 } | ||||
|                 $type_req = $description['columns'][$column->Field] | ||||
|                     ['content']['type']; | ||||
|                 if ( | ||||
|                     false !== stripos( | ||||
|                         $type_req, | ||||
|                         ' COLLATE ' | ||||
|                     ) | ||||
|                 ) { | ||||
|                     // suspend collation checking | ||||
|                     //$type_db .= $collation; | ||||
|                     $type_req = preg_replace( | ||||
|                         '#^ | ||||
|                             (.+) | ||||
|                             \s+CHARACTER\s+SET\s+[a-z0-9_]+ | ||||
|                             \s+COLLATE\s+[a-z0-9_]+ | ||||
|                             (.+)?\s* | ||||
|                         $#six', | ||||
|                         '$1$2', | ||||
|                         $type_req | ||||
|                     ); | ||||
|                 } | ||||
|                 $type_db  = strtolower( | ||||
|                     preg_replace( '#\s+#', '', $type_db ) | ||||
|                 ); | ||||
|                 $type_req = strtolower( | ||||
|                     preg_replace( '#\s+#', '', $type_req ) | ||||
|                 ); | ||||
|                 if ( 0 !== strcmp( $type_db, $type_req ) ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'Field `' . $table . '`.`' . $column->Field . | ||||
|                         '` is of incompatible type' | ||||
|                     ); | ||||
|                 } | ||||
|                 if ( | ||||
|                     'YES' === $column->Null && | ||||
|                     false === $description['columns'][$column->Field] | ||||
|                         ['content']['null'] || | ||||
|                     'NO' === $column->Null && | ||||
|                     true === $description['columns'][$column->Field] | ||||
|                         ['content']['null'] | ||||
|                 ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'Field `' . $table . '`.`' . $column->Field . | ||||
|                         '` NULLability is flipped' | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|             if ( | ||||
|                 $missing = array_diff( | ||||
|                     array_keys( $description['columns'] ), | ||||
|                     $db_column_names | ||||
|                 ) | ||||
|             ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'In table `' . $table . '` fields are missing: ' . | ||||
|                         implode( ', ', $missing ) | ||||
|                     ); | ||||
|             } | ||||
|  | ||||
|             $indexes = $this->get_indices( $table ); | ||||
|  | ||||
|             foreach ( $indexes as $name => $definition ) { | ||||
|                 if ( ! isset( $description['indexes'][$name] ) ) { | ||||
|                     continue; // ignore so far | ||||
|                     //throw new Ai1ec_Database_Error( | ||||
|                     //    'Unknown index `' . $name . | ||||
|                     //    '` is defined for table `' . $table . '`' | ||||
|                     //); | ||||
|                 } | ||||
|                 if ( | ||||
|                     $missed = array_diff_assoc( | ||||
|                         $description['indexes'][$name]['content'], | ||||
|                         $definition['columns'] | ||||
|                     ) | ||||
|                 ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'Index `' . $name . | ||||
|                         '` definition for table `' . $table . '` has invalid ' . | ||||
|                         ' fields: ' . implode( ', ', array_keys( $missed ) ) | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             if ( | ||||
|                 $missing = array_diff( | ||||
|                     array_keys( $description['indexes'] ), | ||||
|                     array_keys( $indexes ) | ||||
|                 ) | ||||
|             ) { | ||||
|                     throw new Ai1ec_Database_Error( | ||||
|                         'In table `' . $table . '` indexes are missing: ' . | ||||
|                         implode( ', ', $missing ) | ||||
|                     ); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _is_not_empty_line method | ||||
|      * | ||||
|      * Helper method, to check that any given line is not empty. | ||||
|      * Aids array_filter in detecting empty SQL query lines. | ||||
|      * | ||||
|      * @param string $line Single line of DB query statement | ||||
|      * | ||||
|      * @return bool True if line is not empty, false otherwise | ||||
|      */ | ||||
|     protected function _is_not_empty_line( $line ) { | ||||
|         $line = trim( $line ); | ||||
|         return ! empty( $line ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,62 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Library function for massive time conversion operations. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Date | ||||
|  */ | ||||
| class Ai1ec_Date_Converter { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object Instance of objects registry. | ||||
|      */ | ||||
|     protected $_registry = null; | ||||
|  | ||||
|     /** | ||||
|      * Get reference of object registry. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry Injected objects registry. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         $this->_registry = $registry; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Change timezone of times provided. | ||||
|      * | ||||
|      * @param array  $input     List of time entries to convert. | ||||
|      * @param string $source_tz Timezone to convert from. | ||||
|      * @param string $target_tz Timezone to convert to. | ||||
|      * @param string $format    Format of target time entries. | ||||
|      * | ||||
|      * @return array List of converted times. | ||||
|      */ | ||||
|     public function change_timezone( | ||||
|         array $input, | ||||
|         $source_tz, | ||||
|         $target_tz = 'UTC', | ||||
|         $format    = 'U' | ||||
|     ) { | ||||
|         $output = array(); | ||||
|         foreach ( $input as $time ) { | ||||
|             try { | ||||
|                 $time_object = $this->_registry->get( | ||||
|                     'date.time', | ||||
|                     $input, | ||||
|                     $source_tz | ||||
|                 ); | ||||
|                 $output[] = $time_object->format( $format, $target_tz ); | ||||
|                 unset( $time_object ); | ||||
|             } catch ( Ai1ec_Date_Exception $exception ) { | ||||
|                 // ignore | ||||
|             } | ||||
|         } | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,53 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Wrapper for `DateTimeZone` to extend it with convenient methods | ||||
|  * | ||||
|  * @author     Justas Butkus <justas@butkus.lt> | ||||
|  * @since      2013.03.06 | ||||
|  * | ||||
|  * @package    AllInOneCalendar | ||||
|  * @subpackage AllInOneCalendar.Utility.Time | ||||
|  */ | ||||
| class Ai1ec_Date_Date_Time_Zone extends DateTimeZone { | ||||
|  | ||||
|     /** | ||||
|      * Map of transitions details for given timestamp | ||||
|      * {@see DateTimeZone::getTransitions()} for representation of details. | ||||
|      * Return a map of prev(ious), curr(ent) and next transitions for | ||||
|      * a given timestamp. | ||||
|      * | ||||
|      * @NOTICE: if we start accepting PHP 5.3 - update getTransitions | ||||
|      * usage, to add offsets. | ||||
|      * | ||||
|      * @param int $timestamp UNIX timestamp (UTC0) for which to find transitions | ||||
|      * | ||||
|      * @return array Map of prev|curr|next transitions | ||||
|      */ | ||||
|     public function getDetailedTransitions( $timestamp ) { | ||||
|         $transition_list = $this->getTransitions(); | ||||
|         $output          = array( | ||||
|             'prev' => NULL, | ||||
|             'curr' => NULL, | ||||
|             'next' => NULL, | ||||
|         ); | ||||
|         $previous = $current = NULL; | ||||
|         foreach ( $transition_list as $transition ) { | ||||
|             if ( | ||||
|                 NULL !== $previous && | ||||
|                 $timestamp >= $current['ts'] && | ||||
|                 $timestamp < $transition['ts'] | ||||
|             ) { | ||||
|                 $output['prev'] = $previous; | ||||
|                 $output['curr'] = $current; | ||||
|                 $output['next'] = $transition; | ||||
|                 break; | ||||
|             } | ||||
|             $previous = $current; | ||||
|             $current        = $transition; | ||||
|         } | ||||
|         unset( $previous, $current, $transition_list, $transition ); | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Base exception for all date/time operation failures. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Date.exception | ||||
|  */ | ||||
| class Ai1ec_Date_Exception extends Ai1ec_Exception { | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Exception to be thrown when timezone operation fails. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Date | ||||
|  */ | ||||
| class Ai1ec_Date_Timezone_Exception extends Ai1ec_Date_Exception { | ||||
| } | ||||
| @@ -0,0 +1,42 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Legacy Time utility. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Date | ||||
|  */ | ||||
| class Ai1ec_Time_Utility { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object | ||||
|      */ | ||||
|     static protected $_registry; | ||||
|  | ||||
|     /** | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      */ | ||||
|     static public function set_registry( Ai1ec_Registry_Object $registry ) { | ||||
|         self::$_registry = $registry; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Legacy function needed for theme compatibility | ||||
|      * | ||||
|      * @param string $format | ||||
|      * @param int    $timestamp | ||||
|      * @param bool   $is_gmt | ||||
|      */ | ||||
|     static public function date_i18n( | ||||
|         $format, | ||||
|         $timestamp = false, | ||||
|         $is_gmt    = true | ||||
|     ) { | ||||
|         $timezone = ( $is_gmt ) ? 'UTC' : 'sys.default'; | ||||
|         return self::$_registry->get( 'date.time', $timestamp, $timezone ) | ||||
|             ->format_i18n( $format ); | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										279
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/date/system.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/date/system.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Wrap library calls to date subsystem. | ||||
|  * | ||||
|  * Meant to increase performance and work around known bugs in environment. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Date | ||||
|  */ | ||||
|  | ||||
| class Ai1ec_Date_System extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var array List of local time (key '0') and GMT time (key '1'). | ||||
|      */ | ||||
|     protected $_current_time = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Cache_Memory | ||||
|      */ | ||||
|     protected $_gmtdates; | ||||
|  | ||||
|     /** | ||||
|      * Initiate current time list. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $gmt_time = ( version_compare( PHP_VERSION, '5.1.0' ) >= 0 ) | ||||
|             ? time() | ||||
|             : gmmktime(); | ||||
|         $requestTime = isset( $_SERVER['REQUEST_TIME'] ) ? (int)$_SERVER['REQUEST_TIME'] : time(); | ||||
|         $this->_current_time     = array( | ||||
|             $requestTime, | ||||
|             $gmt_time, | ||||
|         ); | ||||
|         $this->_gmtdates = $registry->get( 'cache.memory' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get current time UNIX timestamp. | ||||
|      * | ||||
|      * Uses in-memory value, instead of re-calling `time()` / `gmmktime()`. | ||||
|      * | ||||
|      * @param bool $is_gmt Set to true to get GMT timestamp. | ||||
|      * | ||||
|      * @return int Current time UNIX timestamp | ||||
|      */ | ||||
|     public function current_time( $is_gmt = false ) { | ||||
|         return $this->_current_time[(int)( (bool)$is_gmt )]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the associative array of date patterns supported by the plugin. | ||||
|      * | ||||
|      * Currently the formats are: | ||||
|      *   array( | ||||
|      *     'def' => 'd/m/yyyy', | ||||
|      *     'us'  => 'm/d/yyyy', | ||||
|      *     'iso' => 'yyyy-m-d', | ||||
|      *     'dot' => 'm.d.yyyy', | ||||
|      *   ); | ||||
|      * | ||||
|      * 'd' or 'dd' represent the day, 'm' or 'mm' represent the month, and 'yy' | ||||
|      * or 'yyyy' represent the year. | ||||
|      * | ||||
|      * @return array List of supported date patterns. | ||||
|      */ | ||||
|     public function get_date_patterns() { | ||||
|         return array( | ||||
|             'def' => 'd/m/yyyy', | ||||
|             'us'  => 'm/d/yyyy', | ||||
|             'iso' => 'yyyy-m-d', | ||||
|             'dot' => 'm.d.yyyy', | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get acceptable date format. | ||||
|      * | ||||
|      * Returns the date pattern (in the form 'd-m-yyyy', for example) associated | ||||
|      * with the provided key, used by plugin settings. Simply a static map as | ||||
|      * follows: | ||||
|      * | ||||
|      * @param string $key Key for the date format. | ||||
|      * | ||||
|      * @return string Associated date format pattern. | ||||
|      */ | ||||
|     public function get_date_pattern_by_key( $key = 'def' ) { | ||||
|         $patterns = $this->get_date_patterns(); | ||||
|         if ( ! isset( $patterns[$key] ) ) { | ||||
|             return (string)current( $patterns ); | ||||
|         } | ||||
|         return $patterns[$key]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Format timestamp into URL safe, user selected representation. | ||||
|      * | ||||
|      * Returns a formatted date given a timestamp, based on the given date | ||||
|      * format, with any '/' characters replaced with URL-friendly '-' | ||||
|      * characters. | ||||
|      * | ||||
|      * @see Ai1ec_Date_System::get_date_patterns() for supported date formats. | ||||
|      * | ||||
|      * @param int    $timestamp UNIX timestamp representing a date. | ||||
|      * @param string $pattern   Key of date pattern (@see | ||||
|      *                          self::get_date_format_patter()) to | ||||
|      *                          format date with | ||||
|      * | ||||
|      * @return string Formatted date string. | ||||
|      */ | ||||
|     public function format_date_for_url( $timestamp, $pattern = 'def' ) { | ||||
|         $date = $this->format_date( $timestamp, $pattern ); | ||||
|         $date = str_replace( '/', '-', $date ); | ||||
|         return $date; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Similar to {@see format_date_for_url} just using new DateTime interface. | ||||
|      * | ||||
|      * @param Ai1ec_Date_Time $datetime Instance of datetime to format. | ||||
|      * @param string          $pattern  Target format to use. | ||||
|      * | ||||
|      * @return string Formatted datetime string. | ||||
|      */ | ||||
|     public function format_datetime_for_url( | ||||
|         Ai1ec_Date_Time $datetime, | ||||
|         $pattern = 'def' | ||||
|     ) { | ||||
|         $date = $datetime->format( $this->get_date_format_patter( $pattern ) ); | ||||
|         return str_replace( '/', '-', $date ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the date formatted with new pattern from a given date and old pattern. | ||||
|      * | ||||
|      * @see  self::get_date_patterns() for supported date formats. | ||||
|      * | ||||
|      * @param  string $date          Formatted date string | ||||
|      * @param  string $old_pattern   Key of old date pattern (@see | ||||
|      *                               self::get_date_format_patter()) | ||||
|      * @param  string $new_pattern   Key of new date pattern (@see | ||||
|      *                               self::get_date_format_patter()) | ||||
|      * @return string                Formatted date string with new pattern | ||||
|      */ | ||||
|     public function convert_date_format( $date, $old_pattern, $new_pattern ) { | ||||
|         // Convert old date to timestamp | ||||
|         $timeArray = date_parse_from_format( $this->get_date_format_patter( $old_pattern ), $date ); | ||||
|  | ||||
|         $timestamp = mktime( | ||||
|             $timeArray['hour'], $timeArray['minute'], $timeArray['second'], | ||||
|             $timeArray['month'], $timeArray['day'], $timeArray['year'] | ||||
|         ); | ||||
|  | ||||
|         // Convert to new date pattern | ||||
|         return $this->format_date( $timestamp, $new_pattern ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns a formatted date given a timestamp, based on the given date format. | ||||
|      * | ||||
|      * @see  self::get_date_patterns() for supported date formats. | ||||
|      * | ||||
|      * @param  int $timestamp    UNIX timestamp representing a date (in GMT) | ||||
|      * @param  string $pattern   Key of date pattern (@see | ||||
|      *                           self::get_date_format_patter()) to | ||||
|      *                           format date with | ||||
|      * @return string            Formatted date string | ||||
|      */ | ||||
|     public function format_date( $timestamp, $pattern = 'def' ) { | ||||
|         return gmdate( $this->get_date_format_patter( $pattern ), $timestamp ); | ||||
|     } | ||||
|  | ||||
|     public function get_date_format_patter( $requested ) { | ||||
|         $pattern = $this->get_date_pattern_by_key( $requested ); | ||||
|         $pattern = str_replace( | ||||
|             array( 'dd', 'd', 'mm', 'm', 'yyyy', 'yy' ), | ||||
|             array( 'd',  'j', 'm',  'n', 'Y',    'y' ), | ||||
|             $pattern | ||||
|         ); | ||||
|         return $pattern; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns human-readable version of the GMT offset. | ||||
|      * | ||||
|      * @param string $timezone_name Olsen Timezone name [optional=null] | ||||
|      * | ||||
|      * @return string GMT offset expression | ||||
|      */ | ||||
|     public function get_gmt_offset_expr( $timezone_name = null ) { | ||||
|         $timezone = $this->get_gmt_offset( $timezone_name ); | ||||
|         $offset_h = (int)( $timezone / 60 ); | ||||
|         $offset_m = absint( $timezone - $offset_h * 60 ); | ||||
|         $timezone = sprintf( | ||||
|             Ai1ec_I18n::__( 'GMT%+d:%02d' ), | ||||
|             $offset_h, | ||||
|             $offset_m | ||||
|         ); | ||||
|  | ||||
|         return $timezone; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get current GMT offset in seconds. | ||||
|      * | ||||
|      * @param string $timezone_name Olsen Timezone name [optional=null] | ||||
|      * | ||||
|      * @return int Offset from GMT in seconds. | ||||
|      */ | ||||
|     public function get_gmt_offset( $timezone_name = null ) { | ||||
|         if ( null === $timezone_name ) { | ||||
|             $timezone_name = 'sys.default'; | ||||
|         } | ||||
|         $current = $this->_registry->get( | ||||
|             'date.time', | ||||
|             'now', | ||||
|             $timezone_name | ||||
|         ); | ||||
|         return $current->get_gmt_offset(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * gmgetdate method | ||||
|      * | ||||
|      * Get date/time information in GMT | ||||
|      * | ||||
|      * @param int $timestamp Timestamp at which information shall be evaluated | ||||
|      * | ||||
|      * @return array Associative array of information related to the timestamp | ||||
|      */ | ||||
|     public function gmgetdate( $timestamp = NULL ) { | ||||
|         if ( NULL === $timestamp ) { | ||||
|             $timestamp = isset( $_SERVER['REQUEST_TIME'] ) ? (int)$_SERVER['REQUEST_TIME'] : time(); | ||||
|         } | ||||
|         if ( NULL === ( $date = $this->_gmtdates->get( $timestamp ) ) ) { | ||||
|             $particles = explode( | ||||
|                 ',', | ||||
|                 gmdate( 's,i,G,j,w,n,Y,z,l,F,U', $timestamp ) | ||||
|             ); | ||||
|             $date      = array_combine( | ||||
|                 array( | ||||
|                     'seconds', | ||||
|                     'minutes', | ||||
|                     'hours', | ||||
|                     'mday', | ||||
|                     'wday', | ||||
|                     'mon', | ||||
|                     'year', | ||||
|                     'yday', | ||||
|                     'weekday', | ||||
|                     'month', | ||||
|                     0 | ||||
|                 ), | ||||
|                 $particles | ||||
|             ); | ||||
|             $this->_gmtdates->set( $timestamp, $date ); | ||||
|         } | ||||
|         return $date; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns current rounded time as unix integer. | ||||
|      * | ||||
|      * @param int $shift Shift value. | ||||
|      * | ||||
|      * @return int Unix timestamp. | ||||
|      */ | ||||
|     public function get_current_rounded_time( $shift = 11 ) { | ||||
|         return $this->current_time() >> $shift << $shift; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,216 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Time and date internationalization management library | ||||
|  * | ||||
|  * @author     Timely Network Inc | ||||
|  * @since      2012.10.09 | ||||
|  * | ||||
|  * @package    AllInOneCalendar | ||||
|  * @subpackage AllInOneCalendar.Lib.Utility | ||||
|  */ | ||||
| class Ai1ec_Time_I18n_Utility extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var char Separator to wrap unique keys and avoid collisions | ||||
|      *           EOT is used instead of NUL, as NUL is used by `date()` | ||||
|      *           functions family as guard and causes memory leaks. | ||||
|      */ | ||||
|     protected $_separator = "\004"; | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of keys, used by date methods | ||||
|      */ | ||||
|     protected $_keys      = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of keys for substition | ||||
|      */ | ||||
|     protected $_skeys     = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var string Format to use when calling `date_i18n()` | ||||
|      */ | ||||
|     protected $_format    = NULL; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Memory_Utility Parsed time entries | ||||
|      */ | ||||
|     protected $_memory    = NULL; | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Memory_Utility Parsed format entries | ||||
|      */ | ||||
|     protected $_transf    = NULL; | ||||
|  | ||||
|     /** | ||||
|      * Constructor | ||||
|      * | ||||
|      * Initialize internal memory objects and date keys. | ||||
|      * | ||||
|      * @param Ai1ec_Memory_Utility $memory Optionally inject memory to use | ||||
|      * | ||||
|      * @return void Constructor does not return | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         Ai1ec_Cache_Memory $memory = null | ||||
|     ) { | ||||
|         parent::__construct( $registry ); | ||||
|         if ( NULL === $memory ) { | ||||
|             $memory = $this->_registry->get( 'cache.memory', 120 ); // 30 * 4 | ||||
|         } | ||||
|         $this->_memory = $memory; | ||||
|         $this->_transf = $this->_registry->get( 'cache.memory' ); | ||||
|         $this->_keys   = $this->_initialize_keys(); | ||||
|         $this->_skeys  = $this->_initialize_keys( | ||||
|             $this->_separator, | ||||
|             $this->_separator | ||||
|         ); | ||||
|         $this->_format = implode( $this->_separator, $this->_keys ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * format method | ||||
|      * | ||||
|      * Convenient wrapper for `date_i18n()`, which caches both faster format | ||||
|      * version and response for {$timestamp} and {$is_gmt} combination. | ||||
|      * | ||||
|      * @param string $format    Format string to output timestamp in | ||||
|      * @param int    $timestamp UNIX timestamp to output in given format | ||||
|      * @param bool   $is_gmt    Set to true, to treat {$timestamp} as GMT | ||||
|      * | ||||
|      * @return string Formatted date-time entry | ||||
|      */ | ||||
|     public function format( $format, $timestamp = false, $is_gmt = false ) { | ||||
|         $time_elements = $this->parse( $timestamp, $is_gmt ); | ||||
|         $local_format  = $this->_safe_format( $format ); | ||||
|         return str_replace( $this->_skeys, $time_elements, $local_format ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * parse method | ||||
|      * | ||||
|      * Parse given timestamp into I18n date/time values map. | ||||
|      * | ||||
|      * @param int  $timestamp Timestamp to parse | ||||
|      * @param bool $is_gmt    Set to true, to treat value as present in GMT | ||||
|      * | ||||
|      * @return array Map of date format keys and corresponding time values | ||||
|      */ | ||||
|     public function parse( $timestamp = false, $is_gmt = false ) { | ||||
|         $timestamp = (int)$timestamp; | ||||
|         if ( $timestamp <= 0 ) { | ||||
|             $timestamp = $this->_registry->get( 'date.system' )->current_time(); | ||||
|         } | ||||
|         $cache_key = $timestamp . "\0" . $is_gmt; | ||||
|         if ( NULL === ( $record = $this->_memory->get( $cache_key ) ) ) { | ||||
|             $record = array_combine( | ||||
|                 $this->_keys, | ||||
|                 explode( | ||||
|                     $this->_separator, | ||||
|                     date_i18n( $this->_format, $timestamp, $is_gmt ) | ||||
|                 ) | ||||
|             ); | ||||
|             $this->_memory->set( $cache_key, $record ); | ||||
|         } | ||||
|         return $record; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _safe_format method | ||||
|      * | ||||
|      * Prepare safe format value, to use in substitutions. | ||||
|      * In prepared string special values are wrapped by {$_separator} to allow | ||||
|      * fast replacement methods, using binary search. | ||||
|      * | ||||
|      * @param string $format Given format to polish | ||||
|      * | ||||
|      * @return string Modified format, with special keys wrapped in bin fields | ||||
|      */ | ||||
|     protected function _safe_format( $format ) { | ||||
|         if ( NULL === ( $safe = $this->_transf->get( $format ) ) ) { | ||||
|             $safe      = ''; | ||||
|             $state     = 0; | ||||
|             $separator = $this->_separator; | ||||
|             $length    = strlen( $format ); | ||||
|             for ( $index = 0; $index < $length; $index++ ) { | ||||
|                 if ( $state > 0 ) { | ||||
|                     --$state; | ||||
|                 } | ||||
|                 $current = $format{$index}; | ||||
|                 if ( 0 === $state ) { | ||||
|                     if ( '\\' === $current ) { | ||||
|                         $state = 2; | ||||
|                     } elseif ( isset( $this->_keys[$current] ) ) { | ||||
|                         $current = $separator . $current . $separator; | ||||
|                     } | ||||
|                 } | ||||
|                 if ( 2 !== $state ) { | ||||
|                     $safe .= $current; | ||||
|                 } | ||||
|             } | ||||
|             $this->_transf->set( $format, $safe ); | ||||
|         } | ||||
|         return $safe; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _initialize_keys method | ||||
|      * | ||||
|      * Prepare list of keys, used by date functions. | ||||
|      * Optionally wrap values (keys are the same, always). | ||||
|      * | ||||
|      * @param string $prepend Prefix to date key | ||||
|      * @param string $append  Suffix to date key | ||||
|      * | ||||
|      * @return array Map of date keys | ||||
|      */ | ||||
|     protected function _initialize_keys( $prepend = '', $append = '' ) { | ||||
|         $keys = array( | ||||
|             'd', | ||||
|             'D', | ||||
|             'j', | ||||
|             'l', | ||||
|             'N', | ||||
|             'S', | ||||
|             'w', | ||||
|             'z', | ||||
|             'W', | ||||
|             'F', | ||||
|             'm', | ||||
|             'M', | ||||
|             'n', | ||||
|             't', | ||||
|             'L', | ||||
|             'o', | ||||
|             'Y', | ||||
|             'y', | ||||
|             'a', | ||||
|             'A', | ||||
|             'B', | ||||
|             'g', | ||||
|             'G', | ||||
|             'h', | ||||
|             'H', | ||||
|             'i', | ||||
|             's', | ||||
|             'u', | ||||
|             'e', | ||||
|             'I', | ||||
|             'O', | ||||
|             'P', | ||||
|             'T', | ||||
|             'Z', | ||||
|             'c', | ||||
|             'r', | ||||
|             'U', | ||||
|         ); | ||||
|         $map = array(); | ||||
|         foreach ( $keys as $key ) { | ||||
|             $map[$key] = $prepend . $key . $append; | ||||
|         } | ||||
|         return $map; | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										423
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/date/time.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										423
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/date/time.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,423 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Time entity. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Date | ||||
|  */ | ||||
| class Ai1ec_Date_Time { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object Instance of objects registry. | ||||
|      */ | ||||
|     protected $_registry  = null; | ||||
|  | ||||
|     /** | ||||
|      * @var DateTime Instance of date time object used to perform manipulations. | ||||
|      */ | ||||
|     protected $_date_time = null; | ||||
|  | ||||
|     /** | ||||
|      * @var string Olsen name of preferred timezone to use if none is requested. | ||||
|      */ | ||||
|     protected $_preferred_timezone = null; | ||||
|  | ||||
|     /** | ||||
|      * @var bool Set to true when `no value` is set. | ||||
|      */ | ||||
|     protected $_is_empty = false; | ||||
|  | ||||
|     /** | ||||
|      * Initialize local date entity. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry Objects registry instance. | ||||
|      * @param string                $time     For details {@see self::format}. | ||||
|      * @param string                $timezone For details {@see self::format}. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         $time     = 'now', | ||||
|         $timezone = 'UTC' | ||||
|     ) { | ||||
|         $this->_registry = $registry; | ||||
|         $this->set_date_time( $time, $timezone ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Since clone is shallow, we need to clone the DateTime object | ||||
|      */ | ||||
|     public function __clone() { | ||||
|         $this->_date_time = clone $this->_date_time; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return formatted date in desired timezone. | ||||
|      * | ||||
|      * NOTICE: consider optimizing by storing multiple copies of `DateTime` for | ||||
|      * each requested timezone, or some of them, as of now timezone is changed | ||||
|      * back and forth every time when formatting is called for. | ||||
|      * | ||||
|      * @param string $format   Desired format as accepted by {@see date}. | ||||
|      * @param string $timezone Valid timezone identifier. Defaults to current. | ||||
|      * | ||||
|      * @return string Formatted date time. | ||||
|      * | ||||
|      * @throws Ai1ec_Date_Timezone_Exception If timezone is not recognized. | ||||
|      */ | ||||
|     public function format( $format = 'U', $timezone = null ) { | ||||
|         if ( $this->_is_empty ) { | ||||
|             return null; | ||||
|         } | ||||
|         if ( 'U' === $format ) { // performance cut | ||||
|             return $this->_date_time->format( 'U' ); | ||||
|         } | ||||
|         $timezone  = $this->get_default_format_timezone( $timezone ); | ||||
|         $last_tz   = $this->get_timezone(); | ||||
|         $this->set_timezone( $timezone ); | ||||
|         $formatted = $this->_date_time->format( $format ); | ||||
|         $this->set_timezone( $last_tz ); | ||||
|         return $formatted; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Format date time to i18n representation. | ||||
|      * | ||||
|      * @param string $format   Target I18n format. | ||||
|      * @param string $timezone Valid timezone identifier. Defaults to current. | ||||
|      * | ||||
|      * @return string Formatted time. | ||||
|      */ | ||||
|     public function format_i18n( $format, $timezone = null ) { | ||||
|         $parser    = $this->_registry->get( 'parser.date' ); | ||||
|         $parsed    = $parser->get_format( $format ); | ||||
|         $inflected = $this->format( $parsed, $timezone ); | ||||
|         $formatted = $parser->squeeze( $inflected ); | ||||
|         return $formatted; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Commodity method to format to UTC. | ||||
|      * | ||||
|      * @param string $format Target format, defaults to UNIX timestamp. | ||||
|      * | ||||
|      * @return string Formatted datetime string. | ||||
|      */ | ||||
|     public function format_to_gmt( $format = 'U' ) { | ||||
|         return $this->format( $format, 'UTC' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create JavaScript ready date/time information string. | ||||
|      * | ||||
|      * @param bool $event_timezone Set to true to format in event timezone. | ||||
|      * | ||||
|      * @return string JavaScript date/time string. | ||||
|      */ | ||||
|     public function format_to_javascript( $event_timezone = false ) { | ||||
|         $event_timezone = ( $event_timezone ) | ||||
|             ? $this->get_timezone() | ||||
|             : null; | ||||
|         return $this->format( 'Y-m-d\TH:i:s', $event_timezone ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get timezone to use when format doesn't have one. | ||||
|      * | ||||
|      * Precedence: | ||||
|      *     1. Timezone supplied for formatting; | ||||
|      *     2. Objects preferred timezone; | ||||
|      *     3. Default systems timezone. | ||||
|      * | ||||
|      * @var string $timezone Requested formatting timezone. | ||||
|      * | ||||
|      * @return string Olsen timezone name to use. | ||||
|      */ | ||||
|     public function get_default_format_timezone( $timezone = null ) { | ||||
|         if ( null !== $timezone ) { | ||||
|             return $timezone; | ||||
|         } | ||||
|         if ( null !== $this->_preferred_timezone ) { | ||||
|             return $this->_preferred_timezone; | ||||
|         } | ||||
|         return $this->_registry->get( 'date.timezone' ) | ||||
|             ->get_default_timezone(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Offset from GMT in minutes. | ||||
|      * | ||||
|      * @return int Signed integer - offset. | ||||
|      */ | ||||
|     public function get_gmt_offset() { | ||||
|         return $this->_date_time->getOffset() / 60; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns timezone offset as human readable GMT string. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function get_gmt_offset_as_text() { | ||||
|         $offset        = $this->_date_time->getOffset(); | ||||
|         $offsetHours   = $offset / 3600; | ||||
|         $offset        = $offset % 3600; | ||||
|         $offsetMinutes = abs( $offset ) / 60; | ||||
|         return sprintf( '(GMT%+03d:%02d)', $offsetHours, $offsetMinutes ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set preferred timezone to use when format is called without any. | ||||
|      * | ||||
|      * @param DateTimeZone $timezone Preferred timezone instance. | ||||
|      * | ||||
|      * @return Ai1ec_Date_Time Instance of self for chaining. | ||||
|      */ | ||||
|     public function set_preferred_timezone( $timezone ) { | ||||
|         if ( $timezone instanceof DateTimeZone ) { | ||||
|             $timezone = $timezone->getName(); | ||||
|         } | ||||
|         $this->_preferred_timezone = (string)$timezone; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Change timezone of stored entity. | ||||
|      * | ||||
|      * @param string $timezone Valid timezone identifier. | ||||
|      * | ||||
|      * @return Ai1ec_Date Instance of self for chaining. | ||||
|      * | ||||
|      * @throws Ai1ec_Date_Timezone_Exception If timezone is not recognized. | ||||
|      */ | ||||
|     public function set_timezone( $timezone = 'UTC' ) { | ||||
|         $date_time_tz = ( $timezone instanceof DateTimeZone ) | ||||
|             ? $timezone | ||||
|             : $this->_registry->get( 'date.timezone' )->get( $timezone ); | ||||
|         $this->_date_time->setTimezone( $date_time_tz ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get timezone associated with current object. | ||||
|      * | ||||
|      * @return string|null Valid PHP timezone string or null on error. | ||||
|      */ | ||||
|     public function get_timezone() { | ||||
|         $timezone = $this->_date_time->getTimezone(); | ||||
|         if ( false === $timezone ) { | ||||
|             return null; | ||||
|         } | ||||
|         return $timezone->getName(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get difference in seconds between to dates. | ||||
|      * | ||||
|      * In PHP versions post 5.3.0 the {@see DateTimeImmutable::diff()} is | ||||
|      * used. In earlier versions the difference between two timestamps is | ||||
|      * being checked. | ||||
|      * | ||||
|      * @param Ai1ec_Date_Time $comparable Other date time entity. | ||||
|      * | ||||
|      * @return int Number of seconds between two dates. | ||||
|      */ | ||||
|     public function diff_sec( Ai1ec_Date_Time $comparable, $timezone = null ) { | ||||
|         // NOTICE: `$this->_is_empty` is not touched here intentionally | ||||
|         // because there is no meaningful difference to `empty` value. | ||||
|         // It is left to be handled at upper level - you are not likely to | ||||
|         // reach situation where you compare something against empty value. | ||||
|         if ( version_compare( PHP_VERSION, '5.3.0' ) < 0 ) { | ||||
|             $difference = $this->_date_time->format( 'U' ) - | ||||
|                 $comparable->_date_time->format( 'U' ); | ||||
|             if ( $difference < 0 ) { | ||||
|                 $difference *= -1; | ||||
|             } | ||||
|             return $difference; | ||||
|         } | ||||
|         $difference = $this->_date_time->diff( $comparable->_date_time, true ); | ||||
|         return ( | ||||
|             $difference->days * 86400 + | ||||
|             $difference->h    * 3600  + | ||||
|             $difference->i    * 60    + | ||||
|             $difference->s | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adjust only date fragment of entity. | ||||
|      * | ||||
|      * @param int $year  Year of the date. | ||||
|      * @param int $month Month of the date. | ||||
|      * @param int $day   Day of the date. | ||||
|      * | ||||
|      * @return Ai1ec_Date_Time Instance of self for chaining. | ||||
|      */ | ||||
|     public function set_date( $year, $month, $day ) { | ||||
|         $this->_date_time->setDate( $year, $month, $day ); | ||||
|         $this->_is_empty = false; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adjust only time fragment of entity. | ||||
|      * | ||||
|      * @param int $hour   Hour of the time. | ||||
|      * @param int $minute Minute of the time. | ||||
|      * @param int $second Second of the time. | ||||
|      * | ||||
|      * @return Ai1ec_Date_Time Instance of self for chaining. | ||||
|      */ | ||||
|     public function set_time( $hour, $minute = 0, $second = 0 ) { | ||||
|         $this->_date_time->setTime( $hour, $minute, $second ); | ||||
|         $this->_is_empty = false; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adjust day part of date time entity. | ||||
|      * | ||||
|      * @param int $quantifier Day adjustment quantifier. | ||||
|      * | ||||
|      * @return Ai1ec_Date_Time Instance of self for chaining. | ||||
|      */ | ||||
|     public function adjust_day( $quantifier ) { | ||||
|         // NOTICE: `$this->_is_empty` is not touched here, because if you | ||||
|         // start adjusting value it's likely not empty by then. | ||||
|         $this->adjust( $quantifier, 'day' ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adjust day part of date time entity. | ||||
|      * | ||||
|      * @param int $quantifier Day adjustment quantifier. | ||||
|      * | ||||
|      * @return Ai1ec_Date_Time Instance of self for chaining. | ||||
|      */ | ||||
|     public function adjust_month( $quantifier ) { | ||||
|         $this->adjust( $quantifier, 'month' ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Change/initiate stored date time entity. | ||||
|      * | ||||
|      * NOTICE: time specifiers falling in range 0..2048 will be treated | ||||
|      * as a UNIX timestamp, to full format specification, thus ignoring | ||||
|      * any value passed for timezone. | ||||
|      * | ||||
|      * @param string $time     Valid (PHP-parseable) date/time identifier. | ||||
|      * @param string $timezone Valid timezone identifier. | ||||
|      * | ||||
|      * @return Ai1ec_Date Instance of self for chaining. | ||||
|      */ | ||||
|     public function set_date_time( $time = 'now', $timezone = 'UTC' ) { | ||||
|         if ( $time instanceof self ) { | ||||
|             $this->_is_empty           = $time->_is_empty; | ||||
|             $this->_date_time          = clone $time->_date_time; | ||||
|             $this->_preferred_timezone = $time->_preferred_timezone; | ||||
|             if ( 'UTC' !== $timezone && $timezone ) { | ||||
|                 $this->set_timezone( $timezone ); | ||||
|             } | ||||
|             return $this; | ||||
|         } | ||||
|         $this->assert_utc_timezone(); | ||||
|         $date_time_tz = $this->_registry->get( 'date.timezone' ) | ||||
|                 ->get( $timezone ); | ||||
|         $reset_tz     = false; | ||||
|         $this->_is_empty = false; | ||||
|         if ( null === $time ) { | ||||
|             $this->_is_empty = true; | ||||
|             $time            = '@' . ~PHP_INT_MAX; | ||||
|             $reset_tz        = true; | ||||
|         } else if ( $this->is_timestamp( $time ) ) { | ||||
|             $time     = '@' . $time; // treat as UNIX timestamp | ||||
|             $reset_tz = true; // store intended TZ | ||||
|         } | ||||
|         // PHP <= 5.3.5 compatible | ||||
|         $this->_date_time = new DateTime( $time, $date_time_tz ); | ||||
|         if ( $reset_tz ) { | ||||
|             $this->set_timezone( $date_time_tz ); | ||||
|         } | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if value should be treated as a UNIX timestamp. | ||||
|      * | ||||
|      * @param string $time Provided time value. | ||||
|      * | ||||
|      * @return bool True if seems like UNIX timestamp. | ||||
|      */ | ||||
|     public function is_timestamp( $time ) { | ||||
|         // '20001231T001559Z' | ||||
|         if ( isset( $time{8} ) && 'T' === $time{8} ) { | ||||
|             return false; | ||||
|         } | ||||
|         if ( (string)(int)$time !== (string)$time ) { | ||||
|             return false; | ||||
|         } | ||||
|         // 1000..2459 are treated as hours, 2460..9999 - as years | ||||
|         if ( $time > 999 && $time < 2460 ) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Assert that current timezone is UTC. | ||||
|      * | ||||
|      * @return bool Success. | ||||
|      */ | ||||
|     public function assert_utc_timezone() { | ||||
|         $default = (string)date_default_timezone_get(); | ||||
|         $success = true; | ||||
|         if ( 'UTC' !== $default ) { | ||||
|             // issue admin notice | ||||
|             $success = date_default_timezone_set( 'UTC' ); | ||||
|         } | ||||
|         return $success; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Magic method for compatibility. | ||||
|      * | ||||
|      * @return string ISO-8601 formatted date-time. | ||||
|      */ | ||||
|     public function __toString() { | ||||
|         return $this->format( 'c' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Modifies the DateTime object | ||||
|      * | ||||
|      * @param int $quantifieruantifier | ||||
|      * @param string $longname | ||||
|      */ | ||||
|     public function adjust( $quantifier, $longname ) { | ||||
|         $quantifier = (int)$quantifier; | ||||
|         if ( $quantifier > 0 && '+' !== $quantifier{0} ) { | ||||
|             $quantifier = '+' . $quantifier; | ||||
|         } | ||||
|         $modifier = $quantifier . ' ' . $longname; | ||||
|         $this->_date_time->modify( $modifier ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Explicitly check if value (date) is empty. | ||||
|      * | ||||
|      * @return bool Emptiness | ||||
|      */ | ||||
|     public function is_empty() { | ||||
|         return $this->_is_empty; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,617 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Timezones manipulation object. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Date | ||||
|  */ | ||||
| class Ai1ec_Date_Timezone extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Cache_Interface In-memory storage for timezone objects. | ||||
|      */ | ||||
|     protected $_cache           = null; | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of timezone names and their Olson TZ counterparts. | ||||
|      */ | ||||
|     protected $_zones           = array( | ||||
|         '+00:00'                           => 'UTC', | ||||
|         'Z'                                => 'UTC', | ||||
|         'AUS Central Standard Time'        => 'Australia/Darwin', | ||||
|         'AUS Eastern Standard Time'        => 'Australia/Sydney', | ||||
|         'Acre'                             => 'America/Rio_Branco', | ||||
|         'Afghanistan'                      => 'Asia/Kabul', | ||||
|         'Afghanistan Standard Time'        => 'Asia/Kabul', | ||||
|         'Africa_Central'                   => 'Africa/Maputo', | ||||
|         'Africa_Eastern'                   => 'Africa/Nairobi', | ||||
|         'Africa_FarWestern'                => 'Africa/El_Aaiun', | ||||
|         'Africa_Southern'                  => 'Africa/Johannesburg', | ||||
|         'Africa_Western'                   => 'Africa/Lagos', | ||||
|         'Aktyubinsk'                       => 'Asia/Aqtobe', | ||||
|         'Alaska'                           => 'America/Juneau', | ||||
|         'Alaska_Hawaii'                    => 'America/Anchorage', | ||||
|         'Alaskan Standard Time'            => 'America/Anchorage', | ||||
|         'Almaty'                           => 'Asia/Almaty', | ||||
|         'Amazon'                           => 'America/Manaus', | ||||
|         'America_Central'                  => 'America/Chicago', | ||||
|         'America_Eastern'                  => 'America/New_York', | ||||
|         'America_Mountain'                 => 'America/Denver', | ||||
|         'America_Pacific'                  => 'America/Los_Angeles', | ||||
|         'Anadyr'                           => 'Asia/Anadyr', | ||||
|         'Aqtau'                            => 'Asia/Aqtau', | ||||
|         'Aqtobe'                           => 'Asia/Aqtobe', | ||||
|         'Arab Standard Time'               => 'Asia/Riyadh', | ||||
|         'Arabian'                          => 'Asia/Riyadh', | ||||
|         'Arabian Standard Time'            => 'Asia/Dubai', | ||||
|         'Arabic Standard Time'             => 'Asia/Baghdad', | ||||
|         'Argentina'                        => 'America/Buenos_Aires', | ||||
|         'Argentina Standard Time'          => 'America/Buenos_Aires', | ||||
|         'Argentina_Western'                => 'America/Mendoza', | ||||
|         'Armenia'                          => 'Asia/Yerevan', | ||||
|         'Armenian Standard Time'           => 'Asia/Yerevan', | ||||
|         'Ashkhabad'                        => 'Asia/Ashgabat', | ||||
|         'Atlantic'                         => 'America/Halifax', | ||||
|         'Atlantic Standard Time'           => 'America/Halifax', | ||||
|         'Australia_Central'                => 'Australia/Adelaide', | ||||
|         'Australia_CentralWestern'         => 'Australia/Eucla', | ||||
|         'Australia_Eastern'                => 'Australia/Sydney', | ||||
|         'Australia_Western'                => 'Australia/Perth', | ||||
|         'Azerbaijan'                       => 'Asia/Baku', | ||||
|         'Azerbaijan Standard Time'         => 'Asia/Baku', | ||||
|         'Azores'                           => 'Atlantic/Azores', | ||||
|         'Azores Standard Time'             => 'Atlantic/Azores', | ||||
|         'Baku'                             => 'Asia/Baku', | ||||
|         'Bangladesh'                       => 'Asia/Dhaka', | ||||
|         'Bering'                           => 'America/Adak', | ||||
|         'Bhutan'                           => 'Asia/Thimphu', | ||||
|         'Bolivia'                          => 'America/La_Paz', | ||||
|         'Borneo'                           => 'Asia/Kuching', | ||||
|         'Brasilia'                         => 'America/Sao_Paulo', | ||||
|         'British'                          => 'Europe/London', | ||||
|         'Brunei'                           => 'Asia/Brunei', | ||||
|         'Canada Central Standard Time'     => 'America/Regina', | ||||
|         'Cape Verde Standard Time'         => 'Atlantic/Cape_Verde', | ||||
|         'Cape_Verde'                       => 'Atlantic/Cape_Verde', | ||||
|         'Caucasus Standard Time'           => 'Asia/Yerevan', | ||||
|         'Cen. Australia Standard Time'     => 'Australia/Adelaide', | ||||
|         'Central America Standard Time'    => 'America/Guatemala', | ||||
|         'Central Asia Standard Time'       => 'Asia/Dhaka', | ||||
|         'Central Brazilian Standard Time'  => 'America/Manaus', | ||||
|         'Central Europe Standard Time'     => 'Europe/Budapest', | ||||
|         'Central European Standard Time'   => 'Europe/Warsaw', | ||||
|         'Central Pacific Standard Time'    => 'Pacific/Guadalcanal', | ||||
|         'Central Standard Time'            => 'America/Chicago', | ||||
|         'Central Standard Time (Mexico)'   => 'America/Mexico_City', | ||||
|         'Chamorro'                         => 'Pacific/Saipan', | ||||
|         'Changbai'                         => 'Asia/Harbin', | ||||
|         'Chatham'                          => 'Pacific/Chatham', | ||||
|         'Chile'                            => 'America/Santiago', | ||||
|         'China'                            => 'Asia/Shanghai', | ||||
|         'China Standard Time'              => 'Asia/Shanghai', | ||||
|         'Choibalsan'                       => 'Asia/Choibalsan', | ||||
|         'Christmas'                        => 'Indian/Christmas', | ||||
|         'Cocos'                            => 'Indian/Cocos', | ||||
|         'Colombia'                         => 'America/Bogota', | ||||
|         'Cook'                             => 'Pacific/Rarotonga', | ||||
|         'Cuba'                             => 'America/Havana', | ||||
|         'Dacca'                            => 'Asia/Dhaka', | ||||
|         'Dateline Standard Time'           => 'Etc/GMT+12', | ||||
|         'Davis'                            => 'Antarctica/Davis', | ||||
|         'Dominican'                        => 'America/Santo_Domingo', | ||||
|         'DumontDUrville'                   => 'Antarctica/DumontDUrville', | ||||
|         'Dushanbe'                         => 'Asia/Dushanbe', | ||||
|         'Dutch_Guiana'                     => 'America/Paramaribo', | ||||
|         'E. Africa Standard Time'          => 'Africa/Nairobi', | ||||
|         'E. Australia Standard Time'       => 'Australia/Brisbane', | ||||
|         'E. Europe Standard Time'          => 'Europe/Minsk', | ||||
|         'E. South America Standard Time'   => 'America/Sao_Paulo', | ||||
|         'East_Timor'                       => 'Asia/Dili', | ||||
|         'Easter'                           => 'Pacific/Easter', | ||||
|         'Eastern Standard Time'            => 'America/New_York', | ||||
|         'Ecuador'                          => 'America/Guayaquil', | ||||
|         'Egypt Standard Time'              => 'Africa/Cairo', | ||||
|         'Ekaterinburg Standard Time'       => 'Asia/Yekaterinburg', | ||||
|         'Europe_Central'                   => 'Europe/Paris', | ||||
|         'Europe_Eastern'                   => 'Europe/Bucharest', | ||||
|         'Europe_Western'                   => 'Atlantic/Canary', | ||||
|         'FLE Standard Time'                => 'Europe/Kiev', | ||||
|         'Falkland'                         => 'Atlantic/Stanley', | ||||
|         'Fiji'                             => 'Pacific/Fiji', | ||||
|         'Fiji Standard Time'               => 'Pacific/Fiji', | ||||
|         'French_Guiana'                    => 'America/Cayenne', | ||||
|         'French_Southern'                  => 'Indian/Kerguelen', | ||||
|         'Frunze'                           => 'Asia/Bishkek', | ||||
|         'GMT'                              => 'UTC', // seems better than 'Atlantic/Reykjavik' | ||||
|         'GMT Standard Time'                => 'Europe/London', | ||||
|         'GTB Standard Time'                => 'Europe/Istanbul', | ||||
|         'Galapagos'                        => 'Pacific/Galapagos', | ||||
|         'Gambier'                          => 'Pacific/Gambier', | ||||
|         'Georgia'                          => 'Asia/Tbilisi', | ||||
|         'Georgian Standard Time'           => 'Etc/GMT-3', | ||||
|         'Gilbert_Islands'                  => 'Pacific/Tarawa', | ||||
|         'Goose_Bay'                        => 'America/Goose_Bay', | ||||
|         'Greenland Standard Time'          => 'America/Godthab', | ||||
|         'Greenland_Central'                => 'America/Scoresbysund', | ||||
|         'Greenland_Eastern'                => 'America/Scoresbysund', | ||||
|         'Greenland_Western'                => 'America/Godthab', | ||||
|         'Greenwich Standard Time'          => 'Atlantic/Reykjavik', | ||||
|         'Guam'                             => 'Pacific/Guam', | ||||
|         'Gulf'                             => 'Asia/Dubai', | ||||
|         'Guyana'                           => 'America/Guyana', | ||||
|         'Hawaii_Aleutian'                  => 'Pacific/Honolulu', | ||||
|         'Hawaiian Standard Time'           => 'Pacific/Honolulu', | ||||
|         'Hong_Kong'                        => 'Asia/Hong_Kong', | ||||
|         'Hovd'                             => 'Asia/Hovd', | ||||
|         'India'                            => 'Asia/Calcutta', | ||||
|         'India Standard Time'              => 'Asia/Calcutta', | ||||
|         'Indian_Ocean'                     => 'Indian/Chagos', | ||||
|         'Indochina'                        => 'Asia/Saigon', | ||||
|         'Indonesia_Central'                => 'Asia/Makassar', | ||||
|         'Indonesia_Eastern'                => 'Asia/Jayapura', | ||||
|         'Indonesia_Western'                => 'Asia/Jakarta', | ||||
|         'Iran'                             => 'Asia/Tehran', | ||||
|         'Iran Standard Time'               => 'Asia/Tehran', | ||||
|         'Irish'                            => 'Europe/Dublin', | ||||
|         'Irkutsk'                          => 'Asia/Irkutsk', | ||||
|         'Israel'                           => 'Asia/Jerusalem', | ||||
|         'Israel Standard Time'             => 'Asia/Jerusalem', | ||||
|         'Japan'                            => 'Asia/Tokyo', | ||||
|         'Jordan Standard Time'             => 'Asia/Amman', | ||||
|         'Kamchatka'                        => 'Asia/Kamchatka', | ||||
|         'Karachi'                          => 'Asia/Karachi', | ||||
|         'Kashgar'                          => 'Asia/Kashgar', | ||||
|         'Kazakhstan_Eastern'               => 'Asia/Almaty', | ||||
|         'Kazakhstan_Western'               => 'Asia/Aqtobe', | ||||
|         'Kizilorda'                        => 'Asia/Qyzylorda', | ||||
|         'Korea'                            => 'Asia/Seoul', | ||||
|         'Korea Standard Time'              => 'Asia/Seoul', | ||||
|         'Kosrae'                           => 'Pacific/Kosrae', | ||||
|         'Krasnoyarsk'                      => 'Asia/Krasnoyarsk', | ||||
|         'Kuybyshev'                        => 'Europe/Samara', | ||||
|         'Kwajalein'                        => 'Pacific/Kwajalein', | ||||
|         'Kyrgystan'                        => 'Asia/Bishkek', | ||||
|         'Lanka'                            => 'Asia/Colombo', | ||||
|         'Liberia'                          => 'Africa/Monrovia', | ||||
|         'Line_Islands'                     => 'Pacific/Kiritimati', | ||||
|         'Long_Shu'                         => 'Asia/Chongqing', | ||||
|         'Lord_Howe'                        => 'Australia/Lord_Howe', | ||||
|         'Macau'                            => 'Asia/Macau', | ||||
|         'Magadan'                          => 'Asia/Magadan', | ||||
|         'Malaya'                           => 'Asia/Kuala_Lumpur', | ||||
|         'Malaysia'                         => 'Asia/Kuching', | ||||
|         'Maldives'                         => 'Indian/Maldives', | ||||
|         'Marquesas'                        => 'Pacific/Marquesas', | ||||
|         'Marshall_Islands'                 => 'Pacific/Majuro', | ||||
|         'Mauritius'                        => 'Indian/Mauritius', | ||||
|         'Mauritius Standard Time'          => 'Indian/Mauritius', | ||||
|         'Mawson'                           => 'Antarctica/Mawson', | ||||
|         'Mexico Standard Time'             => 'America/Mexico_City', | ||||
|         'Mexico Standard Time 2'           => 'America/Chihuahua', | ||||
|         'Mid-Atlantic Standard Time'       => 'Atlantic/South_Georgia', | ||||
|         'Middle East Standard Time'        => 'Asia/Beirut', | ||||
|         'Mongolia'                         => 'Asia/Ulaanbaatar', | ||||
|         'Montevideo Standard Time'         => 'America/Montevideo', | ||||
|         'Morocco Standard Time'            => 'Africa/Casablanca', | ||||
|         'Moscow'                           => 'Europe/Moscow', | ||||
|         'Mountain Standard Time'           => 'America/Denver', | ||||
|         'Mountain Standard Time (Mexico)'  => 'America/Chihuahua', | ||||
|         'Myanmar'                          => 'Asia/Rangoon', | ||||
|         'Myanmar Standard Time'            => 'Asia/Rangoon', | ||||
|         'N. Central Asia Standard Time'    => 'Asia/Novosibirsk', | ||||
|         'Namibia Standard Time'            => 'Africa/Windhoek', | ||||
|         'Nauru'                            => 'Pacific/Nauru', | ||||
|         'Nepal'                            => 'Asia/Katmandu', | ||||
|         'Nepal Standard Time'              => 'Asia/Katmandu', | ||||
|         'New Zealand Standard Time'        => 'Pacific/Auckland', | ||||
|         'New_Caledonia'                    => 'Pacific/Noumea', | ||||
|         'New_Zealand'                      => 'Pacific/Auckland', | ||||
|         'Newfoundland'                     => 'America/St_Johns', | ||||
|         'Newfoundland Standard Time'       => 'America/St_Johns', | ||||
|         'Niue'                             => 'Pacific/Niue', | ||||
|         'Norfolk'                          => 'Pacific/Norfolk', | ||||
|         'Noronha'                          => 'America/Noronha', | ||||
|         'North Asia East Standard Time'    => 'Asia/Irkutsk', | ||||
|         'North Asia Standard Time'         => 'Asia/Krasnoyarsk', | ||||
|         'North_Mariana'                    => 'Pacific/Saipan', | ||||
|         'Novosibirsk'                      => 'Asia/Novosibirsk', | ||||
|         'Omsk'                             => 'Asia/Omsk', | ||||
|         'Oral'                             => 'Asia/Oral', | ||||
|         'Pacific SA Standard Time'         => 'America/Santiago', | ||||
|         'Pacific Standard Time'            => 'America/Los_Angeles', | ||||
|         'Pacific Standard Time (Mexico)'   => 'America/Tijuana', | ||||
|         'Pakistan'                         => 'Asia/Karachi', | ||||
|         'Pakistan Standard Time'           => 'Asia/Karachi', | ||||
|         'Palau'                            => 'Pacific/Palau', | ||||
|         'Papua_New_Guinea'                 => 'Pacific/Port_Moresby', | ||||
|         'Paraguay'                         => 'America/Asuncion', | ||||
|         'Peru'                             => 'America/Lima', | ||||
|         'Philippines'                      => 'Asia/Manila', | ||||
|         'Phoenix_Islands'                  => 'Pacific/Enderbury', | ||||
|         'Pierre_Miquelon'                  => 'America/Miquelon', | ||||
|         'Pitcairn'                         => 'Pacific/Pitcairn', | ||||
|         'Ponape'                           => 'Pacific/Ponape', | ||||
|         'Qyzylorda'                        => 'Asia/Qyzylorda', | ||||
|         'Reunion'                          => 'Indian/Reunion', | ||||
|         'Romance Standard Time'            => 'Europe/Paris', | ||||
|         'Rothera'                          => 'Antarctica/Rothera', | ||||
|         'Russian Standard Time'            => 'Europe/Moscow', | ||||
|         'SA Eastern Standard Time'         => 'Etc/GMT+3', | ||||
|         'SA Pacific Standard Time'         => 'America/Bogota', | ||||
|         'SA Western Standard Time'         => 'America/La_Paz', | ||||
|         'SE Asia Standard Time'            => 'Asia/Bangkok', | ||||
|         'Sakhalin'                         => 'Asia/Sakhalin', | ||||
|         'Samara'                           => 'Europe/Samara', | ||||
|         'Samarkand'                        => 'Asia/Samarkand', | ||||
|         'Samoa'                            => 'Pacific/Apia', | ||||
|         'Samoa Standard Time'              => 'Pacific/Apia', | ||||
|         'Seychelles'                       => 'Indian/Mahe', | ||||
|         'Shevchenko'                       => 'Asia/Aqtau', | ||||
|         'Singapore'                        => 'Asia/Singapore', | ||||
|         'Singapore Standard Time'          => 'Asia/Singapore', | ||||
|         'Solomon'                          => 'Pacific/Guadalcanal', | ||||
|         'South Africa Standard Time'       => 'Africa/Johannesburg', | ||||
|         'South_Georgia'                    => 'Atlantic/South_Georgia', | ||||
|         'Sri Lanka Standard Time'          => 'Asia/Colombo', | ||||
|         'Suriname'                         => 'America/Paramaribo', | ||||
|         'Sverdlovsk'                       => 'Asia/Yekaterinburg', | ||||
|         'Syowa'                            => 'Antarctica/Syowa', | ||||
|         'Tahiti'                           => 'Pacific/Tahiti', | ||||
|         'Taipei'                           => 'Asia/Taipei', | ||||
|         'Taipei Standard Time'             => 'Asia/Taipei', | ||||
|         'Tajikistan'                       => 'Asia/Dushanbe', | ||||
|         'Tashkent'                         => 'Asia/Tashkent', | ||||
|         'Tasmania Standard Time'           => 'Australia/Hobart', | ||||
|         'Tbilisi'                          => 'Asia/Tbilisi', | ||||
|         'Tokelau'                          => 'Pacific/Fakaofo', | ||||
|         'Tokyo Standard Time'              => 'Asia/Tokyo', | ||||
|         'Tonga'                            => 'Pacific/Tongatapu', | ||||
|         'Tonga Standard Time'              => 'Pacific/Tongatapu', | ||||
|         'Truk'                             => 'Pacific/Truk', | ||||
|         'Turkey'                           => 'Europe/Istanbul', | ||||
|         'Turkmenistan'                     => 'Asia/Ashgabat', | ||||
|         'Tuvalu'                           => 'Pacific/Funafuti', | ||||
|         'US/Eastern'                       => 'America/New_York', | ||||
|         'US Eastern Standard Time'         => 'Etc/GMT+5', | ||||
|         'US Mountain Standard Time'        => 'America/Phoenix', | ||||
|         'Uralsk'                           => 'Asia/Oral', | ||||
|         'Uruguay'                          => 'America/Montevideo', | ||||
|         'Urumqi'                           => 'Asia/Urumqi', | ||||
|         'Uzbekistan'                       => 'Asia/Tashkent', | ||||
|         'Vanuatu'                          => 'Pacific/Efate', | ||||
|         'Venezuela'                        => 'America/Caracas', | ||||
|         'Venezuela Standard Time'          => 'America/Caracas', | ||||
|         'Vladivostok'                      => 'Asia/Vladivostok', | ||||
|         'Vladivostok Standard Time'        => 'Asia/Vladivostok', | ||||
|         'Volgograd'                        => 'Europe/Volgograd', | ||||
|         'Vostok'                           => 'Antarctica/Vostok', | ||||
|         'W. Australia Standard Time'       => 'Australia/Perth', | ||||
|         'W. Central Africa Standard Time'  => 'Africa/Lagos', | ||||
|         'W. Europe Standard Time'          => 'Europe/Berlin', | ||||
|         'Wake'                             => 'Pacific/Wake', | ||||
|         'Wallis'                           => 'Pacific/Wallis', | ||||
|         'West Asia Standard Time'          => 'Asia/Tashkent', | ||||
|         'West Pacific Standard Time'       => 'Pacific/Port_Moresby', | ||||
|         'Yakutsk'                          => 'Asia/Yakutsk', | ||||
|         'Yakutsk Standard Time'            => 'Asia/Yakutsk', | ||||
|         'Yekaterinburg'                    => 'Asia/Yekaterinburg', | ||||
|         'Yerevan'                          => 'Asia/Yerevan', | ||||
|         'Yukon'                            => 'America/Yakutat', | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of timezones acceptable by DateTimeZone but not strtotime. | ||||
|      */ | ||||
|     protected $_invalid_legacy  = array( | ||||
|         'US/Eastern' => true, | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @var array|bool List of system identifiers or false if none available. | ||||
|      */ | ||||
|     protected $_identifiers     = false; | ||||
|  | ||||
|     /** | ||||
|      * Initialize local cache and identifiers. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry Registry to use. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_cache = $this->_registry->get( 'cache.memory' ); | ||||
|         $this->_init_identifiers(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get default timezone to use in input/output. | ||||
|      * | ||||
|      * Approach is as follows: | ||||
|      * - check user profile for timezone preference; | ||||
|      * - if user has no preference - check site for timezone selection; | ||||
|      * - if site has no selection - raise notice and use 'UTC'. | ||||
|      * | ||||
|      * @return string Olson timezone string identifier. | ||||
|      */ | ||||
|     public function get_default_timezone() { | ||||
|         static $default_timezone = null; | ||||
|         if ( null === $default_timezone ) { | ||||
|             $candidates = array(); | ||||
|             $candidates[] = (string)$this->_registry->get( 'model.meta-user' ) | ||||
|                 ->get_current( 'ai1ec_timezone' ); | ||||
|             $candidates[] = (string)$this->_registry->get( 'model.option' ) | ||||
|                 ->get( 'timezone_string' ); | ||||
|             $candidates[] = (string)$this->_registry->get( 'model.option' ) | ||||
|                 ->get( 'gmt_offset' ); | ||||
|             $candidates   = array_filter( $candidates, 'strlen' ); | ||||
|             foreach ( $candidates as $timezone ) { | ||||
|                 $timezone = $this->get_name( $timezone ); | ||||
|                 if ( false !== $timezone ) { | ||||
|                     $default_timezone = $timezone; | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             if ( null === $default_timezone ) { | ||||
|                 $default_timezone = 'UTC'; | ||||
|                 $this->_registry->get( 'notification.admin' )->store( | ||||
|                     sprintf( | ||||
|                         Ai1ec_I18n::__( | ||||
|                             'Please select site timezone in %s <em>Timezone</em> dropdown menu.' | ||||
|                         ), | ||||
|                         '<a href="' . ai1ec_admin_url( 'options-general.php' ) . | ||||
|                         '">' . Ai1ec_I18n::__( 'Settings' ) . '</a>' | ||||
|                     ), | ||||
|                     'error' | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return $default_timezone; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Attempt to decode GMT offset to some Olson timezone. | ||||
|      * | ||||
|      * @param float $zone GMT offset. | ||||
|      * | ||||
|      * @return string Valid Olson timezone name (UTC is last resort). | ||||
|      */ | ||||
|     public function decode_gmt_timezone( $zone ) { | ||||
|         $auto_zone = timezone_name_from_abbr( null, $zone * 3600, true ); | ||||
|         if ( false !== $auto_zone ) { | ||||
|             return $auto_zone; | ||||
|         } | ||||
|         $auto_zone = timezone_name_from_abbr( | ||||
|             null, | ||||
|             ( (int) $zone ) * 3600, | ||||
|             true | ||||
|         ); | ||||
|         if ( false !== $auto_zone ) { | ||||
|             return $auto_zone; | ||||
|         } | ||||
|         $this->_registry->get( 'notification.admin' )->store( | ||||
|             sprintf( | ||||
|                 Ai1ec_I18n::__( | ||||
|                     'Timezone "UTC%+d" is not recognized. Please %suse valid%s timezone name, until then events will be created in UTC timezone.' | ||||
|                 ), | ||||
|                 $zone, | ||||
|                 '<a href="' . ai1ec_admin_url( 'options-general.php' ) . '">', | ||||
|                 '</a>' | ||||
|             ), | ||||
|             'error' | ||||
|         ); | ||||
|         return 'UTC'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get valid timezone name from input. | ||||
|      * | ||||
|      * @param string $zone Name to check/parse. | ||||
|      * | ||||
|      * @return string Timezone name to use | ||||
|      */ | ||||
|     public function get_name( $zone ) { | ||||
|         if ( is_numeric( $zone ) ) { | ||||
|             $decoded_zone = $this->decode_gmt_timezone( $zone ); | ||||
|             if ( 'UTC' !== $decoded_zone ) { | ||||
|                 $message = sprintf( | ||||
|                     Ai1ec_I18n::__( | ||||
|                         'Selected timezone "UTC%+d" will be treated as %s.' | ||||
|                     ), | ||||
|                     $zone, | ||||
|                     $decoded_zone | ||||
|                 ); | ||||
|                 $this->_registry->get( 'notification.admin' ) | ||||
|                     ->store( $message ); | ||||
|             } | ||||
|             $zone = $decoded_zone; | ||||
|         } | ||||
|         if ( false === $this->_identifiers ) { | ||||
|             return $zone; // anything should do, as zones are not supported | ||||
|         } | ||||
|         if ( ! isset( $this->_identifiers[$zone] ) ) { | ||||
|             $zone         = $this->_olson_lookup( $zone ); | ||||
|             $valid_legacy = false; | ||||
|             try { | ||||
|                 new DateTimeZone( $zone ); // throw away instantly | ||||
|                 $valid_legacy = true; | ||||
|             } catch ( Exception $excpt ) { | ||||
|                 $valid_legacy = false; | ||||
|             } | ||||
|             if ( ! $valid_legacy || isset( $this->_invalid_legacy[$zone] ) ) { | ||||
|                 return $this->guess_zone( $zone ); | ||||
|             } | ||||
|             $this->_identifiers[$zone] = $zone; | ||||
|             unset( $valid_legacy ); | ||||
|         } | ||||
|         return $zone; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Quick map look-up to discard zones that have limited recognition. | ||||
|      * | ||||
|      * @param string $zone Name of timezone to lookup. | ||||
|      * | ||||
|      * @return string Timezone name to use. Might be the same as $zone. | ||||
|      */ | ||||
|     protected function _olson_lookup( $zone ) { | ||||
|         if ( isset( $this->_zones[$zone] ) ) { | ||||
|             return $this->_zones[$zone]; | ||||
|         } | ||||
|         return $zone; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if timezone is set in wp_option | ||||
|      * | ||||
|      */ | ||||
|     public function is_timezone_not_set() { | ||||
|         $timezone = $this->_registry->get( 'model.option' ) | ||||
|             ->get( 'timezone_string' ); | ||||
|         return empty( $timezone ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Render options for select in settings | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function get_timezones( $only_zones = false ) { | ||||
|         $zones = DateTimeZone::listIdentifiers(); | ||||
|         if ( | ||||
|             empty( $zones ) | ||||
|         ) { | ||||
|             return array(); | ||||
|         } | ||||
|         if ( ! $only_zones ) { | ||||
|             $manual =  __( 'Manual Offset', AI1EC_PLUGIN_NAME ); | ||||
|             $options = array(); | ||||
|             $options[$manual][] = array( | ||||
|                 'text'  => __( 'Choose your timezone', AI1EC_PLUGIN_NAME ), | ||||
|                 'value' => '', | ||||
|                 'args'  => array( | ||||
|                     'selected' => 'selected' | ||||
|                 ) | ||||
|             ); | ||||
|         } | ||||
|         foreach ( $zones as $zone ) { | ||||
|             $exploded_zone = explode( '/', $zone ); | ||||
|             if ( ! isset( $exploded_zone[1] ) && ! $only_zones ) { | ||||
|                 $exploded_zone[1] = $exploded_zone[0]; | ||||
|                 $exploded_zone[0] = $manual; | ||||
|             } | ||||
|             $optgroup = $exploded_zone[0]; | ||||
|             unset( $exploded_zone[0] ); | ||||
|             $options[$optgroup][] = array( | ||||
|                 'text'  => implode( '/', $exploded_zone ), | ||||
|                 'value' => $zone, | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $options; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Guess valid timezone identifier from arbitrary input. | ||||
|      * | ||||
|      * @param string $meta_name Arbitrary input. | ||||
|      * | ||||
|      * @return string|bool Parsed timezone name or false if none found. | ||||
|      */ | ||||
|     public function guess_zone( $meta_name ) { | ||||
|         if ( isset( $this->_zones[$meta_name] ) ) { | ||||
|             return $this->_zones[$meta_name]; | ||||
|         } | ||||
|         $name_variants = array( | ||||
|             strtr( $meta_name, ' ', '_' ), | ||||
|             strtr( $meta_name, '_', ' ' ), | ||||
|         ); | ||||
|         if ( false !== ( $parenthesis_pos = strpos( $meta_name, '(' ) ) ) { | ||||
|             foreach ( $name_variants as $name ) { | ||||
|                 $name_variants[] = substr( $name, 0, $parenthesis_pos - 1 ); | ||||
|             } | ||||
|         } | ||||
|         foreach ( $name_variants as $name ) { | ||||
|             if ( isset( $this->_zones[$name] ) ) { | ||||
|                 // cache to avoid future lookups and return | ||||
|                 $this->_zones[$meta_name] = $this->_zones[$name]; | ||||
|                 return $this->_zones[$name]; | ||||
|             } | ||||
|         } | ||||
|         if ( | ||||
|             isset( $meta_name{0} ) && | ||||
|             '(' === $meta_name{0} && | ||||
|             $closing_pos = strpos( $meta_name, ')' ) | ||||
|         ) { | ||||
|             $meta_name = trim( substr( $meta_name, $closing_pos + 1 ) ); | ||||
|             return $this->guess_zone( $meta_name ); | ||||
|         } | ||||
|         if ( | ||||
|             false === strpos( $meta_name, ' Standard ' ) && | ||||
|             false !== ( $time_pos = strpos( $meta_name, ' Time' ) ) | ||||
|         ) { | ||||
|             $meta_name = substr( $meta_name, 0, $time_pos ) . | ||||
|                 ' Standard' . substr( $meta_name, $time_pos ); | ||||
|             return $this->guess_zone( $meta_name ); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get timezone object instance. | ||||
|      * | ||||
|      * @param string $timezone Name of timezone to get instance for. | ||||
|      * | ||||
|      * @return DateTimeZone Instance of timezone object. | ||||
|      * | ||||
|      * @throws Ai1ec_Date_Timezone_Exception If an error occurs. | ||||
|      */ | ||||
|     public function get( $timezone ) { | ||||
|         if ( 'sys.default' === $timezone ) { | ||||
|             $timezone = $this->get_default_timezone(); | ||||
|         } | ||||
|         $name = $this->get_name( $timezone ); | ||||
|         if ( ! $name ) { | ||||
|             $name = $this->get_name( $this->get_default_timezone() ); | ||||
|         } | ||||
|         $zone = $this->_cache->get( $name, null ); | ||||
|         if ( null === $zone ) { | ||||
|             $exception = null; | ||||
|             try { | ||||
|                 $zone = new DateTimeZone( $name ); | ||||
|             } catch ( Exception $invalid_tz ) { | ||||
|                 $exception = $invalid_tz; | ||||
|             } | ||||
|             if ( null !== $exception ) { | ||||
|                 throw new Ai1ec_Date_Timezone_Exception( $exception->getMessage() ); | ||||
|             } | ||||
|             $this->_cache->set( $name, $zone ); | ||||
|         } | ||||
|         return $zone; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add system identifiers to object registry. | ||||
|      * | ||||
|      * @return bool Success | ||||
|      */ | ||||
|     protected function _init_identifiers() { | ||||
|         $identifiers  = DateTimeZone::listIdentifiers(); | ||||
|         if ( ! $identifiers ) { | ||||
|             return false; | ||||
|         } | ||||
|         $mapped = array(); | ||||
|         foreach ( $identifiers as $zone ) { | ||||
|             $zone                = (string)$zone; | ||||
|             $mapped[$zone]       = true; | ||||
|             $this->_zones[$zone] = $zone; | ||||
|         } | ||||
|         unset( $identifiers, $zone ); | ||||
|         $this->_identifiers = $mapped; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,155 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Validation utility library | ||||
|  * | ||||
|  * @author     Timely Network Inc | ||||
|  * @since      2012.08.21 | ||||
|  * | ||||
|  * @package    AllInOneCalendar | ||||
|  * @subpackage AllInOneCalendar.Lib.Utility | ||||
|  */ | ||||
| class Ai1ec_Validation_Utility { | ||||
|  | ||||
|     /** | ||||
|      * Check if the date supplied is valid. It validates $date in the format given | ||||
|      * by $pattern, which matches one of the supported date patterns. | ||||
|      * | ||||
|      * @param  string  $date    Date string to validate | ||||
|      * @param  string  $pattern Key of date pattern (@see | ||||
|      *                          self::get_date_patterns()) to | ||||
|      *                          match date string against | ||||
|      * @return boolean | ||||
|      */ | ||||
|     static public function validate_date( $date, $pattern = 'def' ) { | ||||
|         $result = self::validate_date_and_return_parsed_date( $date, $pattern ); | ||||
|         if( $result === false ) { | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if the date supplied is valid. It validates date in the format given | ||||
|      * by $pattern, which matches one of the supported date patterns. | ||||
|      * | ||||
|      * @param string $date    Date string to parse | ||||
|      * @param string $pattern Key of date pattern (@see | ||||
|      *                        self::get_date_patterns()) to | ||||
|      *                        match date string against | ||||
|      * @return array|boolean An array with the parsed date or false if the date | ||||
|      *                       is not valid. | ||||
|      */ | ||||
|     static public function validate_date_and_return_parsed_date( | ||||
|         $date, $pattern = 'def' | ||||
|     ) { | ||||
|         $pattern = self::_get_pattern_regexp( $pattern ); | ||||
|         if ( preg_match( $pattern, $date, $matches ) ) { | ||||
|             if ( checkdate( $matches['m'], $matches['d'], $matches['y'] ) ) { | ||||
|                 return array( | ||||
|                     'month' => $matches['m'], | ||||
|                     'day'   => $matches['d'], | ||||
|                     'year'  => $matches['y'], | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert input into a valid ISO date. | ||||
|      * | ||||
|      * @param string $date    Date to convert to ISO. | ||||
|      * @param string $pattern Format used to store it. | ||||
|      * | ||||
|      * @return string|bool Re-formatted date or false on failure. | ||||
|      */ | ||||
|     static public function format_as_iso( $date, $pattern = 'def' ) { | ||||
|         $regexp = self::_get_pattern_regexp( $pattern ); | ||||
|         if ( ! preg_match( $regexp, $date, $matches ) ) { | ||||
|             return false; | ||||
|         } | ||||
|         return sprintf( | ||||
|             '%04d-%02d-%02d', | ||||
|             $matches['y'], | ||||
|             $matches['m'], | ||||
|             $matches['d'] | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create regexp with named groups to match positional elements. | ||||
|      * | ||||
|      * @param string $pattern Pattern to convert. | ||||
|      * | ||||
|      * @return string Regular expression pattern. | ||||
|      */ | ||||
|     static protected function _get_pattern_regexp( $pattern ) { | ||||
|         $pattern = self::get_date_pattern_by_key( $pattern ); | ||||
|         $pattern = preg_quote( $pattern, '/' ); | ||||
|         $pattern = str_replace( | ||||
|             array( 'dd',           'd',              'mm',           'm',              'yyyy',         'yy' ), | ||||
|             array( '(?P<d>\d{2})', '(?P<d>\d{1,2})', '(?P<m>\d{2})', '(?P<m>\d{1,2})', '(?P<y>\d{4})', '(?P<y>\d{2})' ), | ||||
|             $pattern | ||||
|         ); | ||||
|         // Accept hyphens and dots in place of forward slashes (for URLs). | ||||
|         $pattern = str_replace( '\/', '[\/\-\.]', $pattern ); | ||||
|         return '#^' . $pattern . '$#'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if the string or integer is a valid timestamp. | ||||
|      * | ||||
|      * @see http://stackoverflow.com/questions/2524680/check-whether-the-string-is-a-unix-timestamp | ||||
|      * @param string|int $timestamp | ||||
|      * @return boolean | ||||
|      */ | ||||
|     static public function is_valid_time_stamp( $timestamp ) { | ||||
|         return | ||||
|             ( | ||||
|                 is_int( $timestamp ) || | ||||
|                 ( (string)(int)$timestamp ) === (string)$timestamp | ||||
|             ) | ||||
|             && ( $timestamp <= PHP_INT_MAX ) | ||||
|             && ( $timestamp >= 0 /*~ PHP_INT_MAX*/ ); | ||||
|         // do not allow negative timestamps until this is widely accepted | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the associative array of date patterns supported by the plugin, | ||||
|      * currently: | ||||
|      *   array( | ||||
|      *     'def' => 'd/m/yyyy', | ||||
|      *     'us'  => 'm/d/yyyy', | ||||
|      *     'iso' => 'yyyy-m-d', | ||||
|      *     'dot' => 'm.d.yyyy', | ||||
|      *   ); | ||||
|      * | ||||
|      * 'd' or 'dd' represent the day, 'm' or 'mm' represent the month, and 'yy' | ||||
|      * or 'yyyy' represent the year. | ||||
|      * | ||||
|      * @return array Supported date patterns | ||||
|      */ | ||||
|     static public function get_date_patterns() { | ||||
|         return array( | ||||
|             'def' => 'd/m/yyyy', | ||||
|             'us'  => 'm/d/yyyy', | ||||
|             'iso' => 'yyyy-m-d', | ||||
|             'dot' => 'm.d.yyyy', | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the date pattern (in the form 'd-m-yyyy', for example) associated | ||||
|      * with the provided key, used by plugin settings. Simply a static map as | ||||
|      * follows: | ||||
|      * | ||||
|      * @param  string $key Key for the date format | ||||
|      * @return string      Associated date format pattern | ||||
|      */ | ||||
|     static public function get_date_pattern_by_key( $key = 'def' ) { | ||||
|         $patterns = self::get_date_patterns(); | ||||
|         return $patterns[$key]; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * DBI utils. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Dbi | ||||
|  */ | ||||
|  | ||||
| class Ai1ec_Dbi_Utils extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Returns SQL string for INSERT statement. | ||||
|      * | ||||
|      * @param array $value Array of values. | ||||
|      * | ||||
|      * @return string SQL statement. | ||||
|      */ | ||||
|     public function array_value_to_sql_value( array $value ) { | ||||
|         return '(' . implode( ',', array_values( $value ) ) . ')'; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										453
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/dbi/dbi.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										453
									
								
								wp-content/plugins/all-in-one-event-calendar/lib/dbi/dbi.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,453 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Wrapper for WPDB (WordPress DB Class) | ||||
|  * | ||||
|  * Thic class wrap the access to WordPress DB class ($wpdb) and | ||||
|  * allows us to abstract from the WordPress code and to expand it | ||||
|  * with convenience method specific for ai1ec | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Dbi | ||||
|  */ | ||||
| class Ai1ec_Dbi { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object Instance of object registry. | ||||
|      */ | ||||
|     protected $_registry = null; | ||||
|  | ||||
|     /** | ||||
|      * @var wpdb Instance of database interface object | ||||
|      */ | ||||
|     protected $_dbi = null; | ||||
|  | ||||
|     /** | ||||
|      * @var array Queries executed for log. | ||||
|      */ | ||||
|     protected $_queries = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var bool Set to true when logging is enabled. | ||||
|      */ | ||||
|     protected $_log_enabled = false; | ||||
|  | ||||
|     /** | ||||
|      * Constructor assigns injected database access object to class variable. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry Injected registry. | ||||
|      * @param wpdb                  $dbi      Injected database access object. | ||||
|      * | ||||
|      * @return void Constructor does not return. | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         $dbi = null | ||||
|     ) { | ||||
|         if ( null === $dbi ) { | ||||
|             global $wpdb; | ||||
|             $dbi = $wpdb; | ||||
|         } | ||||
|         $this->_dbi      = $dbi; | ||||
|         $this->_registry = $registry; | ||||
|         $this->_registry->get( 'controller.shutdown' )->register( | ||||
|             array( $this, 'shutdown' ) | ||||
|         ); | ||||
|         add_action( | ||||
|             'ai1ec_loaded', | ||||
|             array( $this, 'check_debug' ), | ||||
|             PHP_INT_MAX | ||||
|         ); | ||||
|         $this->set_timezone(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set timezone to UTC to avoid conversion errors. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function set_timezone() { | ||||
|         $this->_dbi->query( "SET time_zone = '+0:00'" ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Call explicitly when debug output must be disabled. | ||||
|      * | ||||
|      * @return void Method is not meant to return. | ||||
|      */ | ||||
|     public function disable_debug() { | ||||
|         $this->_log_enabled = false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Only attempt to enable debug after all add-ons are loaded. | ||||
|      * | ||||
|      * @wp_hook ai1ec_loaded | ||||
|      * | ||||
|      * @uses apply_filters ai1ec_dbi_debug | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function check_debug() { | ||||
|         $this->_log_enabled = apply_filters( | ||||
|             'ai1ec_dbi_debug', | ||||
|             ( false !== AI1EC_DEBUG ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform a MySQL database query, using current database connection. | ||||
|      * | ||||
|      * @param string $sql_query Database query | ||||
|      * | ||||
|      * @return int|false Number of rows affected/selected or false on error | ||||
|      */ | ||||
|     public function query( $sql_query ) { | ||||
|         $this->_query_profile( $sql_query ); | ||||
|         $result = $this->_dbi->query( $sql_query ); | ||||
|         $this->_query_profile( $result ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve one column from the database. | ||||
|      * | ||||
|      * Executes a SQL query and returns the column from the SQL result. | ||||
|      * If the SQL result contains more than one column, this function returns the column specified. | ||||
|      * If $query is null, this function returns the specified column from the previous SQL result. | ||||
|      * | ||||
|      * @param string|null $query Optional. SQL query. Defaults to previous query. | ||||
|      * @param int         $col   Optional. Column to return. Indexed from 0. | ||||
|      * | ||||
|      * @return array Database query result. Array indexed from 0 by SQL result row number. | ||||
|      */ | ||||
|     public function get_col( $query = null , $col = 0 ) { | ||||
|         $this->_query_profile( $query ); | ||||
|         $result = $this->_dbi->get_col( $query, $col ); | ||||
|         $this->_query_profile( count( $result ) ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if the terms variable is set in the Wpdb object | ||||
|      */ | ||||
|     public function are_terms_set() { | ||||
|         return isset( $this->_dbi->terms ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Prepares a SQL query for safe execution. Uses sprintf()-like syntax. | ||||
|      * | ||||
|      * The following directives can be used in the query format string: | ||||
|      *   %d (integer) | ||||
|      *   %f (float) | ||||
|      *   %s (string) | ||||
|      *   %% (literal percentage sign - no argument needed) | ||||
|      * | ||||
|      * All of %d, %f, and %s are to be left unquoted in the query string and they need an argument passed for them. | ||||
|      * Literals (%) as parts of the query must be properly written as %%. | ||||
|      * | ||||
|      * This function only supports a small subset of the sprintf syntax; it only supports %d (integer), %f (float), and %s (string). | ||||
|      * Does not support sign, padding, alignment, width or precision specifiers. | ||||
|      * Does not support argument numbering/swapping. | ||||
|      * | ||||
|      * May be called like {@link http://php.net/sprintf sprintf()} or like {@link http://php.net/vsprintf vsprintf()}. | ||||
|      * | ||||
|      * Both %d and %s should be left unquoted in the query string. | ||||
|      * | ||||
|      * @param string $query Query statement with sprintf()-like placeholders | ||||
|      * @param array|mixed $args The array of variables to substitute into the query's placeholders if being called like | ||||
|      *     {@link http://php.net/vsprintf vsprintf()}, or the first variable to substitute into the query's placeholders if | ||||
|      *     being called like {@link http://php.net/sprintf sprintf()}. | ||||
|      * @param mixed $args,... further variables to substitute into the query's placeholders if being called like | ||||
|      *     {@link http://php.net/sprintf sprintf()}. | ||||
|      * | ||||
|      * @return null|false|string Sanitized query string, null if there is no query, false if there is an error and string | ||||
|      *     if there was something to prepare | ||||
|      */ | ||||
|     public function prepare( $query, $args ) { | ||||
|  | ||||
|         if ( null === $query ) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         $args = func_get_args(); | ||||
|         array_shift( $args ); | ||||
|         // If args were passed as an array (as in vsprintf), move them up | ||||
|         if ( isset( $args[0] ) && is_array( $args[0] ) ) { | ||||
|             $args = $args[0]; | ||||
|         } | ||||
|         $query = str_replace( "'%s'", '%s', $query ); // in case someone mistakenly already singlequoted it | ||||
|         $query = str_replace( '"%s"', '%s', $query ); // doublequote unquoting | ||||
|         $query = preg_replace( '|(?<!%)%f|', '%F', $query ); // Force floats to be locale unaware | ||||
|         $query = preg_replace( '|(?<!%)%s|', "'%s'", $query ); // quote the strings, avoiding escaped strings like %%s | ||||
|         array_walk( $args, array( $this->_dbi, 'escape_by_ref' ) ); | ||||
|         return @vsprintf( $query, $args ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve an entire SQL result set from the database (i.e., many rows) | ||||
|      * | ||||
|      * Executes a SQL query and returns the entire SQL result. | ||||
|      * | ||||
|      * @param string $query SQL query. | ||||
|      * @param string $output Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants. With one of the first three, return an array of rows indexed from 0 by SQL result row number. | ||||
|      *     Each row is an associative array (column => value, ...), a numerically indexed array (0 => value, ...), or an object. ( ->column = value ), respectively. | ||||
|      *     With OBJECT_K, return an associative array of row objects keyed by the value of each row's first column's value. Duplicate keys are discarded. | ||||
|      * | ||||
|      * @return mixed Database query results | ||||
|      */ | ||||
|     public function get_results( $query, $output = OBJECT ){ | ||||
|         $this->_query_profile( $query ); | ||||
|         $result = $this->_dbi->get_results( $query, $output ); | ||||
|         $this->_query_profile( count( $result ) ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve one variable from the database. | ||||
|      * | ||||
|      * Executes a SQL query and returns the value from the SQL result. | ||||
|      * If the SQL result contains more than one column and/or more than one row, this function returns the value in the column and row specified. | ||||
|      * If $query is null, this function returns the value in the specified column and row from the previous SQL result. | ||||
|      * | ||||
|      * @param string|null $query SQL query. Defaults to null, use the result from the previous query. | ||||
|      * @param int         $col   Column of value to return. Indexed from 0. | ||||
|      * @param int         $row   Row of value to return. Indexed from 0. | ||||
|      * | ||||
|      * @return string|null Database query result (as string), or null on failure | ||||
|      */ | ||||
|     public function get_var( $query = null, $col = 0, $row = 0 ) { | ||||
|         $this->_query_profile( $query ); | ||||
|         $result = $this->_dbi->get_var( $query, $col, $row ); | ||||
|         $this->_query_profile( null !== $result ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve one row from the database. | ||||
|      * | ||||
|      * Executes a SQL query and returns the row from the SQL result | ||||
|      * | ||||
|      * @param string|null $query SQL query. | ||||
|      * @param string $output Optional. one of ARRAY_A | ARRAY_N | OBJECT constants. Return an associative array (column => value, ...), | ||||
|      *     a numerically indexed array (0 => value, ...) or an object ( ->column = value ), respectively. | ||||
|      * @param int $row Optional. Row to return. Indexed from 0. | ||||
|      * | ||||
|      * @return mixed Database query result in format specified by $output or null on failure | ||||
|      */ | ||||
|     public function get_row( $query = null, $output = OBJECT, $row = 0 ) { | ||||
|         $this->_query_profile( $query ); | ||||
|         $result = $this->_dbi->get_row( $query, $output, $row ); | ||||
|         $this->_query_profile( null !== $result ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Insert a row into a table. | ||||
|      * | ||||
|      * @param string $table table name | ||||
|      * @param array $data Data to insert (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped). | ||||
|      * @param array|string $format Optional. An array of formats to be mapped to each of the value in $data. If string, that format will be used for all of the values in $data. | ||||
|      *     A format is one of '%d', '%f', '%s' (integer, float, string). If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types. | ||||
|      * | ||||
|      * @return int|false The number of rows inserted, or false on error. | ||||
|      */ | ||||
|     public function insert( $table, $data, $format = null ) { | ||||
|         $this->_query_profile( | ||||
|             'INSERT INTO ' . $table . '; data: ' . json_encode( $data ) | ||||
|         ); | ||||
|         $result = $this->_dbi->insert( | ||||
|             $this->get_table_name( $table ), | ||||
|             $data, | ||||
|             $format | ||||
|         ); | ||||
|         $this->_query_profile( $result ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform removal from table. | ||||
|      * | ||||
|      * @param string $table  Table to remove from. | ||||
|      * @param array  $where  Where conditions | ||||
|      * @param array  $format Format entities for where. | ||||
|      * | ||||
|      * @return int|false Number of rows deleted or false. | ||||
|      */ | ||||
|     public function delete( $table, $where, $format = null ) { | ||||
|         $this->_query_profile( | ||||
|             'DELETE FROM ' . $table . '; conditions: ' . json_encode( $where ) | ||||
|         ); | ||||
|         $result = $this->_dbi->delete( | ||||
|             $this->get_table_name( $table ), | ||||
|             $where, | ||||
|             $format | ||||
|         ); | ||||
|         $this->_query_profile( $result ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Update a row in the table | ||||
|      * | ||||
|      * @param string $table table name | ||||
|      * @param array $data Data to update (in column => value pairs). Both $data columns and $data values should be "raw" (neither should be SQL escaped). | ||||
|      * @param array $where A named array of WHERE clauses (in column => value pairs). Multiple clauses will be joined with ANDs. Both $where columns and $where values should be "raw". | ||||
|      * @param array|string $format Optional. An array of formats to be mapped to each of the values in $data. If string, that format will be used for all of the values in $data. | ||||
|      *     A format is one of '%d', '%f', '%s' (integer, float, string). If omitted, all values in $data will be treated as strings unless otherwise specified in wpdb::$field_types. | ||||
|      * @param array|string $where_format Optional. An array of formats to be mapped to each of the values in $where. If string, that format will be used for all of the items in $where. A format is one of '%d', '%f', '%s' (integer, float, string). If omitted, all values in $where will be treated as strings. | ||||
|      * | ||||
|      * @return int|false The number of rows updated, or false on error. | ||||
|      */ | ||||
|     public function update( $table, $data, $where, $format = null, $where_format = null ) { | ||||
|         $this->_query_profile( 'UPDATE ' . $table . ': ' . implode( '//', $data ) ); | ||||
|         $result = $this->_dbi->update( $table, $data, $where, $format, $where_format ); | ||||
|         $this->_query_profile( $result ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieve all results from given table. | ||||
|      * | ||||
|      * @param string $table   Name of table. | ||||
|      * @param array  $columns List of columns to retrieve. | ||||
|      * @param string $output  See {@see self::get_results()} $output for more. | ||||
|      * | ||||
|      * @return array Collection. | ||||
|      */ | ||||
|     public function select( $table, array $columns, $output = OBJECT ) { | ||||
|         $sql_query = 'SELECT `' . implode( '`, `', $columns ) . '` FROM `' . | ||||
|             $this->get_table_name( $table ) . '`'; | ||||
|         return $this->get_results( $sql_query, $output ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * The database version number. | ||||
|      * | ||||
|      * @return false|string false on failure, version number on success | ||||
|      */ | ||||
|     public function db_version() { | ||||
|         return $this->_dbi->db_version(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the id of last `insert` operation. | ||||
|      * | ||||
|      * @return int Returns integer optionally zero when no insert was performed. | ||||
|      */ | ||||
|     public function get_insert_id() { | ||||
|         return $this->_dbi->insert_id; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the full name for the table. | ||||
|      * | ||||
|      * @param string $table Table name. | ||||
|      * | ||||
|      * @return string Full table name for the table requested. | ||||
|      */ | ||||
|     public function get_table_name( $table = '' ) { | ||||
|         static $prefix_len = null; | ||||
|         if ( ! isset( $this->_dbi->{$table} ) ) { | ||||
|             if ( null === $prefix_len ) { | ||||
|                 $prefix_len = strlen( $this->_dbi->prefix ); | ||||
|             } | ||||
|             if ( 0 === strncmp( $this->_dbi->prefix, $table, $prefix_len ) ) { | ||||
|                 return $table; | ||||
|             } | ||||
|             return $this->_dbi->prefix . $table; | ||||
|         } | ||||
|         return $this->_dbi->{$table}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return escaped value. | ||||
|      * | ||||
|      * @param string $input Value to be escaped. | ||||
|      * | ||||
|      * @return string Escaped value. | ||||
|      */ | ||||
|     public function escape( $input ) { | ||||
|         $this->_dbi->escape_by_ref( $input ); | ||||
|         return $input; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * In debug mode prints DB queries table. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function shutdown() { | ||||
|         if ( ! $this->_log_enabled ) { | ||||
|             return false; | ||||
|         } | ||||
|         echo '<div class="timely timely-debug"> | ||||
|           <table class="table table-striped"> | ||||
|             <thead> | ||||
|               <tr> | ||||
|                 <th>N.</th> | ||||
|                 <th>Query</th> | ||||
|                 <th>Duration, ms</th> | ||||
|                 <th>Row Count</th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody>'; | ||||
|         $i    = 0; | ||||
|         $time = 0; | ||||
|         foreach ( $this->_queries as $query ) { | ||||
|             $time += $query['d']; | ||||
|             echo '<tr> | ||||
|                     <td>', ++$i, '</td> | ||||
|                     <td>', $query['q'], '</td> | ||||
|                     <td>', round( $query['d'] * 1000, 2 ), '</td> | ||||
|                     <td>', (int)$query['r'], '</td> | ||||
|                   </tr>'; | ||||
|         } | ||||
|         echo ' | ||||
|             </tbody> | ||||
|             <tfoot> | ||||
|               <tr> | ||||
|                 <th colspan="4">Total time, ms: ', | ||||
|                 round( $time * 1000, 2 ), '</th> | ||||
|               </tr> | ||||
|             </tfoot> | ||||
|           </table> | ||||
|         </div>'; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method aiding query profiling. | ||||
|      * | ||||
|      * How to use: | ||||
|      * - on method resulting in query start call _query_profiler( 'SQL query' ) | ||||
|      * - on it's end call _query_profiler( (int)number_of_rows|(bool)false ) | ||||
|      * | ||||
|      * @param mixed $query_or_result Query on first call, result on second. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function _query_profile( $query_or_result ) { | ||||
|         static $last = null; | ||||
|         if ( null === $last ) { | ||||
|             $last = array( | ||||
|                 'd' => microtime( true ), | ||||
|                 'q' => $query_or_result, | ||||
|             ); | ||||
|         } else { | ||||
|             if ( count( $this->_queries ) > 200 ) { | ||||
|                 array_shift( $this->_queries ); | ||||
|             } | ||||
|             $this->_queries[] = array( | ||||
|                 'd' => microtime( true ) - $last['d'], | ||||
|                 'q' => $last['q'], | ||||
|                 'r' => $query_or_result, | ||||
|             ); | ||||
|             $last = null; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,269 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Checks configurations and notifies admin. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib | ||||
|  */ | ||||
| class Ai1ec_Environment_Checks extends Ai1ec_Base { | ||||
|  | ||||
|     const CORE_NAME = 'all-in-one-event-calendar/all-in-one-event-calendar.php'; | ||||
|  | ||||
|     /** | ||||
|      * List of dependencies. | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $_addons = array( | ||||
|         'all-in-one-event-calendar-extended-views/all-in-one-event-calendar-extended-views.php'               => '1.1.3', | ||||
|         'all-in-one-event-calendar-super-widget/all-in-one-event-calendar-super-widget.php'                   => '1.1.0', | ||||
|         'all-in-one-event-calendar-featured-events/all-in-one-event-calendar-featured-events.php'             => '1.0.5', | ||||
|         'all-in-one-event-calendar-frontend-submissions/all-in-one-event-calendar-frontend-submissions.php'   => '1.1.3', | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * Runs checks for necessary config options. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      */ | ||||
|     public function run_checks() { | ||||
|         $role         = get_role( 'administrator' ); | ||||
|         $current_user = get_userdata( get_current_user_id() ); | ||||
|         if ( | ||||
|             ! is_object( $role ) || | ||||
|             ! is_object( $current_user ) || | ||||
|             ! $role->has_cap( 'manage_ai1ec_options' ) || | ||||
|             ( | ||||
|                 defined( 'DOING_AJAX' ) && | ||||
|                 DOING_AJAX | ||||
|             ) | ||||
|         ) { | ||||
|             return; | ||||
|         } | ||||
|         do_action( 'ai1ec_env_check' ); | ||||
|         global $plugin_page; | ||||
|         $settings              = $this->_registry->get( 'model.settings' ); | ||||
|         $option                = $this->_registry->get( 'model.option' ); | ||||
|         $notification          = $this->_registry->get( 'notification.admin' ); | ||||
|         $created_calendar_page = false; | ||||
|  | ||||
|         // check if is set calendar page | ||||
|         if ( ! $settings->get( 'calendar_page_id' ) ) { | ||||
|             $calendar_page_id = wp_insert_post( | ||||
|                 array( | ||||
|                     'post_title'     => 'Calendar', | ||||
|                     'post_type'      => 'page', | ||||
|                     'post_status'    => 'publish', | ||||
|                     'comment_status' => 'closed' | ||||
|                 ) | ||||
|             ); | ||||
|             $settings->set( 'calendar_page_id', $calendar_page_id ); | ||||
|             $created_calendar_page = true; | ||||
|         } | ||||
|         if ( | ||||
|             $plugin_page !== AI1EC_PLUGIN_NAME . '-settings' && | ||||
|             $created_calendar_page | ||||
|         ) { | ||||
|             if ( | ||||
|                 $current_user->has_cap( 'manage_ai1ec_options' ) | ||||
|             ) { | ||||
|                 $msg = sprintf( | ||||
|                     Ai1ec_I18n::__( 'The plugin is successfully installed! <a href="%s">Add some events</a> and see them on your <a href="%s">Calendar page</a>.<br />Visit the <a href="%s">Settings page</a> to configure the plugin and get most of it.' ), | ||||
|                     'post-new.php?post_type=ai1ec_event', | ||||
|                     get_page_link( $calendar_page_id ), | ||||
|                     ai1ec_admin_url( AI1EC_SETTINGS_BASE_URL ) | ||||
|                 ); | ||||
|                 $notification->store( | ||||
|                     $msg, | ||||
|                     'updated', | ||||
|                     2, | ||||
|                     array( Ai1ec_Notification_Admin::RCPT_ADMIN ) | ||||
|                 ); | ||||
|             } else { | ||||
|                 $msg = Ai1ec_I18n::__( | ||||
|                     'The plugin is installed, but has not been configured. Please log in as an Administrator to set it up.' | ||||
|                 ); | ||||
|                 $notification->store( | ||||
|                     $msg, | ||||
|                     'updated', | ||||
|                     2, | ||||
|                     array( Ai1ec_Notification_Admin::RCPT_ALL ) | ||||
|                 ); | ||||
|             } | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Tell user to sign in to API in order to use Feeds | ||||
|         $ics_current_db_version = $option->get( Ai1ecIcsConnectorPlugin::ICS_OPTION_DB_VERSION ); | ||||
|         if ( $ics_current_db_version != null && $ics_current_db_version != '' ) { | ||||
|             $rows      = $this->_registry->get( 'dbi.dbi' )->select( | ||||
|                 'ai1ec_event_feeds', | ||||
|                 array( 'feed_id' ) | ||||
|             ); | ||||
|             $api_reg   = $this->_registry->get( 'model.api.api-registration' ); | ||||
|             $is_signed = $api_reg->is_signed(); | ||||
|  | ||||
|             if ( 0 < count( $rows ) && ! $is_signed ) { | ||||
|                 $msg = Ai1ec_I18n::__( | ||||
|                         '<b>ACTION REQUIRED!</b> Please, <a href="edit.php?post_type=ai1ec_event&page=all-in-one-event-calendar-settings">sign</a> into Timely Network to continue syncing your imported events.' | ||||
|                     ); | ||||
|                 $notification->store( | ||||
|                     $msg, | ||||
|                     'error', | ||||
|                     0, | ||||
|                     array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                     false | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Check for needed PHP extensions. | ||||
|         if ( | ||||
|             ! function_exists( 'iconv' ) && | ||||
|             ! $option->get( 'ai1ec_iconv_notification' ) | ||||
|         ) { | ||||
|             $msg = Ai1ec_I18n::__( | ||||
|                     'PHP extension "iconv" needed for All-In-One-Event-Calendar is missing. Please, check your PHP configuration.<br />' | ||||
|                 ); | ||||
|             $notification->store( | ||||
|                 $msg, | ||||
|                 'error', | ||||
|                 0, | ||||
|                 array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                 true | ||||
|             ); | ||||
|             $option->set( 'ai1ec_iconv_notification', true ); | ||||
|         } | ||||
|         if ( | ||||
|             ! function_exists( 'mb_check_encoding' ) && | ||||
|             ! $option->get( 'ai1ec_mbstring_notification' ) | ||||
|         ) { | ||||
|             $msg = Ai1ec_I18n::__( | ||||
|                     'PHP extension "mbstring" needed for All-In-One-Event-Calendar is missing. Please, check your PHP configuration.<br />' | ||||
|                 ); | ||||
|             $notification->store( | ||||
|                 $msg, | ||||
|                 'error', | ||||
|                 0, | ||||
|                 array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                 true | ||||
|             ); | ||||
|             $option->set( 'ai1ec_mbstring_notification', true ); | ||||
|         } | ||||
|         global $wp_rewrite; | ||||
|         $option  = $this->_registry->get( 'model.option' ); | ||||
|         $rewrite = $option->get( 'ai1ec_force_flush_rewrite_rules' ); | ||||
|         if ( | ||||
|             ! $rewrite || | ||||
|             ! is_object( $wp_rewrite ) || | ||||
|             ! isset( $wp_rewrite->rules ) || | ||||
|             ! is_array ( $wp_rewrite->rules ) || | ||||
|             0 === count( $wp_rewrite->rules ) | ||||
|         ) { | ||||
|             return; | ||||
|         } | ||||
|         $this->_registry->get( 'rewrite.helper' )->flush_rewrite_rules(); | ||||
|         $option->set( 'ai1ec_force_flush_rewrite_rules', false ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Checks for add-on versions. | ||||
|      * | ||||
|      * @param string $plugin Plugin name. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      */ | ||||
|     public function check_addons_activation( $plugin ) { | ||||
|         switch ( $plugin ) { | ||||
|             case self::CORE_NAME: | ||||
|                 $this->_check_active_addons(); | ||||
|                 break; | ||||
|             default: | ||||
|                 $min_version = isset( $this->_addons[$plugin] ) | ||||
|                 ? $this->_addons[$plugin] | ||||
|                 : null; | ||||
|                 if ( null !== $min_version ) { | ||||
|                     $this->_plugin_activation( $plugin, $min_version ); | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Launches after bulk update. | ||||
|      * | ||||
|      * @param bool $result Input filter value. | ||||
|      * | ||||
|      * @return bool Output filter value. | ||||
|      */ | ||||
|     public function check_bulk_addons_activation( $result ) { | ||||
|         $this->_check_active_addons( true ); | ||||
|         return $result; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Checks all Time.ly addons. | ||||
|      * | ||||
|      * @param bool $silent Whether to perform silent plugin deactivation or not. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      */ | ||||
|     protected function _check_active_addons( $silent = false ) { | ||||
|         foreach ( $this->_addons as $addon => $version ) { | ||||
|             if ( is_plugin_active( $addon ) ) { | ||||
|                 $this->_plugin_activation( $addon, $version, true, $silent ); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Performs Extended Views version check. | ||||
|      * | ||||
|      * @param string $addon       Addon identifier. | ||||
|      * @param string $min_version Minimum required version. | ||||
|      * @param bool   $core        If set to true Core deactivates active and | ||||
|      *                            outdated addons when it is activated. If set | ||||
|      *                            false it means that addon activation process | ||||
|      *                            called this method and it's enough to throw | ||||
|      *                            and exception and allow exception handler | ||||
|      *                            to deactivate addon with proper notices. | ||||
|      * @param bool   $silent      Whether to perform silent plugin deactivation | ||||
|      *                            or not. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      * @throws Ai1ec_Outdated_Addon_Exception | ||||
|      */ | ||||
|     protected function _plugin_activation( | ||||
|         $addon, | ||||
|         $min_version, | ||||
|         $core   = false, | ||||
|         $silent = false | ||||
|     ) { | ||||
|         $ev_data = get_plugin_data( | ||||
|             WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $addon | ||||
|         ); | ||||
|         if ( ! isset( $ev_data['Version'] ) ) { | ||||
|             return; | ||||
|         } | ||||
|         $version = $ev_data['Version']; | ||||
|         if ( -1 === version_compare( $version, $min_version ) ) { | ||||
|             $msg1 = Ai1ec_I18n::__( 'The add-on <strong>%s</strong> must be updated to at least version %s to maintain compatibility with the core calendar.' ); | ||||
|             $msg2 = Ai1ec_I18n::__( 'If you do not see update notices below, ensure you have properly <a href="https://time.ly/document/user-guide/getting-started/license-keys/" target="_blank">entered your licence keys</a>. Alternatively, navigate to <a href="https://time.ly/your-account/">your account</a> to download the latest version of the add-on(s) and <a href="https://time.ly/document/user-guide/troubleshooting/perform-manual-upgrade/">update manually</a>. Please <a href="https://time.ly/forums/">post in the forum</a> if you have trouble. We are happy to help.' ); | ||||
|  | ||||
|             $message = sprintf( | ||||
|                 '<span class="highlight" style="margin: 0 -6px; padding: 4px 6px">' . | ||||
|                     $msg1 . '</span></p><p>' . $msg2, | ||||
|                 $ev_data['Name'], | ||||
|                 $min_version | ||||
|             ); | ||||
|             $this->_registry->get( 'calendar.updates' )->clear_transients(); | ||||
|             throw new Ai1ec_Outdated_Addon_Exception( $message, $addon ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * The exception thrown when value doesn't pass validation. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib | ||||
|  */ | ||||
| class Ai1ec_Outdated_Addon_Exception extends Ai1ec_Exception { | ||||
|  | ||||
|     protected $_addon; | ||||
|  | ||||
|     /** | ||||
|      * Constructor. | ||||
|      * | ||||
|      * @param string $message Exception message. | ||||
|      * @param string $addon   Addon to disable. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      */ | ||||
|     public function __construct( $message, $addon ) { | ||||
|         parent::__construct( $message ); | ||||
|         $this->_addon = $addon; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns addon name. | ||||
|      * | ||||
|      * @return string Addon name. | ||||
|      */ | ||||
|     public function plugin_to_disable() { | ||||
|         return $this->_addon; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Overrides __toString() to avoid stack trace. | ||||
|      * | ||||
|      * @return string Empty string. | ||||
|      */ | ||||
|     public function __toString() { | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @see Ai1ec_Exception::get_redirect_url() | ||||
|      */ | ||||
|     public function get_redirect_url() { | ||||
|         return ai1ec_admin_url( 'plugins.php' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @see Ai1ec_Exception::display_backtrace() | ||||
|      */ | ||||
|     public function display_backtrace(){ | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,62 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Abstract Class for Callback Events. | ||||
|  * | ||||
|  * @author       Time.ly Network Inc. | ||||
|  * @since       2.0 | ||||
|  * | ||||
|  * @package       AI1EC | ||||
|  * @subpackage AI1EC.Event | ||||
|  */ | ||||
| abstract class Ai1ec_Event_Callback_Abstract { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Registry_Object The Object registry. | ||||
|      */ | ||||
|     protected $_registry      = null; | ||||
|  | ||||
|     /** | ||||
|      * @var string The registry method name defined in the class map. | ||||
|      */ | ||||
|     protected $_registry_name = null; | ||||
|  | ||||
|     /** | ||||
|      * @var string The method invoked by the current callback. | ||||
|      */ | ||||
|     protected $_method          = null; | ||||
|  | ||||
|     /** | ||||
|      * Initiate callback objects. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry Registry object. | ||||
|      * @param string                $path      Registry method name defined in the class map. | ||||
|      * @param string                $method      Method invoked by the currect callback. | ||||
|      * | ||||
|      * @return void Constructor does not return. | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         $path, | ||||
|         $method | ||||
|     ) { | ||||
|         $this->_registry      = $registry; | ||||
|         $this->_registry_name = $path; | ||||
|         $this->_method          = $method; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Invoke the method added to the current callback. | ||||
|      * | ||||
|      * @return mixed Value returned by the current method. | ||||
|      */ | ||||
|     public function run() { | ||||
|         $argv = func_get_args(); | ||||
|         return $this->_registry->dispatch( | ||||
|             $this->_registry_name, | ||||
|             $this->_method, | ||||
|             $argv | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Event Callback Action creation. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @package      AI1EC | ||||
|  * @subpackage   AI1EC.Event | ||||
|  */ | ||||
| class Ai1ec_Event_Callback_Action extends Ai1ec_Event_Callback_Abstract { | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Event Callback Filter creation. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @package      AI1EC | ||||
|  * @subpackage   AI1EC.Event | ||||
|  */ | ||||
| class Ai1ec_Event_Callback_Filter extends Ai1ec_Event_Callback_Abstract { | ||||
| } | ||||
| @@ -0,0 +1,14 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Event Callback Shortcode creation. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @package      AI1EC | ||||
|  * @subpackage   AI1EC.Event | ||||
|  */ | ||||
| class Ai1ec_Event_Callback_Shortcode extends Ai1ec_Event_Callback_Abstract { | ||||
| } | ||||
| @@ -0,0 +1,145 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Event Dispatcher processing. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package       AI1EC | ||||
|  * @subpackage AI1EC.Event | ||||
|  */ | ||||
| class Ai1ec_Event_Dispatcher extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Register callback object. | ||||
|      * | ||||
|      * @param string                        $hook           Name of the event hook. | ||||
|      * @param Ai1ec_Event_Callback_Abstract $entity           Event Callback object. | ||||
|      * @param integer                        $priority       Priorify of the event hook execution. | ||||
|      * @param integer                        $accepted_args Number of accepted method parameters. | ||||
|      * | ||||
|      * @return Ai1ec_Event_Dispatcher Event Dispatcher Object. | ||||
|      */ | ||||
|     public function register( | ||||
|         $hook, | ||||
|         Ai1ec_Event_Callback_Abstract $entity, | ||||
|         $priority      = 10, | ||||
|         $accepted_args = 1 | ||||
|     ) { | ||||
|         $wp_method = 'add_action'; | ||||
|         if ( $entity instanceof Ai1ec_Event_Callback_Filter ) { | ||||
|             $wp_method = 'add_filter'; | ||||
|         } | ||||
|         $wp_method( | ||||
|             $hook, | ||||
|             array( $entity, 'run' ), | ||||
|             $priority, | ||||
|             $accepted_args | ||||
|         ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a callback object and register it. | ||||
|      * | ||||
|      * @param string  $hook          Name of the event hook. | ||||
|      * @param array   $method        Method to call. | ||||
|      * @param integer $priority      Priorify of the event hook execution. | ||||
|      * @param integer $accepted_args Number of accepted method parameters. | ||||
|      * @param string  $type          The type to add. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     protected function _register( | ||||
|         $hook, | ||||
|         array $method, | ||||
|         $type, | ||||
|         $priority      = 10, | ||||
|         $accepted_args = 1 | ||||
|     ) { | ||||
|         $action = $this->_registry->get( | ||||
|             'event.callback.' . $type, | ||||
|             $method[0], | ||||
|             $method[1] | ||||
|         ); | ||||
|         $this->register( | ||||
|             $hook, | ||||
|             $action, | ||||
|             $priority, | ||||
|             $accepted_args | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register a filter. | ||||
|      * | ||||
|      * @param string  $hook          Name of the event hook. | ||||
|      * @param array   $method        Method to call. | ||||
|      * @param integer $priority      Priorify of the event hook execution. | ||||
|      * @param integer $accepted_args Number of accepted method parameters. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function register_filter( | ||||
|         $hook, | ||||
|         array $method, | ||||
|         $priority      = 10, | ||||
|         $accepted_args = 1 | ||||
|     ) { | ||||
|         $this->_register( | ||||
|             $hook, | ||||
|             $method, | ||||
|             'filter', | ||||
|             $priority, | ||||
|             $accepted_args | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register an action. | ||||
|      * | ||||
|      * @param string  $hook          Name of the event hook. | ||||
|      * @param array   $method        Method to call. | ||||
|      * @param integer $priority      Priorify of the event hook execution. | ||||
|      * @param integer $accepted_args Number of accepted method parameters. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function register_action( | ||||
|         $hook, | ||||
|         array $method, | ||||
|         $priority      = 10, | ||||
|         $accepted_args = 1 | ||||
|     ) { | ||||
|         $this->_register( | ||||
|             $hook, | ||||
|             $method, | ||||
|             'action', | ||||
|             $priority, | ||||
|             $accepted_args | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Register a shortcode. | ||||
|      * | ||||
|      * @param string $shortcode Name of the shortcode tag. | ||||
|      * @param array  $method    Method to call. | ||||
|      * | ||||
|      * @return void | ||||
|      */ | ||||
|     public function register_shortcode( | ||||
|         $shortcode, | ||||
|         array $method | ||||
|     ) { | ||||
|         $entity = $this->_registry->get( | ||||
|             'event.callback.shortcode', | ||||
|             $method[0], | ||||
|             $method[1] | ||||
|         ); | ||||
|         add_shortcode( $shortcode, array( $entity, 'run' ) ); | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Abstract base class for all our excpetion. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Exception | ||||
|  */ | ||||
| class Ai1ec_Exception extends Exception { | ||||
|  | ||||
|     /** | ||||
|      * A message to be displayed for admin | ||||
|      * | ||||
|      * Specific Exceptions should override this. | ||||
|      * | ||||
|      * @return string Message to be displayed for admin | ||||
|      */ | ||||
|     public function get_html_message() { | ||||
|         return $this->getMessage(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the èath of the plugin to disable it. | ||||
|      * If empty it disables core. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function plugin_to_disable() { | ||||
|         return ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns destination URL if exception handler redirects. | ||||
|      * | ||||
|      * @return string Result. | ||||
|      */ | ||||
|     public function get_redirect_url() { | ||||
|         return ai1ec_get_admin_url(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Defined whether exception handler should attach backtrace or not. | ||||
|      * | ||||
|      * @return bool Value. | ||||
|      */ | ||||
|     public function display_backtrace(){ | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Exception that is raised when a PHP error happens in our plugin. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Exception | ||||
|  */ | ||||
| class Ai1ec_Error_Exception extends ErrorException { | ||||
| } | ||||
| @@ -0,0 +1,606 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Handles exception and errors | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Exception | ||||
|  */ | ||||
| class Ai1ec_Exception_Handler { | ||||
|  | ||||
|     /** | ||||
|      * @var string The option for the messgae in the db | ||||
|      */ | ||||
|     const DB_DEACTIVATE_MESSAGE = 'ai1ec_deactivate_message'; | ||||
|  | ||||
|     /** | ||||
|      * @var string The GET parameter to reactivate the plugin | ||||
|      */ | ||||
|     const DB_REACTIVATE_PLUGIN  = 'ai1ec_reactivate_plugin'; | ||||
|  | ||||
|     /** | ||||
|      * @var callable|null Previously set exception handler if any | ||||
|      */ | ||||
|     protected $_prev_ex_handler; | ||||
|  | ||||
|     /** | ||||
|      * @var callable|null Previously set error handler if any | ||||
|      */ | ||||
|     protected $_prev_er_handler; | ||||
|  | ||||
|     /** | ||||
|      * @var string The name of the Exception class to handle | ||||
|      */ | ||||
|     protected $_exception_class; | ||||
|  | ||||
|     /** | ||||
|      * @var string The name of the ErrorException class to handle | ||||
|      */ | ||||
|     protected $_error_exception_class; | ||||
|  | ||||
|     /** | ||||
|      * @var string The message to display in the admin notice | ||||
|      */ | ||||
|     protected $_message; | ||||
|  | ||||
|     /** | ||||
|      * @var array Mapped list of errors that are non-fatal, to be ignored | ||||
|      *            in production. | ||||
|      */ | ||||
|     protected $_nonfatal_errors = null; | ||||
|  | ||||
|     /** | ||||
|      * Store exception handler that was previously set | ||||
|      * | ||||
|      * @param callable|null $_prev_ex_handler | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      */ | ||||
|     public function set_prev_ex_handler( $prev_ex_handler ) { | ||||
|         $this->_prev_ex_handler = $prev_ex_handler; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Store error handler that was previously set | ||||
|      * | ||||
|      * @param callable|null $_prev_er_handler | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      */ | ||||
|     public function set_prev_er_handler( $prev_er_handler ) { | ||||
|         $this->_prev_er_handler = $prev_er_handler; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Constructor accepts names of classes to be handled | ||||
|      * | ||||
|      * @param string $exception_class Name of exceptions base class to handle | ||||
|      * @param string $error_class     Name of errors base class to handle | ||||
|      * | ||||
|      * @return void Constructor newer returns | ||||
|      */ | ||||
|     public function __construct( $exception_class, $error_class ) { | ||||
|         $this->_exception_class       = $exception_class; | ||||
|         $this->_error_exception_class = $error_class; | ||||
|         $this->_nonfatal_errors       = array( | ||||
|             E_USER_WARNING => true, | ||||
|             E_WARNING      => true, | ||||
|             E_USER_NOTICE  => true, | ||||
|             E_NOTICE       => true, | ||||
|             E_STRICT       => true, | ||||
|         ); | ||||
|         if ( version_compare( PHP_VERSION, '5.3.0' ) >= 0 ) { | ||||
|             // wrapper `constant( 'XXX' )` is used to avoid compile notices | ||||
|             // on earlier PHP versions. | ||||
|             $this->_nonfatal_errors[constant( 'E_DEPRECATED' )]      = true; | ||||
|             $this->_nonfatal_errors[constant( 'E_USER_DEPRECATED') ] = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return add-on, which caused the exception or null if it was Core. | ||||
|      * | ||||
|      * Relies on `plugin_to_disable` method which may be implemented by | ||||
|      * an exception. If it returns non empty value - it is returned. | ||||
|      * | ||||
|      * @param Exception $exception Actual exception which was thrown. | ||||
|      * | ||||
|      * @return string|null Add-on identifier (plugin url), or null. | ||||
|      */ | ||||
|     public function is_caused_by_addon( $exception ) { | ||||
|         $addon = null; | ||||
|         if ( method_exists( $exception, 'plugin_to_disable' ) ) { | ||||
|             $addon = $exception->plugin_to_disable(); | ||||
|             if ( empty( $addon ) ) { | ||||
|                 $addon = null; | ||||
|             } | ||||
|         } | ||||
|         if ( null === $addon ) { | ||||
|             $position   = strlen( dirname( AI1EC_PATH ) ) + 1; | ||||
|             $length     = strlen( AI1EC_PLUGIN_NAME ); | ||||
|             $trace_list = $exception->getTrace(); | ||||
|             array_unshift( | ||||
|                 $trace_list, | ||||
|                 array( 'file' => $exception->getFile() ) | ||||
|             ); | ||||
|             foreach ( $trace_list as $trace ) { | ||||
|                 if ( | ||||
|                     ! isset( $trace['file'] ) || | ||||
|                     ! isset( $trace['file'][$position] ) | ||||
|                 ) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 $file = substr( | ||||
|                     $trace['file'], | ||||
|                     $position, | ||||
|                     strpos( $trace['file'], '/', $position ) - $position | ||||
|                 ); | ||||
|                 if ( 0 === strncmp( AI1EC_PLUGIN_NAME, $file, $length ) ) { | ||||
|                     if ( AI1EC_PLUGIN_NAME !== $file ) { | ||||
|                         $addon = $file . '/' . $file . '.php'; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if ( 'core' === strtolower( $addon ) ) { | ||||
|             return null; | ||||
|         } | ||||
|         return $addon; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get tag-line for disabling. | ||||
|      * | ||||
|      * Extracts plugin name from file. | ||||
|      * | ||||
|      * @param string $addon Name of disabled add-on. | ||||
|      * | ||||
|      * @return string Message to display before full trace. | ||||
|      */ | ||||
|     public function get_disabled_line( $addon ) { | ||||
|         $file = dirname( AI1EC_PATH ) . DIRECTORY_SEPARATOR . $addon; | ||||
|         $line = ''; | ||||
|         if ( | ||||
|             is_file( $file ) && | ||||
|             preg_match( | ||||
|                 '|Plugin Name:\s*(.+)|', | ||||
|                 file_get_contents( $file ), | ||||
|                 $matches | ||||
|             ) | ||||
|         ) { | ||||
|             $line = '<p><strong>' . | ||||
|                 sprintf( | ||||
|                     __( 'The add-on "%s" has been disabled due to an error:' ), | ||||
|                     __( trim( $matches[1] ), dirname( $addon ) ) | ||||
|                 ) . | ||||
|                 '</strong></p>'; | ||||
|         } | ||||
|         return $line; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Global exceptions handling method | ||||
|      * | ||||
|      * @param Exception $exception Previously thrown exception to handle | ||||
|      * | ||||
|      * @return void Exception handler is not expected to return | ||||
|      */ | ||||
|     public function handle_exception( $exception ) { | ||||
|         if ( defined( 'AI1EC_DEBUG' ) && true === AI1EC_DEBUG ) { | ||||
|             echo '<pre>'; | ||||
|             $this->var_debug( $exception ); | ||||
|             echo '</pre>'; | ||||
|             die(); | ||||
|         } | ||||
|         // if it's something we handle, handle it | ||||
|         $backtrace = $this->_get_backtrace( $exception ); | ||||
|         if ( $exception instanceof $this->_exception_class ) { | ||||
|             // check if it's a plugin instead of core | ||||
|             $disable_addon = $this->is_caused_by_addon( $exception ); | ||||
|             $message       = method_exists( $exception, 'get_html_message' ) | ||||
|                 ? $exception->get_html_message() | ||||
|                 : $exception->getMessage(); | ||||
|             $message = '<p>' . $message . '</p>'; | ||||
|             if ( $exception->display_backtrace() ) { | ||||
|                 $message .= $backtrace; | ||||
|             } | ||||
|             if ( null !== $disable_addon ) { | ||||
|                 include_once ABSPATH . 'wp-admin/includes/plugin.php'; | ||||
|                 // deactivate the plugin. Fire handlers to hide options. | ||||
|                 deactivate_plugins( $disable_addon ); | ||||
|                 global $ai1ec_registry; | ||||
|                 $ai1ec_registry->get( 'notification.admin' ) | ||||
|                     ->store( | ||||
|                         $this->get_disabled_line( $disable_addon ) . $message, | ||||
|                         'error', | ||||
|                         2, | ||||
|                         array( Ai1ec_Notification_Admin::RCPT_ADMIN ), | ||||
|                         true | ||||
|                     ); | ||||
|                 $this->redirect( $exception->get_redirect_url() ); | ||||
|             } else { | ||||
|                 // check if it has a methof for deatiled html | ||||
|                 $this->soft_deactivate_plugin( $message ); | ||||
|             } | ||||
|  | ||||
|         } | ||||
|         // if it's a PHP error in our plugin files, deactivate and redirect | ||||
|         else if ( $exception instanceof $this->_error_exception_class ) { | ||||
|             $this->soft_deactivate_plugin( | ||||
|                 $exception->getMessage() . $backtrace | ||||
|             ); | ||||
|         } | ||||
|         // if another handler was set, let it handle the exception | ||||
|         if ( is_callable( $this->_prev_ex_handler ) ) { | ||||
|             call_user_func( $this->_prev_ex_handler, $exception ); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Throws an Ai1ec_Error_Exception if the error comes from our plugin | ||||
|      * | ||||
|      * @param int    $errno      Error level as integer | ||||
|      * @param string $errstr     Error message raised | ||||
|      * @param string $errfile    File in which error was raised | ||||
|      * @param string $errline    Line in which error was raised | ||||
|      * @param array  $errcontext Error context symbols table copy | ||||
|      * | ||||
|      * @throws Ai1ec_Error_Exception If error originates from within Ai1EC | ||||
|      * | ||||
|      * @return boolean|void Nothing when error is ours, false when no | ||||
|      *                      other handler exists | ||||
|      */ | ||||
|     public function handle_error( | ||||
|         $errno, | ||||
|         $errstr, | ||||
|         $errfile, | ||||
|         $errline, | ||||
|         $errcontext = array() | ||||
|     ) { | ||||
|         // if the error is not in our plugin, let PHP handle things. | ||||
|         $position = strpos( $errfile, AI1EC_PLUGIN_NAME ); | ||||
|         if ( false === $position ) { | ||||
|             if ( is_callable( $this->_prev_er_handler ) ) { | ||||
|                 return call_user_func_array( | ||||
|                     $this->_prev_er_handler, | ||||
|                     func_get_args() | ||||
|                 ); | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         // do not disable plugin in production if the error is rather low | ||||
|         if ( | ||||
|             isset( $this->_nonfatal_errors[$errno] ) && ( | ||||
|                 ! defined( 'AI1EC_DEBUG' ) || false === AI1EC_DEBUG | ||||
|             ) | ||||
|         ) { | ||||
|             $message = sprintf( | ||||
|                 'All-in-One Event Calendar: %s @ %s:%d #%d', | ||||
|                 $errstr, | ||||
|                 $errfile, | ||||
|                 $errline, | ||||
|                 $errno | ||||
|             ); | ||||
|             return error_log( $message, 0 ); | ||||
|         } | ||||
|         // let's get the plugin folder | ||||
|         $tail = substr( $errfile, $position ); | ||||
|         $exploded = explode( DIRECTORY_SEPARATOR, $tail ); | ||||
|         $plugin_dir = $exploded[0]; | ||||
|         // if the error doesn't belong to core, throw the plugin exception to trigger disabling | ||||
|         // of the plugin in the exception handler | ||||
|         if ( AI1EC_PLUGIN_NAME !== $plugin_dir ) { | ||||
|             $exc = implode( | ||||
|                 array_map( | ||||
|                     array( $this, 'return_first_char' ), | ||||
|                     explode( '-', $plugin_dir ) | ||||
|                 ) | ||||
|             ); | ||||
|             // all plugins should implement an exception based on this convention | ||||
|             // which is the same convention we use for constants, only with just first letter uppercase | ||||
|             $exc = str_replace( 'aioec', 'Ai1ec', $exc ) . '_Exception'; | ||||
|             if ( class_exists( $exc ) ) { | ||||
|                 $message = sprintf( | ||||
|                     'All-in-One Event Calendar: %s @ %s:%d #%d', | ||||
|                     $errstr, | ||||
|                     $errfile, | ||||
|                     $errline, | ||||
|                     $errno | ||||
|                 ); | ||||
|                 throw new $exc( $message ); | ||||
|             } | ||||
|         } | ||||
|         throw new Ai1ec_Error_Exception( | ||||
|             $errstr, | ||||
|             $errno, | ||||
|             0, | ||||
|             $errfile, | ||||
|             $errline | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     public function return_first_char( $name ) { | ||||
|         return $name[0]; | ||||
|     } | ||||
|     /** | ||||
|      * Perform what's needed to deactivate the plugin softly | ||||
|      * | ||||
|      * @param string $message Error message to be displayed to admin | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      */ | ||||
|     protected function soft_deactivate_plugin( $message ) { | ||||
|         add_option( self::DB_DEACTIVATE_MESSAGE, $message ); | ||||
|         $this->redirect(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform what's needed to reactivate the plugin | ||||
|      * | ||||
|      * @return boolean Success | ||||
|      */ | ||||
|     public function reactivate_plugin() { | ||||
|         return delete_option( self::DB_DEACTIVATE_MESSAGE ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get message to be displayed to admin if any | ||||
|      * | ||||
|      * @return string|boolean Error message or false if plugin is not disabled | ||||
|      */ | ||||
|     public function get_disabled_message() { | ||||
|         global $wpdb; | ||||
|         $row = $wpdb->get_row( | ||||
|             $wpdb->prepare( | ||||
|                 "SELECT option_value FROM $wpdb->options WHERE option_name = %s LIMIT 1", | ||||
|                 self::DB_DEACTIVATE_MESSAGE | ||||
|             ) | ||||
|         ); | ||||
|         if ( is_object( $row ) ) { | ||||
|             return $row->option_value; | ||||
|         } else { // option does not exist, so we must cache its non-existence | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add an admin notice | ||||
|      * | ||||
|      * @param string $message Message to be displayed to admin | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      */ | ||||
|     public function show_notices( $message ) { | ||||
|         // save the message to use it later | ||||
|         $this->_message = $message; | ||||
|         add_action( 'admin_notices', array( $this, 'render_admin_notice' ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Render HTML snipped to be displayed as a notice to admin | ||||
|      * | ||||
|      * @hook admin_notices When plugin is soft-disabled | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      */ | ||||
|     public function render_admin_notice() { | ||||
|         $redirect_url = esc_url( add_query_arg( | ||||
|             self::DB_REACTIVATE_PLUGIN, | ||||
|             'true', | ||||
|             get_admin_url() | ||||
|         ) ); | ||||
|         $label = __( | ||||
|             'All-in-One Event Calendar has been disabled due to an error:', | ||||
|             AI1EC_PLUGIN_NAME | ||||
|         ); | ||||
|         $message  = '<div class="message error">'; | ||||
|         $message .= '<p><strong>' . $label . '</strong></p>'; | ||||
|         $message .= $this->_message; | ||||
|         $message .= ' <a href="' . $redirect_url . | ||||
|             '" class="button button-primary ai1ec-dismissable">' . | ||||
|             __( | ||||
|                 'Try reactivating plugin', | ||||
|                 AI1EC_PLUGIN_NAME | ||||
|             ); | ||||
|         $message .= '</a>'; | ||||
|         $message .= '<p></p></div>'; | ||||
|         echo $message; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Redirect the user either to the front page or the dashbord page | ||||
|      * | ||||
|      * @return void Method does not return | ||||
|      */ | ||||
|     protected function redirect( $suggested_url = null ) { | ||||
|         $url = ai1ec_get_site_url(); | ||||
|         if ( is_admin() ) { | ||||
|             $url = null !== $suggested_url | ||||
|                 ? $suggested_url | ||||
|                 : ai1ec_get_admin_url(); | ||||
|         } | ||||
|         Ai1ec_Http_Response_Helper::redirect( $url ); | ||||
|     } | ||||
|     /** | ||||
|      * Had to add it as var_dump was locking my browser. | ||||
|      * | ||||
|      * Taken from http://www.leaseweblabs.com/2013/10/smart-alternative-phps-var_dump-function/ | ||||
|      * | ||||
|      * @param mixed $variable | ||||
|      * @param int $strlen | ||||
|      * @param int $width | ||||
|      * @param int $depth | ||||
|      * @param int $i | ||||
|      * @param array $objects | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function var_debug( | ||||
|         $variable, | ||||
|         $strlen = 400, | ||||
|         $width = 25, | ||||
|         $depth = 10, | ||||
|         $i = 0, | ||||
|         &$objects = array() | ||||
|     ) { | ||||
|         $search  = array( "\0", "\a", "\b", "\f", "\n", "\r", "\t", "\v" ); | ||||
|         $replace = array( '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v' ); | ||||
|         $string  = ''; | ||||
|  | ||||
|         switch ( gettype( $variable ) ) { | ||||
|             case 'boolean' : | ||||
|                 $string .= $variable ? 'true' : 'false'; | ||||
|                 break; | ||||
|             case 'integer' : | ||||
|                 $string .= $variable; | ||||
|                 break; | ||||
|             case 'double' : | ||||
|                 $string .= $variable; | ||||
|                 break; | ||||
|             case 'resource' : | ||||
|                 $string .= '[resource]'; | ||||
|                 break; | ||||
|             case 'NULL' : | ||||
|                 $string .= "null"; | ||||
|                 break; | ||||
|             case 'unknown type' : | ||||
|                 $string .= '???'; | ||||
|                 break; | ||||
|             case 'string' : | ||||
|                 $len = strlen( $variable ); | ||||
|                 $variable = str_replace( | ||||
|                     $search, | ||||
|                     $replace, | ||||
|                     substr( $variable, 0, $strlen ), | ||||
|                     $count ); | ||||
|                 $variable = substr( $variable, 0, $strlen ); | ||||
|                 if ( $len < $strlen ) { | ||||
|                     $string .= '"' . $variable . '"'; | ||||
|                 } else { | ||||
|                     $string .= 'string(' . $len . '): "' . $variable . '"...'; | ||||
|                 } | ||||
|                 break; | ||||
|             case 'array' : | ||||
|                 $len = count( $variable ); | ||||
|                 if ( $i == $depth ) { | ||||
|                     $string .= 'array(' . $len . ') {...}'; | ||||
|                 } elseif ( ! $len) { | ||||
|                     $string .= 'array(0) {}'; | ||||
|                 } else { | ||||
|                     $keys    = array_keys( $variable ); | ||||
|                     $spaces  = str_repeat( ' ', $i * 2 ); | ||||
|                     $string .= "array($len)\n" . $spaces . '{'; | ||||
|                     $count   = 0; | ||||
|                     foreach ( $keys as $key ) { | ||||
|                         if ( $count == $width ) { | ||||
|                             $string .= "\n" . $spaces . "  ..."; | ||||
|                             break; | ||||
|                         } | ||||
|                         $string .= "\n" . $spaces . "  [$key] => "; | ||||
|                         $string .= $this->var_debug( | ||||
|                             $variable[$key], | ||||
|                             $strlen, | ||||
|                             $width, | ||||
|                             $depth, | ||||
|                             $i + 1, | ||||
|                             $objects | ||||
|                         ); | ||||
|                         $count ++; | ||||
|                     } | ||||
|                     $string .= "\n" . $spaces . '}'; | ||||
|                 } | ||||
|                 break; | ||||
|             case 'object': | ||||
|                 $id = array_search( $variable, $objects, true ); | ||||
|                 if ( $id !== false ) { | ||||
|                     $string .= get_class( $variable ) . '#' . ( $id + 1 ) . ' {...}'; | ||||
|                 } else if ( $i == $depth ) { | ||||
|                     $string .= get_class( $variable ) . ' {...}'; | ||||
|                 } else { | ||||
|                     $id = array_push( $objects, $variable ); | ||||
|                     $array = ( array ) $variable; | ||||
|                     $spaces = str_repeat( ' ', $i * 2 ); | ||||
|                     $string .= get_class( $variable ) . "#$id\n" . $spaces . '{'; | ||||
|                     $properties = array_keys( $array ); | ||||
|                     foreach ( $properties as $property ) { | ||||
|                         $name    = str_replace( "\0", ':', trim( $property ) ); | ||||
|                         $string .= "\n" . $spaces . "  [$name] => "; | ||||
|                         $string .= $this->var_debug( | ||||
|                             $array[$property], | ||||
|                             $strlen, | ||||
|                             $width, | ||||
|                             $depth, | ||||
|                             $i + 1, | ||||
|                             $objects | ||||
|                         ); | ||||
|                     } | ||||
|                     $string .= "\n" . $spaces . '}'; | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         if ( $i > 0 ) { | ||||
|             return $string; | ||||
|         } | ||||
|  | ||||
|         $backtrace = debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); | ||||
|         do { | ||||
|             $caller = array_shift( $backtrace ); | ||||
|         } while ( | ||||
|             $caller && | ||||
|             ! isset( $caller['file'] ) | ||||
|         ); | ||||
|         if ( $caller ) { | ||||
|             $string = $caller['file'] . ':' . $caller['line'] . "\n" . $string; | ||||
|         } | ||||
|  | ||||
|         echo nl2br( str_replace( ' ', ' ', htmlentities( $string ) ) ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get HTML code with backtrace information for given exception. | ||||
|      * | ||||
|      * @param Exception $exception | ||||
|      * | ||||
|      * @return string HTML code. | ||||
|      */ | ||||
|     protected function _get_backtrace( $exception ) { | ||||
|         $backtrace = ''; | ||||
|         $trace     = nl2br( $exception->getTraceAsString() ); | ||||
|         $ident     = sha1( $trace ); | ||||
|         if ( ! empty( $trace ) ) { | ||||
|             $request_uri = ''; | ||||
|             if ( isset( $_SERVER['REQUEST_URI'] ) ) { | ||||
|                 // Remove all whitespaces | ||||
|                 $request_uri = preg_replace( '/\s+/', '', $_SERVER['REQUEST_URI'] ); | ||||
|                 // Convert request URI and strip tags | ||||
|                 $request_uri  = strip_tags( htmlspecialchars_decode( $request_uri ) ); | ||||
|                 // Limit URL to 100 characters | ||||
|                 $request_uri = substr($request_uri, 0, 100); | ||||
|             } | ||||
|             $button_label = __( 'Toggle error details', AI1EC_PLUGIN_NAME ); | ||||
|             $title        = __( 'Error Details:', AI1EC_PLUGIN_NAME ); | ||||
|             $backtrace    = <<<JAVASCRIPT | ||||
|             <script type="text/javascript"> | ||||
|             jQuery( function($) { | ||||
|                 $( "a[data-rel='$ident']" ).click( function() { | ||||
|                     jQuery( "#ai1ec-error-$ident" ).slideToggle( "fast" ); | ||||
|                     return false; | ||||
|                 }); | ||||
|             }); | ||||
|             </script> | ||||
|             <blockquote id="ai1ec-error-$ident" style="display: none;"> | ||||
|                 <strong>$title</strong> | ||||
|                 <p>$trace</p> | ||||
|                 <p>Request Uri: $request_uri</p> | ||||
|             </blockquote> | ||||
|             <a href="#" data-rel="$ident" class="button">$button_label</a> | ||||
| JAVASCRIPT; | ||||
|         } | ||||
|         return $backtrace; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,68 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * A factory class for events. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Factory | ||||
|  */ | ||||
| class Ai1ec_Factory_Event extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var bool whether the theme is legacy | ||||
|      */ | ||||
|     protected $_legacy; | ||||
|  | ||||
|     /** | ||||
|      * Public constructor | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_legacy = $registry->get( 'theme.loader' )->is_legacy_theme(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Factory method for events | ||||
|      * | ||||
|      * @param string $data | ||||
|      * @param string $instance | ||||
|      * | ||||
|      * @return Ai1ec_Event | ||||
|      */ | ||||
|     public function create_event_instance( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         $data     = null, | ||||
|         $instance = false | ||||
|     ) { | ||||
|         $use_backward_compatibility = $registry->get( | ||||
|             'compatibility.check' | ||||
|         )->use_backward_compatibility(); | ||||
|         if ( | ||||
|             $use_backward_compatibility && | ||||
|             true === $this->_legacy | ||||
|         ) { | ||||
|             return new Ai1ec_Event_Legacy( | ||||
|                 $registry, | ||||
|                 $data, | ||||
|                 $instance | ||||
|             ); | ||||
|         } | ||||
|         $class_name = 'Ai1ec_Event'; | ||||
|         if ( | ||||
|             $use_backward_compatibility && | ||||
|             'Ai1ec_Event' === $class_name | ||||
|         ) { | ||||
|             $class_name = 'Ai1ec_Event_Compatibility'; | ||||
|         } | ||||
|         return new $class_name( | ||||
|             $registry, | ||||
|             $data, | ||||
|             $instance | ||||
|         ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,294 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * A factory class for html elements | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Factory | ||||
|  */ | ||||
| class Ai1ec_Factory_Html extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     protected $pretty_permalinks_enabled = false; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $page; | ||||
|  | ||||
|     /** | ||||
|      * The contructor method. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $registry | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry | ||||
|      ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $app = $registry->get( 'bootstrap.registry.application' ); | ||||
|         $this->page = $app->get( 'calendar_base_page' ); | ||||
|         $this->pretty_permalinks_enabled = $app->get( 'permalinks_enabled' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates an instance of the class which generates href for links. | ||||
|      * | ||||
|      * @param array $args | ||||
|      * @param string $type | ||||
|      * | ||||
|      * @return Ai1ec_Href_Helper | ||||
|      */ | ||||
|     public function create_href_helper_instance( array $args, $type = 'normal' ) { | ||||
|         $href = new Ai1ec_Html_Element_Href( $args, $this->page ); | ||||
|         $href->set_pretty_permalinks_enabled( $this->pretty_permalinks_enabled ); | ||||
|         switch ( $type ) { | ||||
|             case 'category': | ||||
|                 $href->set_is_category( true ); | ||||
|                 break; | ||||
|             case 'tag': | ||||
|                 $href->set_is_tag( true ); | ||||
|                 break; | ||||
|             case 'author': | ||||
|                 $href->set_is_author( true ); | ||||
|                 break; | ||||
|             default: | ||||
|                 break; | ||||
|         } | ||||
|         return $href; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Create the html element used as the UI control for the datepicker button. | ||||
|      * The href must keep only active filters. | ||||
|      * | ||||
|      * @param array           $args         Populated args for the view | ||||
|      * @param int|string|null $initial_date The datepicker's initially set date | ||||
|      * @param string          $title        Title to display in datepicker button | ||||
|      * @param string          $title_short  Short names in title | ||||
|      * @return Ai1ec_Generic_Html_Tag | ||||
|      */ | ||||
|     public function create_datepicker_link( | ||||
|         array $args, $initial_date = null, $title = '', $title_short = '' | ||||
|     ) { | ||||
|         $settings    = $this->_registry->get( 'model.settings' ); | ||||
|         $date_system = $this->_registry->get( 'date.system' ); | ||||
|  | ||||
|         $date_format_pattern = $date_system->get_date_pattern_by_key( | ||||
|             $settings->get( 'input_date_format' ) | ||||
|         ); | ||||
|  | ||||
|         if ( null === $initial_date ) { | ||||
|             // If exact_date argument was provided, use its value to initialize | ||||
|             // datepicker. | ||||
|             if ( isset( $args['exact_date'] ) && | ||||
|                 $args['exact_date'] !== false && | ||||
|                 $args['exact_date'] !== null ) { | ||||
|                 $initial_date = $args['exact_date']; | ||||
|             } | ||||
|             // Else default to today's date. | ||||
|             else { | ||||
|                 $initial_date = $date_system->current_time(); | ||||
|             } | ||||
|         } | ||||
|         // Convert initial date to formatted date if required. | ||||
|         if ( Ai1ec_Validation_Utility::is_valid_time_stamp( $initial_date ) ) { | ||||
|             $initial_date = $date_system->format_date( | ||||
|                 $initial_date, | ||||
|                 $settings->get( 'input_date_format' ) | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         $href_args = array( | ||||
|             'action'     => $args['action'], | ||||
|             'cat_ids'    => $args['cat_ids'], | ||||
|             'tag_ids'    => $args['tag_ids'], | ||||
|             'exact_date' => "__DATE__", | ||||
|         ); | ||||
|         $href_args = apply_filters( | ||||
|             'ai1ec_date_picker_href_args', | ||||
|             $href_args, | ||||
|             $args | ||||
|         ); | ||||
|         $data_href = $this->create_href_helper_instance( $href_args ); | ||||
|  | ||||
|         $attributes = array( | ||||
|             'data-date' => $initial_date, | ||||
|             'data-date-format' => $date_format_pattern, | ||||
|             'data-date-weekstart' => $settings->get( 'week_start_day' ), | ||||
|             'href' => '#', | ||||
|             'data-href' => $data_href->generate_href(), | ||||
|             'data-lang' => str_replace( '_', '-', get_locale() ), | ||||
|         ); | ||||
|         $loader = $this->_registry->get( 'theme.loader' ); | ||||
|         $file = $loader->get_file( 'date-icon.png' ); | ||||
|  | ||||
|         $args = array( | ||||
|             'attributes'  => $attributes, | ||||
|             'data_type'   => $args['data_type'], | ||||
|             'icon_url'    => $file->get_url(), | ||||
|             'text_date'   => __( 'Choose a date using calendar', AI1EC_PLUGIN_NAME ), | ||||
|             'title'       => $title, | ||||
|             'title_short' => $title_short, | ||||
|         ); | ||||
|  | ||||
|         return $loader->get_file( 'datepicker_link.twig', $args ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a select2 Multiselect. | ||||
|      * | ||||
|      * @param array $args      The arguments for the select. | ||||
|      * @param array $options   The options of the select | ||||
|      * @param array $view_args The args used in the front end. | ||||
|      * | ||||
|      * @return Ai1ec_File_Twig | ||||
|      * | ||||
|      * @staticvar $cached_flips    Maps of taxonomy identifiers. | ||||
|      * @staticvar $checkable_types Map of types and taxonomy identifiers. | ||||
|      */ | ||||
|     public function create_select2_multiselect( | ||||
|         array $args, | ||||
|         array $options, | ||||
|         array $view_args = null | ||||
|     ) { | ||||
|         // if no data is present and we are in the frontend, return a blank | ||||
|         // element. | ||||
|         if ( empty( $options ) && null !== $view_args ) { | ||||
|             return $this->_registry->get( 'html.element.legacy.blank' ); | ||||
|         } | ||||
|         static $cached_flips    = array(); | ||||
|  | ||||
|         static $checkable_types = array( | ||||
|             'category' => 'cat_ids', | ||||
|             'tag'      => 'tag_ids', | ||||
|             'author'   => 'auth_ids', | ||||
|         ); | ||||
|  | ||||
|         $use_id         = isset( $args['use_id'] ); | ||||
|         $options_to_add = array(); | ||||
|         foreach ( $options as $term ) { | ||||
|             $option_arguments = array(); | ||||
|             $color            = false; | ||||
|             if ( $args['type'] === 'category' ) { | ||||
|                 $color = $this->_registry->get( 'model.taxonomy' ) | ||||
|                     ->get_category_color( $term->term_id ); | ||||
|             } | ||||
|             if ( $color ) { | ||||
|                 $option_arguments['data-color'] = $color; | ||||
|             } | ||||
|             if ( null !== $view_args ) { | ||||
|                 // create the href for ajax loading | ||||
|                 $href = $this->create_href_helper_instance( | ||||
|                     $view_args, | ||||
|                     $args['type'] | ||||
|                 ); | ||||
|                 $href->set_term_id( $term->term_id ); | ||||
|                 $option_arguments['data-href'] = $href->generate_href(); | ||||
|                 // check if the option is selected | ||||
|                 $type_to_check = ''; | ||||
|                 // first let's check the correct type | ||||
|                 if ( isset( $checkable_types[$args['type']] ) ) { | ||||
|                     $type_to_check = $checkable_types[$args['type']]; | ||||
|                 } | ||||
|                 // let's flip the array. Just once for performance sake, | ||||
|                 // the categories doesn't change in the same request | ||||
|                 if ( ! isset( $cached_flips[$type_to_check] ) ) { | ||||
|                     $cached_flips[$type_to_check] = array_flip( | ||||
|                         $view_args[$type_to_check] | ||||
|                     ); | ||||
|                 } | ||||
|                 if ( isset( $cached_flips[$type_to_check][$term->term_id] ) ) { | ||||
|                     $option_arguments['selected'] = 'selected'; | ||||
|                 } | ||||
|             } | ||||
|             if ( true === $use_id ) { | ||||
|                 $options_to_add[] = array( | ||||
|                     'text'  => $term->name, | ||||
|                     'value' => $term->term_id, | ||||
|                     'args'  => $option_arguments, | ||||
|                 ); | ||||
|             } else { | ||||
|                 $options_to_add[] = array( | ||||
|                     'text'  => $term->name, | ||||
|                     'value' => $term->name, | ||||
|                     'args'  => $option_arguments, | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         $select2_args = array( | ||||
|             'multiple'         => 'multiple', | ||||
|             'data-placeholder' => $args['placeholder'], | ||||
|             'class'            => 'ai1ec-select2-multiselect-selector span12' | ||||
|         ); | ||||
|         if ( isset( $args['class'] ) ) { | ||||
|             $select2_args['class'] .= ' ' . $args['class']; | ||||
|         } | ||||
|         $container_class = false; | ||||
|         if ( isset( $args['type'] ) ) { | ||||
|             $container_class = 'ai1ec-' . $args['type'] . '-filter'; | ||||
|         } | ||||
|         $loader  = $this->_registry->get( 'theme.loader' ); | ||||
|         $select2 = $loader->get_file( | ||||
|             'select2_multiselect.twig', | ||||
|             array( | ||||
|                 'name'            => $args['name'], | ||||
|                 'id'              => $args['id'], | ||||
|                 'container_class' => $container_class, | ||||
|                 'select2_args'    => $select2_args, | ||||
|                 'options'         => $options_to_add, | ||||
|             ), | ||||
|             true | ||||
|         ); | ||||
|         return $select2; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a select2 input. | ||||
|      * | ||||
|      * @param array $args The arguments of the input. | ||||
|      * | ||||
|      * @return Ai1ec_File_Twig | ||||
|      */ | ||||
|     public function create_select2_input( array $args ) { | ||||
|         if( ! isset ( $args['name'] ) ) { | ||||
|             $args['name'] = $args['id']; | ||||
|         } | ||||
|         // Get tags. | ||||
|         $tags = get_terms( | ||||
|             'events_tags', | ||||
|             array( | ||||
|                 'orderby' => 'name', | ||||
|                 'hide_empty' => 0, | ||||
|             ) | ||||
|         ); | ||||
|  | ||||
|         // Build tags array to pass as JSON. | ||||
|         $tags_json = array(); | ||||
|         foreach ( $tags as $term ) { | ||||
|             $tags_json[] = $term->name; | ||||
|         } | ||||
|         $tags_json = json_encode( $tags_json ); | ||||
|         $tags_json = _wp_specialchars( $tags_json, 'single', 'UTF-8' ); | ||||
|         $loader =$this->_registry->get( 'theme.loader' ); | ||||
|         $select2_args = array( | ||||
|             'data-placeholder' => __( 'Tags (optional)', AI1EC_PLUGIN_NAME ), | ||||
|             'class'            => 'ai1ec-tags-selector span12', | ||||
|             'data-ai1ec-tags'  => $tags_json | ||||
|         ); | ||||
|         $select2 = $loader->get_file( | ||||
|             'select2_input.twig', | ||||
|             array( | ||||
|                 'name'            => $args['name'], | ||||
|                 'id'              => $args['id'], | ||||
|                 'select2_args'    => $select2_args, | ||||
|  | ||||
|             ), | ||||
|             true | ||||
|         ); | ||||
|         return $select2; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,110 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * A factory class for caching strategy. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Factory | ||||
|  */ | ||||
| class Ai1ec_Factory_Strategy extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * create_cache_strategy_instance method | ||||
|      * | ||||
|      * Method to instantiate new cache strategy object | ||||
|      * | ||||
|      * @param string $cache_dirs Cache directory to use | ||||
|      * @param array   $skip_small_bits Set to true, to ignore small entities | ||||
|      *                                cache engines, as APC [optional=false] | ||||
|      * | ||||
|      * @return Ai1ec_Cache_Strategy Instantiated writer | ||||
|      */ | ||||
|     public function create_cache_strategy_instance( | ||||
|         $cache_dirs = null, | ||||
|         $skip_small_entities_cache = false | ||||
|     ) { | ||||
|         $engine = null; | ||||
|         $name   = ''; | ||||
|         if ( false === $skip_small_entities_cache && Ai1ec_Cache_Strategy_Apc::is_available() ) { | ||||
|             $engine = $this->_registry->get( 'cache.strategy.apc' ); | ||||
|         } else if ( | ||||
|             false === AI1EC_DISABLE_FILE_CACHE && | ||||
|             null !== $cache_dirs && | ||||
|             $cache_dir = $this->_get_writable_cache_dir( $cache_dirs ) | ||||
|         ) { | ||||
|             $engine = $this->_registry->get( 'cache.strategy.file', $cache_dir ); | ||||
|         } else { | ||||
|             $engine = $this->_registry->get( | ||||
|                 'cache.strategy.db', | ||||
|                 $this->_registry->get( 'model.option' ) | ||||
|             ); | ||||
|         } | ||||
|         return $engine; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * create_persistence_context method | ||||
|      * | ||||
|      * @param string $key_for_persistance | ||||
|      * @param string $cache_dirs | ||||
|      * @param bool   $skip_small_entities_cache | ||||
|      * | ||||
|      * @return Ai1ec_Persistence_Context Instance of persistance context | ||||
|      */ | ||||
|     public function create_persistence_context( | ||||
|         $key_for_persistance, | ||||
|         $cache_dirs = null, | ||||
|         $skip_small_entities_cache = false | ||||
|     ) { | ||||
|         return new Ai1ec_Persistence_Context( | ||||
|             $key_for_persistance, | ||||
|             $this->create_cache_strategy_instance( $cache_dirs, $skip_small_entities_cache ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get a writable directory if possible, falling back on wp_contet dir | ||||
|      * | ||||
|      * @param array $cache_dirs | ||||
|      * @return boolean|string | ||||
|      */ | ||||
|     protected function _get_writable_cache_dir( $cache_dirs ) { | ||||
|         $writable_folder = false; | ||||
|         foreach ( $cache_dirs as $cache_dir ) { | ||||
|             if ( $this->_is_cache_dir_writable( $cache_dir['path'] ) ) { | ||||
|                 $writable_folder = $cache_dir; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return $writable_folder; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * _is_cache_dir_writable method | ||||
|      * | ||||
|      * Check if given cache directory is writable. | ||||
|      * | ||||
|      * @param string $directory A path to check for writability | ||||
|      * | ||||
|      * @return bool Writability | ||||
|      */ | ||||
|     protected function _is_cache_dir_writable( $directory ) { | ||||
|         static $cache_directories = array(); | ||||
|         if ( ! isset( $cache_directories[$directory] ) ) { | ||||
|             $cache_directories[$directory] = apply_filters( | ||||
|                 'ai1ec_is_cache_dir_writable', | ||||
|                 null, | ||||
|                 $directory | ||||
|             ); | ||||
|             if ( null === $cache_directories[$directory] ) { | ||||
|                 $filesystem = $this->_registry->get( 'filesystem.checker' ); | ||||
|                 $cache_directories[$directory] = $filesystem->is_writable( | ||||
|                     $directory | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return $cache_directories[$directory]; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,156 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * A helper class for Filesystem checks. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Filesystem | ||||
|  */ | ||||
| class Ai1ec_Filesystem_Checker { | ||||
|  | ||||
|  | ||||
|     public function __construct() { | ||||
|         include_once ABSPATH . 'wp-admin/includes/file.php'; | ||||
|     } | ||||
|     /** | ||||
|      * check if the path is writable. To make the check . | ||||
|      * | ||||
|      * @param string $path | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_writable( $path ) { | ||||
|         global $wp_filesystem; | ||||
|  | ||||
|         // try without credentials | ||||
|         $writable = WP_Filesystem( false, $path ); | ||||
|         // We consider the directory as writable if it uses the direct transport, | ||||
|         // otherwise credentials would be needed | ||||
|         if ( true === $writable ) { | ||||
|             return true; | ||||
|         } | ||||
|         // if the user has FTP and sockets defined | ||||
|         if ( | ||||
|                 $this->is_ftp_or_sockets( $wp_filesystem->method ) && | ||||
|                 $this->are_ftp_constants_defined() | ||||
|         ) { | ||||
|             $creds = request_filesystem_credentials( '', $wp_filesystem->method, false, $path ); | ||||
|             $writable = WP_Filesystem( $creds, $path ); | ||||
|             if ( true === $writable ) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         if ( | ||||
|             $this->is_ssh( $wp_filesystem->method ) && | ||||
|             $this->are_ssh_constants_defined() | ||||
|         ) { | ||||
|             $creds = request_filesystem_credentials( '', $wp_filesystem->method, false, $path ); | ||||
|             $writable = WP_Filesystem( $creds, $path ); | ||||
|             if ( true === $writable ) { | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if method is ssh | ||||
|      * | ||||
|      * @param strin $method | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_ssh( $method ) { | ||||
|         return 'ssh2' === $method; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if method is ftp or sockets | ||||
|      * | ||||
|      * @param string $method | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function is_ftp_or_sockets( $method ) { | ||||
|         return 'ftpext' === $method || | ||||
|                 'ftpsockets' === $method; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if credentials for ssh are defined | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function are_ssh_constants_defined() { | ||||
|         return defined('FTP_HOST') && | ||||
|             defined('FTP_PUBKEY') && | ||||
|             defined('FTP_PRIKEY'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if credentials for ftp are defined | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function are_ftp_constants_defined() { | ||||
|         return defined('FTP_HOST') && | ||||
|                 defined('FTP_USER') && | ||||
|                 defined('FTP_PASS'); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a file using $wp_filesystem. | ||||
|      * | ||||
|      * @param string $file | ||||
|      * @param string $content | ||||
|      */ | ||||
|     public function put_contents( $file, $content ) { | ||||
|         global $wp_filesystem; | ||||
|         return $wp_filesystem->put_contents( | ||||
|             $file, | ||||
|             $content | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the content folder from Wordpress if available | ||||
|      * | ||||
|      * @return string the folder to use or '' | ||||
|      */ | ||||
|     public function get_ai1ec_static_dir_if_available() { | ||||
|         global $wp_filesystem; | ||||
|         // reset the filesystem to it's standard. | ||||
|         WP_Filesystem(); | ||||
|         $content_dir = $wp_filesystem->wp_content_dir() . DIRECTORY_SEPARATOR | ||||
|             . 'uploads' . DIRECTORY_SEPARATOR; | ||||
|         $static_dir = trailingslashit( $content_dir . 'ai1ec_static' ); | ||||
|         if ( | ||||
|             ! $wp_filesystem->is_dir( $static_dir ) && | ||||
|             ! $wp_filesystem->mkdir( $static_dir ) | ||||
|         ) { | ||||
|             return ''; | ||||
|         } | ||||
|         return $static_dir; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Check if specified file exists | ||||
|      * | ||||
|      * @return boolean | ||||
|      */ | ||||
|     public function check_file_exists( $file, $check_is_empty ) { | ||||
|         try { | ||||
|             if ( ! file_exists( $file ) ) { | ||||
|                 return false; | ||||
|             } else { | ||||
|                 if ( $check_is_empty && 0 == filesize( $file ) ) { | ||||
|                     return false; | ||||
|                 } else { | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } catch ( Exception $e ) { | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,186 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Miscellaneous file system related functions. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib.Filesystem | ||||
|  */ | ||||
| class Ai1ec_Filesystem_Misc extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Builds directory hashmap. | ||||
|      * | ||||
|      * @param array|string $paths      Paths for hashmap generation. It accepts | ||||
|      *                                 string or array of paths. Elements in | ||||
|      *                                 hashmaps are not overwritten. | ||||
|      * @param array        $exclusions List of excluded file names. | ||||
|      * | ||||
|      * @return array Hashmap. | ||||
|      */ | ||||
|     public function build_dirs_hashmap( $paths, $exclusions = array() ) { | ||||
|         if ( ! is_array( $paths ) ) { | ||||
|             $paths = array( $paths ); | ||||
|         } | ||||
|         $hashmap = array(); | ||||
|         foreach ( $paths as $path ) { | ||||
|             if ( file_exists( $path ) ) { | ||||
|                 $hashmap += $this->build_dir_hashmap( $path, $exclusions ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         ksort( $hashmap ); | ||||
|  | ||||
|         return $hashmap; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Builds hashmap for given directory. | ||||
|      * | ||||
|      * @param string $directory  Directory for hashmap creation. | ||||
|      * @param array  $exclusions List of excluded file names. | ||||
|      * | ||||
|      * @return array Hashmap. | ||||
|      */ | ||||
|     public function build_dir_hashmap( $directory, $exclusions = array() ) { | ||||
|         $directory_iterator = new RecursiveDirectoryIterator( | ||||
|             $directory | ||||
|         ); | ||||
|         $recursive_iterator = new RecursiveIteratorIterator( | ||||
|             $directory_iterator | ||||
|         ); | ||||
|         $files              = new RegexIterator( | ||||
|             $recursive_iterator, | ||||
|             '/^.+\.(less|css|php)$/i', | ||||
|             RegexIterator::GET_MATCH | ||||
|         ); | ||||
|         $hashmap            = array(); | ||||
|         foreach ( $files as $file ) { | ||||
|             $file_info = new SplFileInfo( $file[0] ); | ||||
|             $file_path = $file_info->getPathname(); | ||||
|             if ( in_array( $file_info->getFilename(), $exclusions ) ) { | ||||
|                 continue; | ||||
|             } | ||||
|             $key = str_replace( | ||||
|                 array( $directory, '/' ), | ||||
|                 array( '', '\\' ), | ||||
|                 $file_path | ||||
|             ); | ||||
|  | ||||
|             $hashmap[ $key ] = array( | ||||
|                 'size' => $file_info->getSize(), | ||||
|                 'sha1' => sha1_file( $file_path ), | ||||
|             ); | ||||
|         } | ||||
|         ksort( $hashmap ); | ||||
|  | ||||
|         return $hashmap; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns hashmap for current theme. | ||||
|      * | ||||
|      * @return mixed|null Hashmap or null if none. | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      */ | ||||
|     public function get_current_theme_hashmap() { | ||||
|         $cur_theme     = $this->_registry->get( 'model.option' )->get( 'ai1ec_current_theme' ); | ||||
|  | ||||
|         if ( ! $cur_theme || ( isset( $cur_theme['stylesheet'] ) && 'saas' === $cur_theme['stylesheet'] ) ) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         $file_location = $cur_theme['theme_dir'] . DIRECTORY_SEPARATOR . 'less.sha1.map.php'; | ||||
|         if ( ! file_exists( $file_location ) || ! is_readable( $file_location ) || ! @file_get_contents( $file_location ) ) { | ||||
|             // Delete theme options | ||||
|             $this->_registry->get( 'model.option' )->delete( 'ai1ec_current_theme' ); | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         return require $file_location; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Builds file hashmap for current theme. | ||||
|      * | ||||
|      * @return array Hashmap. | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      * @throws Ai1ec_Invalid_Argument_Exception | ||||
|      */ | ||||
|     public function build_current_theme_hashmap() { | ||||
|         $paths = $this->_registry->get( 'theme.loader' )->get_paths(); | ||||
|  | ||||
|         return $this->build_dirs_hashmap( | ||||
|             array_keys( | ||||
|                 $paths['theme'] | ||||
|             ), | ||||
|             array( | ||||
|                 'ai1ec_parsed_css.css', | ||||
|                 'less.sha1.map.php', | ||||
|                 'index.php', | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns theme structrure for one of core themes. | ||||
|      * | ||||
|      * @param string $stylesheet Theme stylesheet. Expected one of | ||||
|      *                           ['plana','vortex','umbra','gamma']. | ||||
|      * | ||||
|      * @return array Theme structure | ||||
|      * | ||||
|      * @throws Ai1ec_Invalid_Argument_Exception | ||||
|      */ | ||||
|     public function build_theme_structure( $stylesheet ) { | ||||
|         $themes = array( 'plana', 'vortex', 'umbra', 'gamma' ); | ||||
|         if ( ! in_array( $stylesheet, $themes ) ) { | ||||
|             throw new Ai1ec_Invalid_Argument_Exception( | ||||
|                 'Theme ' . $stylesheet . ' compilation is not supported.' | ||||
|             ); | ||||
|         } | ||||
|         $root = AI1EC_PATH . DIRECTORY_SEPARATOR . 'public' . | ||||
|                 DIRECTORY_SEPARATOR . AI1EC_THEME_FOLDER; | ||||
|  | ||||
|         return array( | ||||
|             'theme_root' => $root, | ||||
|             'theme_dir'  => $root . DIRECTORY_SEPARATOR . $stylesheet, | ||||
|             'theme_url'  => AI1EC_URL . '/public/' . AI1EC_THEME_FOLDER . '/' . $stylesheet, | ||||
|             'stylesheet' => $stylesheet, | ||||
|             'legacy'     => false, | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Compares files hashmaps. If $src key doesn't exist in $dst, it's just | ||||
|      * ommited. This is intended for LESS compilation check. Current theme | ||||
|      * may contain more LESS files than base one, what does not matter as | ||||
|      * other files should be changed accordingly. | ||||
|      * | ||||
|      * @param array $src Source hashmap. Should be computed from current | ||||
|      *                   theme contents. | ||||
|      * @param array $dst Base hashmap. Should be taken from less.sha1.map.php | ||||
|      *                   file. | ||||
|      * | ||||
|      * @return bool Comparision result. True if they are equal. | ||||
|      */ | ||||
|     public function compare_hashmaps( array $src, array $dst ) { | ||||
|         foreach ( $src as $key => $value ) { | ||||
|             if ( ! isset( $dst[ $key ] ) ) { | ||||
|                 continue; | ||||
|             } | ||||
|             $dst_value = $dst[ $key ]; | ||||
|             if ( $dst_value !== $value ) { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,241 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Define global functions | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.0 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC.Lib | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Always return false for action/filter hooks | ||||
|  * | ||||
|  * @return boolean | ||||
|  */ | ||||
| function ai1ec_return_false() { | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Executed after initialization of Front Controller. | ||||
|  * | ||||
|  * @return void | ||||
|  */ | ||||
| function ai1ec_start() { | ||||
|     ob_start(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Executed before script shutdown, when WP core objects are present. | ||||
|  * | ||||
|  * @return void | ||||
|  */ | ||||
| function ai1ec_stop() { | ||||
|     if ( ob_get_level() ) { | ||||
|         echo ob_get_clean(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Create `<pre>` wrapped variable dump. | ||||
|  * | ||||
|  * @param mixed $var Arbitrary value to dump. | ||||
|  * | ||||
|  * @return void | ||||
|  */ | ||||
| function ai1ec_dump( $var ) { | ||||
|     if ( ! defined( 'AI1EC_DEBUG' ) || ! AI1EC_DEBUG ) { | ||||
|         return null; | ||||
|     } | ||||
|     echo '<pre>'; | ||||
|     var_dump( $var ); | ||||
|     echo '</pre>'; | ||||
|     exit( 0 ); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Indicate deprecated function. | ||||
|  * | ||||
|  * @param string $function Name of called function. | ||||
|  * | ||||
|  * @return void | ||||
|  */ | ||||
| function ai1ec_deprecated( $function ) { | ||||
|     trigger_error( | ||||
|         'Function \'' . $function . '\' is deprecated.', | ||||
|         E_USER_WARNING | ||||
|     ); | ||||
| } | ||||
|  | ||||
| /* (non-PHPdoc) | ||||
|  * @see admin_url() | ||||
|  */ | ||||
| function ai1ec_admin_url( $path = '', $scheme = 'admin' ) { | ||||
|     if ( ai1ec_is_ssl_forced() ) { | ||||
|         $scheme = 'https'; | ||||
|     } | ||||
|     return admin_url( $path, $scheme ); | ||||
| } | ||||
|  | ||||
| /* (non-PHPdoc) | ||||
|  * @see get_admin_url() | ||||
|  */ | ||||
| function ai1ec_get_admin_url( $blog_id = null, $path = '', $scheme = 'admin' ) { | ||||
|     if ( ai1ec_is_ssl_forced() ) { | ||||
|         $scheme = 'https'; | ||||
|     } | ||||
|     return get_admin_url( $blog_id, $path, $scheme ); | ||||
| } | ||||
|  | ||||
| /* (non-PHPdoc) | ||||
|  * @see get_site_url() | ||||
|  */ | ||||
| function ai1ec_get_site_url( $blog_id = null, $path = '', $scheme = null ) { | ||||
|     if ( ai1ec_is_ssl_forced() ) { | ||||
|         $scheme = 'https'; | ||||
|     } | ||||
|     return get_site_url( $blog_id, $path, $scheme ); | ||||
| } | ||||
|  | ||||
| /* (non-PHPdoc) | ||||
|  * @see site_url() | ||||
|  */ | ||||
| function ai1ec_site_url( $path = '', $scheme = null ) { | ||||
|     if ( ai1ec_is_ssl_forced() ) { | ||||
|         $scheme = 'https'; | ||||
|     } | ||||
|     return site_url( $path, $scheme ); | ||||
| } | ||||
|  | ||||
| /* (non-PHPdoc) | ||||
|  * @see network_admin_url() | ||||
|  */ | ||||
| function ai1ec_network_admin_url( $path = '', $scheme = 'admin' ) { | ||||
|     if ( ai1ec_is_ssl_forced() ) { | ||||
|         $scheme = 'https'; | ||||
|     } | ||||
|     return network_admin_url( $path, $scheme ); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Returns whether SSL URLs are forced or not. | ||||
|  * | ||||
|  * @return bool Result. | ||||
|  */ | ||||
| function ai1ec_is_ssl_forced() { | ||||
|     return ( | ||||
|         is_admin() && | ||||
|         ( | ||||
|             class_exists( 'WordPressHTTPS' ) || | ||||
|             ( | ||||
|                 defined( 'FORCE_SSL_ADMIN' ) && | ||||
|                 true === FORCE_SSL_ADMIN | ||||
|             ) | ||||
|         ) | ||||
|     ); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Check if an string is empty. | ||||
|  * @return bool result | ||||
|  */ | ||||
| function ai1ec_is_blank( $value ) { | ||||
|     if ( null === $value || false === isset( $value ) ) { | ||||
|         return true; | ||||
|     } else { | ||||
|          if ( is_string( $value ) ) { | ||||
|              return strlen( trim( $value ) ) <= 0; | ||||
|          } else if ( is_array( $value ) ) { | ||||
|              return count( $value ) <= 0; | ||||
|          } else { | ||||
|              return false; | ||||
|          } | ||||
|     } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Date parser for PHP <= 5.2 | ||||
|  * | ||||
|  * Source: http://stackoverflow.com/questions/6668223/php-date-parse-from-format-alternative-in-php-5-2 | ||||
|  * | ||||
|  * Modified to always populate hour, minute and second. | ||||
|  * | ||||
|  */ | ||||
| if ( ! function_exists( 'date_parse_from_format' ) ) { | ||||
|     function date_parse_from_format( $format, $date ) { | ||||
|         // reverse engineer date formats | ||||
|         $keys = array( | ||||
|             'Y' => array( 'year',   '\d{4}' ), | ||||
|             'y' => array( 'year',   '\d{2}' ), | ||||
|             'm' => array( 'month',  '\d{2}' ), | ||||
|             'n' => array( 'month',  '\d{1,2}' ), | ||||
|             'M' => array( 'month',  '[A-Z][a-z]{3}' ), | ||||
|             'F' => array( 'month',  '[A-Z][a-z]{2,8}' ), | ||||
|             'd' => array( 'day',    '\d{2}' ), | ||||
|             'j' => array( 'day',    '\d{1,2}' ), | ||||
|             'D' => array( 'day',    '[A-Z][a-z]{2}' ), | ||||
|             'l' => array( 'day',    '[A-Z][a-z]{6,9}' ), | ||||
|             'u' => array( 'hour',   '\d{1,6}' ), | ||||
|             'h' => array( 'hour',   '\d{2}' ), | ||||
|             'H' => array( 'hour',   '\d{2}' ), | ||||
|             'g' => array( 'hour',   '\d{1,2}' ), | ||||
|             'G' => array( 'hour',   '\d{1,2}' ), | ||||
|             'i' => array( 'minute', '\d{2}' ), | ||||
|             's' => array( 'second', '\d{2}' ) | ||||
|         ); | ||||
|  | ||||
|         // convert format string to regex | ||||
|         $regex = ''; | ||||
|         $chars = str_split( $format ); | ||||
|         foreach ( $chars AS $n => $char ) { | ||||
|             $lastChar = isset( $chars[$n - 1] ) ? $chars[$n - 1] : ''; | ||||
|             $skipCurrent = '\\' == $lastChar; | ||||
|             if ( !$skipCurrent && isset( $keys[$char] ) ) { | ||||
|                 $regex .= '(?P<' . $keys[$char][0] . '>' . $keys[$char][1] . ')'; | ||||
|             } else if ( '\\' == $char ) { | ||||
|                 $regex .= $char; | ||||
|             } else { | ||||
|                 $regex .= preg_quote( $char ); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         $dt = array(); | ||||
|         // now try to match it | ||||
|         if ( preg_match( '#^' . $regex . '$#', $date, $dt ) ) { | ||||
|             foreach ( $dt AS $k => $v ) { | ||||
|                 if ( is_int( $k ) ) { | ||||
|                     unset( $dt[$k] ); | ||||
|                 } | ||||
|             } | ||||
|             if ( ! checkdate( $dt['month'], $dt['day'], $dt['year'] ) ) { | ||||
|                 $dt['error_count'] = 1; | ||||
|             } else { | ||||
|                 $dt['error_count'] = 0; | ||||
|             } | ||||
|             if ( ! isset( $dt['hour'] ) ) { | ||||
|                 $dt['hour'] = 0; | ||||
|             } | ||||
|             if ( ! isset( $dt['minute'] ) ) { | ||||
|                 $dt['minute'] = 0; | ||||
|             } | ||||
|             if ( ! isset( $dt['second'] ) ) { | ||||
|                 $dt['second'] = 0; | ||||
|             } | ||||
|         } else { | ||||
|             $dt['error_count'] = 1; | ||||
|         } | ||||
|         $dt['errors']        = array(); | ||||
|         $dt['fraction']      = ''; | ||||
|         $dt['warning_count'] = 0; | ||||
|         $dt['warnings']      = array(); | ||||
|         $dt['is_localtime']  = 0; | ||||
|         $dt['zone_type']     = 0; | ||||
|         $dt['zone']          = 0; | ||||
|         $dt['is_dst']        = ''; | ||||
|  | ||||
|         return $dt; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,268 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * This class handles generations of href for links. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @instantiator Ai1ec_Factory_Html.create_href_helper_instance | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Html_Element_Href { | ||||
|  | ||||
|     /** | ||||
|      * @var array the parameters that are used in the urls | ||||
|      */ | ||||
|     private $used_paramaters = array( | ||||
|         'action', | ||||
|         'page_offset', | ||||
|         'month_offset', | ||||
|         'oneday_offset', | ||||
|         'week_offset', | ||||
|         'time_limit', | ||||
|         'exact_date', | ||||
|         'cat_ids', | ||||
|         'auth_ids', | ||||
|         'post_ids', | ||||
|         'tag_ids', | ||||
|         'instance_ids', | ||||
|         'events_limit', | ||||
|         'request_format', | ||||
|         'no_navigation' | ||||
|     ); | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_category; | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_tag; | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_author; | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $is_custom_filter; | ||||
|  | ||||
|     /** | ||||
|      * @var array the arguments to parse | ||||
|      */ | ||||
|     private $args; | ||||
|  | ||||
|     /** | ||||
|      * @var int | ||||
|      */ | ||||
|     private $term_id; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $calendar_page; | ||||
|  | ||||
|     /** | ||||
|      * @var boolean | ||||
|      */ | ||||
|     private $pretty_permalinks_enabled; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $uri_particle = null; | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $pretty_permalinks_enabled | ||||
|      */ | ||||
|     public function set_pretty_permalinks_enabled( $pretty_permalinks_enabled ) { | ||||
|         $this->pretty_permalinks_enabled = $pretty_permalinks_enabled; | ||||
|         if ( $pretty_permalinks_enabled ) { | ||||
|             $this->calendar_page = trim( (string)$this->calendar_page, '/' ) | ||||
|                 . '/'; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param number $term_id | ||||
|      */ | ||||
|     public function set_term_id( $term_id ) { | ||||
|         $this->term_id = $term_id; | ||||
|     } | ||||
|  | ||||
|     public function __construct( array $args, $calendar ) { | ||||
|         $this->args = $args; | ||||
|         $this->calendar_page = $calendar; | ||||
|         if ( isset( $args['_extra_used_parameters'] ) ) { | ||||
|             $this->used_paramaters = array_merge( | ||||
|                 $this->used_paramaters, | ||||
|                 $args['_extra_used_parameters'] | ||||
|             ); | ||||
|         } | ||||
|         $this->used_paramaters = array_merge( | ||||
|             $this->used_paramaters, | ||||
|             apply_filters( | ||||
|                 'ai1ec_view_args_for_view', | ||||
|                 array() | ||||
|             ) | ||||
|         ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $is_category | ||||
|      */ | ||||
|     public function set_is_category( $is_category ) { | ||||
|         $this->is_category = $is_category; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $is_tag | ||||
|      */ | ||||
|     public function set_is_tag( $is_tag ) { | ||||
|         $this->is_tag = $is_tag; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param boolean $is_author | ||||
|      */ | ||||
|     public function set_is_author( $is_author ) { | ||||
|         $this->is_author = $is_author; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate the correct href for the view. | ||||
|      * This takes into account special filters for categories and tags | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function generate_href() { | ||||
|         $href = ''; | ||||
|         $to_implode = array(); | ||||
|         foreach ( $this->used_paramaters as $key ) { | ||||
|             if ( ! empty( $this->args[$key] ) ) { | ||||
|                 $value = $this->args[$key]; | ||||
|                 if( is_array( $this->args[$key] ) ) { | ||||
|                     $value = implode( ',', $this->args[$key] ); | ||||
|                 } | ||||
|                 $to_implode[$key] = $key . Ai1ec_Uri::DIRECTION_SEPARATOR . | ||||
|                     $value; | ||||
|             } | ||||
|         } | ||||
|         if ( | ||||
|             $this->is_category || | ||||
|             $this->is_tag || | ||||
|             $this->is_author || | ||||
|             $this->is_custom_filter | ||||
|         ) { | ||||
|             $to_implode = $this->add_or_remove_category_from_href( | ||||
|                 $to_implode | ||||
|             ); | ||||
|         } | ||||
|         if ( $this->pretty_permalinks_enabled ) { | ||||
|             $href .= implode( '/', $to_implode ); | ||||
|             if ( ! empty( $href ) ) { | ||||
|                 $href .=  '/'; | ||||
|             } | ||||
|         } else { | ||||
|             $href .= $this->get_param_delimiter_char( $this->calendar_page ); | ||||
|             $href .= 'ai1ec=' . implode( '|', $to_implode ); | ||||
|         } | ||||
|         $full_url = $this->calendar_page . $href; | ||||
|         // persist the `lang` parameter if present | ||||
|         if ( isset( $_REQUEST['lang'] ) ) { | ||||
|             $full_url = esc_url_raw( add_query_arg( 'lang', $_REQUEST['lang'], $full_url ) ); | ||||
|         } | ||||
|         return $full_url; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets that class is used for custom filter. | ||||
|      * | ||||
|      * @param bool   $value        Expected true or false. | ||||
|      * @param string $uri_particle URI particle identifier. | ||||
|      * | ||||
|      * @return void Method does not return. | ||||
|      */ | ||||
|     public function set_custom_filter( $value, $uri_particle = null ) { | ||||
|         $this->is_custom_filter = $value; | ||||
|         $this->uri_particle     = $uri_particle; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Perform some extra manipulation for filter href. Basically if the current | ||||
|      * category is part of the filter, the href will not contain it (because | ||||
|      * clicking on it will actually mean "remove that one from the filter") | ||||
|      * otherwise it will be preserved. | ||||
|      * | ||||
|      * @param array $to_implode | ||||
|      * @return array | ||||
|      */ | ||||
|     private function add_or_remove_category_from_href( array $to_implode ) { | ||||
|         $array_key = $this->uri_particle; | ||||
|         if ( null === $this->uri_particle ) { | ||||
|             $array_key = $this->_current_array_key(); | ||||
|         } | ||||
|         // Let's copy the origina cat_ids or tag_ids so we do not affect it | ||||
|         $copy      = array(); | ||||
|         if ( isset( $this->args[$array_key] ) ) { | ||||
|             $copy  = (array)$this->args[$array_key]; | ||||
|         } | ||||
|         $key       = array_search( $this->term_id, $copy ); | ||||
|         // Let's check if we are already filtering for tags / categorys | ||||
|         if( isset( $to_implode[$array_key] ) ) { | ||||
|             if( $key !== false ) { | ||||
|                 unset( $copy[$key] ); | ||||
|             } else { | ||||
|                 $copy[] = $this->term_id; | ||||
|             } | ||||
|             if( empty( $copy ) ) { | ||||
|                 unset( $to_implode[$array_key] ); | ||||
|             } else { | ||||
|                 $to_implode[$array_key] = $array_key . Ai1ec_Uri::DIRECTION_SEPARATOR . | ||||
|                     implode( ',', $copy ); | ||||
|             } | ||||
|         } else { | ||||
|             $to_implode[$array_key] = $array_key . Ai1ec_Uri::DIRECTION_SEPARATOR . $this->term_id; | ||||
|         } | ||||
|         return $to_implode; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Match current argument key | ||||
|      * | ||||
|      * @return string Name of current argument key | ||||
|      */ | ||||
|     protected function _current_array_key() { | ||||
|         $map = array( | ||||
|             'category' => 'cat', | ||||
|             'tag'      => 'tag', | ||||
|             'author'   => 'auth', | ||||
|         ); | ||||
|         $use_name = ''; | ||||
|         foreach ( $map as $value => $name ) { | ||||
|             if ( $this->{'is_' . $value} ) { | ||||
|                 $use_name = $name; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return $use_name . '_ids'; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the delimiter character to use if a new query string parameter is | ||||
|      * going to be appended to the URL. | ||||
|      * | ||||
|      * @param string $url URL to parse | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public static function get_param_delimiter_char( $url ) { | ||||
|         return strpos( $url, '?' ) === false ? '?' : '&'; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,39 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Interface for HTML elements. | ||||
|  * | ||||
|  * In this context element is a complex collection of HTML tags | ||||
|  * rendered to suit specific needs. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Html | ||||
|  */ | ||||
| interface Ai1ec_Html_Element_Interface { | ||||
|  | ||||
|     /** | ||||
|      * Set attribute for renderable element. | ||||
|      * | ||||
|      * Attributes are object specific. | ||||
|      * | ||||
|      * @param string $attribute Name of attribute to set. | ||||
|      * @param mixed  $value     Value to set for attribute. | ||||
|      * | ||||
|      * @return Ai1ec_Html_Element_Interface Instance of self for chaining. | ||||
|      */ | ||||
|     public function set( $attribute, $value ); | ||||
|  | ||||
|     /** | ||||
|      * Generate HTML snippet for inclusion in page. | ||||
|      * | ||||
|      * @param string $snippet Particle to append to result. | ||||
|      * | ||||
|      * @return string HTML snippet. | ||||
|      * | ||||
|      * @throws Ai1ec_Html_Exception If rendering may not be completed. | ||||
|      */ | ||||
|     public function render( $snippet = '' ); | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,172 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Base html element. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Html | ||||
|  */ | ||||
| abstract class Ai1ec_Html_Element extends Ai1ec_Base implements Ai1ec_Renderable { | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var string | ||||
|      */ | ||||
|     protected $id; | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $classes = array(); | ||||
|  | ||||
|     /** | ||||
|      * @var array | ||||
|      */ | ||||
|     protected $attributes = array(); | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @var Ai1ec_Template_Adapter | ||||
|      */ | ||||
|     protected $template_adapter; | ||||
|  | ||||
|     /** | ||||
|      * Adds the passed attribute name & value to the link's attributes. | ||||
|      * | ||||
|      * @param string       $name | ||||
|      * @param string|array $value | ||||
|      */ | ||||
|     public function set_attribute( $name, $value ) { | ||||
|         $value = ( array ) $value; | ||||
|         // Let's check if we have a value | ||||
|         if ( isset( $this->attributes[$name] ) ) { | ||||
|             // Let's check if it's an array | ||||
|             $this->attributes[$name] = array_unique( | ||||
|                 array_merge( $this->attributes[$name], $value ) | ||||
|             ); | ||||
|         } else { | ||||
|             $this->attributes[$name] = $value; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param string $name | ||||
|      * @return array|NULL | ||||
|      */ | ||||
|     public function get_attribute( $name ) { | ||||
|         if ( isset( $this->attributes[$name] ) ) { | ||||
|             return $this->attributes[$name]; | ||||
|         } else { | ||||
|             return null; | ||||
|         } | ||||
|     } | ||||
|     /** | ||||
|      * Adds the given name="value"-formatted attribute expression to the link's | ||||
|      * set of attributes. | ||||
|      * | ||||
|      * @param string $expr Attribute name-value pair in name="value" format | ||||
|      */ | ||||
|     public function set_attribute_expr( $expr ) { | ||||
|         preg_match( '/^([\w\-_]+)=[\'"]([^\'"]*)[\'"]$/', $expr, $matches ); | ||||
|         $name = $matches[1]; | ||||
|         $value = $matches[2]; | ||||
|         $this->set_attribute( $name, $value ); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public function __construct( Ai1ec_Registry_Object $registry ) { | ||||
|         $this->_registry = $registry; | ||||
|         $this->template_adapter = $registry->get( 'html.helper' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Magic method that renders the object as html | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function __toString() { | ||||
|         return $this->render_as_html(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * | ||||
|      * @param $id string | ||||
|      */ | ||||
|     public function set_id( $id ) { | ||||
|         $this->id = $id; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds an element to the class array | ||||
|      * | ||||
|      * @param string $class | ||||
|      */ | ||||
|     public function add_class( $class ) { | ||||
|         $this->classes[] = $class; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates the markup to be used to create classes | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function create_class_markup() { | ||||
|         if ( empty( $this->classes ) ) { | ||||
|             return ''; | ||||
|         } | ||||
|  | ||||
|         $classes = $this->template_adapter->escape_attribute( | ||||
|             implode( ' ', $this->classes ) | ||||
|         ); | ||||
|         return "class='$classes'"; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates the markup for an attribute | ||||
|      * | ||||
|      * @param string $attribute_name | ||||
|      * @param string $attribute_value | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function create_attribute_markup( | ||||
|         $attribute_name, | ||||
|         $attribute_value | ||||
|     ) { | ||||
|         if (empty( $attribute_value )) { | ||||
|             return ''; | ||||
|         } | ||||
|         $attribute_value = $this->template_adapter->escape_attribute( $attribute_value ); | ||||
|         return "$attribute_name='$attribute_value'"; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Renders the markup for the attributes of the tag | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     protected function render_attributes_markup() { | ||||
|         $html = array(); | ||||
|         foreach ( $this->attributes as $name => $values ) { | ||||
|             $values = $this->template_adapter->escape_attribute( | ||||
|                 implode( ' ', $values ) | ||||
|             ); | ||||
|             $html[] = "$name='$values'"; | ||||
|         } | ||||
|         return implode( ' ', $html ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the content as html instead of echoing it. | ||||
|      * | ||||
|      * @return string | ||||
|      */ | ||||
|     public function render_as_html() { | ||||
|         $this->_registry->get( 'compatibility.ob' )->start(); | ||||
|         $this->render(); | ||||
|         return $this->_registry->get( 'compatibility.ob' )->get_clean(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,17 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Basic interface for the composite. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Html | ||||
|  */ | ||||
| interface Ai1ec_Renderable { | ||||
|     /** | ||||
|      * This is the main function, it just renders the method for the element, | ||||
|      * taking care of childrens ( if any ) | ||||
|      */ | ||||
|     public function render(); | ||||
| } | ||||
| @@ -0,0 +1,132 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * A class that renders bootstrap modals. | ||||
|  * | ||||
|  * @instantiator new | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Bootstrap_Modal extends Ai1ec_Html_Element { | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $delete_button_text; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $keep_button_text; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $body_text; | ||||
|  | ||||
|     /** | ||||
|      * @var string | ||||
|      */ | ||||
|     private $header_text; | ||||
|  | ||||
|     /** | ||||
|      * @param string $modal_text | ||||
|      */ | ||||
|     public function __construct( Ai1ec_Registry_Object $registry, $modal_text ) { | ||||
|         $this->body_text = $modal_text; | ||||
|         parent::__construct( $registry ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $delete_button_text | ||||
|      */ | ||||
|     public function set_delete_button_text( $delete_button_text ) { | ||||
|         $this->delete_button_text = $delete_button_text; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $keep_button_text | ||||
|      */ | ||||
|     public function set_keep_button_text( $keep_button_text ) { | ||||
|         $this->keep_button_text = $keep_button_text; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $body_text | ||||
|      */ | ||||
|     public function set_body_text( $body_text ) { | ||||
|         $this->body_text = $body_text; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @param string $header_text | ||||
|      */ | ||||
|     public function set_header_text( $header_text ) { | ||||
|         $this->header_text = $header_text; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     private function render_id_if_present() { | ||||
|         return isset( $this->id ) ? "id='{$this->id}'" : ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     private function render_header_if_present() { | ||||
|         return isset( $this->header_text ) ? | ||||
|             '<h2>' . $this->header_text . '</h2>' | ||||
|             : ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     private function render_keep_button_if_present() { | ||||
|         return isset( $this->keep_button_text ) ? "<a href='#' class='ai1ec-btn keep ai1ec-btn-primary ai1ec-btn-lg'>{$this->keep_button_text}</a>" : ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     private function render_remove_button_if_present() { | ||||
|         return isset( $this->delete_button_text ) ? "<a href='#' class='ai1ec-btn remove ai1ec-btn-danger ai1ec-btn-lg'>{$this->delete_button_text}</a>" : ''; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return string | ||||
|      */ | ||||
|     public function render() { | ||||
|         $header              = $this->render_header_if_present(); | ||||
|         $id                  = $this->render_id_if_present(); | ||||
|         $remove_event_button = $this->render_remove_button_if_present(); | ||||
|         $keep_event_button   = $this->render_keep_button_if_present(); | ||||
|         $body                = $this->body_text; | ||||
|         $classes             = implode( ' ', $this->classes ); | ||||
|         $html = <<<HTML | ||||
| <div class="ai1ec-modal $classes ai1ec-fade timely" $id> | ||||
|     <div class="ai1ec-modal-dialog"> | ||||
|         <div class="ai1ec-modal-content"> | ||||
|             <div class="ai1ec-modal-header"> | ||||
|                 <button type="button" class="ai1ec-close" data-dismiss="ai1ec-modal" | ||||
|                     aria-hidden="true">×</button> | ||||
|                 $header | ||||
|             </div> | ||||
|             <div class="ai1ec-modal-body"> | ||||
|                 $body | ||||
|             </div> | ||||
|             <div class="ai1ec-modal-footer"> | ||||
|                 $remove_event_button | ||||
|                 $keep_event_button | ||||
|             </div> | ||||
|         </div> | ||||
|     </div> | ||||
| </div> | ||||
| HTML; | ||||
|         echo $html; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,41 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Missing class setting-renderer description. | ||||
|  * | ||||
|  * @author     Time.ly Network Inc. | ||||
|  * @since      2.2 | ||||
|  * | ||||
|  * @package    AI1EC | ||||
|  * @subpackage AI1EC. | ||||
|  */ | ||||
|  | ||||
| class Ai1ec_Html_Setting_Renderer extends Ai1ec_Base { | ||||
|  | ||||
|     /** | ||||
|      * Renders single setting. | ||||
|      * | ||||
|      * @param array $setting Setting structure. | ||||
|      * | ||||
|      * @return string Rendered content. | ||||
|      * | ||||
|      * @throws Ai1ec_Bootstrap_Exception | ||||
|      */ | ||||
|     public function render( array $setting ) { | ||||
|         $renderer_name = $setting['renderer']['class']; | ||||
|         $renderer      = null; | ||||
|         try { | ||||
|             $renderer = $this->_registry->get( | ||||
|                 'html.element.setting.' . $renderer_name, | ||||
|                 $setting | ||||
|             ); | ||||
|         } catch ( Ai1ec_Bootstrap_Exception $exception ) { | ||||
|             $renderer = $this->_registry->get( | ||||
|                 'html.element.setting.input', | ||||
|                 $setting | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         return $renderer->render(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,101 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Abstract class to accelerate settings page snippets development. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Html | ||||
|  */ | ||||
| abstract class Ai1ec_Html_Element_Settings extends Ai1ec_Base | ||||
|     implements Ai1ec_Html_Element_Interface { | ||||
|  | ||||
|     /** | ||||
|      * @var Ai1ec_Html_Helper Instance of HTML helper. | ||||
|      */ | ||||
|     protected $_html = NULL; | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of arbitrary arguments passed to an element. | ||||
|      */ | ||||
|     protected $_args = array(); | ||||
|  | ||||
|     /** | ||||
|      * Constructor accepts system as injectable and requests HTML helper. | ||||
|      * | ||||
|      * @param Ai1ec_Registry_Object $system Injected system argument. | ||||
|      * | ||||
|      * @return void Constructor does not return. | ||||
|      */ | ||||
|     public function __construct( | ||||
|         Ai1ec_Registry_Object $registry, | ||||
|         array $args | ||||
|     ) { | ||||
|         parent::__construct( $registry ); | ||||
|         $this->_args = $args; | ||||
|         $this->_html = $registry->get( 'html.helper' ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set value within current object scope | ||||
|      * | ||||
|      * Value name is formed as {$attribute} with underscore ('_') prefixed. | ||||
|      * | ||||
|      * @param string $attribute Name of attribute to set. | ||||
|      * @param mixed  $value     Value to set for attribute. | ||||
|      * | ||||
|      * @return Ai1ec_Html_Element_Settings Instance of self. | ||||
|      */ | ||||
|     public function set( $attribute, $value ) { | ||||
|         $this->{'_' . $attribute} = $value; | ||||
|         return $this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Override to include any initialization logics. | ||||
|      * | ||||
|      * @return void Method output is ignored. | ||||
|      */ | ||||
|     protected function _initialize() { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate settings output line. | ||||
|      * | ||||
|      * @param string $output Generated output to finalize. | ||||
|      * @param bool   $wrap   Whether content should be wrapped with div or not. | ||||
|      * | ||||
|      * @return string Finalized HTML snippet. | ||||
|      */ | ||||
|     public function render( $output = '', $wrap = true, $hidden = false ) { | ||||
|         if ( isset( $this->_args['renderer']['condition'] ) ) { | ||||
|             $condition = $this->_args['renderer']['condition']; | ||||
|             if ( is_bool( $condition ) ) { | ||||
|                 $render = $condition; | ||||
|             } else { | ||||
|                 $callback = explode( ':', $this->_args['renderer']['condition'] ); | ||||
|                 try { | ||||
|                     $render = $this->_registry->dispatch( | ||||
|                         $callback[0], | ||||
|                         $callback[1] | ||||
|                     ); | ||||
|                 } catch (Ai1ec_Bootstrap_Exception $exception) { | ||||
|                     $render = ''; | ||||
|                 } | ||||
|             } | ||||
|             if ( ! $render ) { | ||||
|                 return ''; | ||||
|             } | ||||
|         } | ||||
|         if ( ! $wrap ) { | ||||
|             return $output; | ||||
|         } | ||||
|         if ( $hidden ) { | ||||
|             return '<div class="ai1ec-form-group ai1ec-hidden">' . $output . '</div>'; | ||||
|         } else { | ||||
|             return '<div class="ai1ec-form-group">' . $output . '</div>'; | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,48 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Renderer of settings page html. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @instantiator new | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Html_Setting_Cache extends Ai1ec_Html_Element_Settings { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Html_Element_Settings::render() | ||||
|      */ | ||||
|     public function render( $output = '', $wrap = true, $hidden = false ) { | ||||
|         $args   = $this->get_twig_cache_args(); | ||||
|         $loader = $this->_registry->get( 'theme.loader' ); | ||||
|         $file   = $loader->get_file( 'setting/twig_cache.twig', $args, true ); | ||||
|         return parent::render( $file->get_content(), $wrap, $hidden ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns data for Twig template. | ||||
|      * | ||||
|      * @return array Data for template | ||||
|      */ | ||||
|     public function get_twig_cache_args() { | ||||
|         $args = array( | ||||
|             'cache_available' => ( | ||||
|                 AI1EC_CACHE_UNAVAILABLE !== $this->_args['value'] && | ||||
|                 ! empty( $this->_args['value'] ) | ||||
|             ), | ||||
|             'id'              => $this->_args['id'], | ||||
|             'label'           => $this->_args['renderer']['label'], | ||||
|             'text'            => array( | ||||
|                 'refresh' => Ai1ec_I18n::__( 'Check again' ), | ||||
|                 'nocache' => Ai1ec_I18n::__( 'Templates cache is not writable' ), | ||||
|                 'okcache' => Ai1ec_I18n::__( 'Templates cache is writable' ), | ||||
|                 'rescan'  => Ai1ec_I18n::__( 'Checking...' ), | ||||
|                 'title'   => Ai1ec_I18n::__( 'Performance Report' ), | ||||
|             ), | ||||
|         ); | ||||
|  | ||||
|         return $args; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,124 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Renderer of settings page Calendar page selection snippet. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Html_Element_Calendar_Page_Selector | ||||
|     extends Ai1ec_Html_Element_Settings { | ||||
|  | ||||
|     /** | ||||
|      * @var string HTML id attribute for selector. | ||||
|      */ | ||||
|     const ELEMENT_ID     = 'calendar_page_id'; | ||||
|  | ||||
|     /** | ||||
|      * @var array Map of pages defined in system, use `get_pages()` WP call. | ||||
|      */ | ||||
|     protected $_pages    = array(); | ||||
|  | ||||
|     /** | ||||
|      * Set attributes for element. | ||||
|      * | ||||
|      * Currently recognized attributes: | ||||
|      *     - 'pages'    - {@see self::$_pages} for details; | ||||
|      *     - 'selected' - {@see self::$_selected} for details. | ||||
|      * | ||||
|      * @param string $attribute Name of attribute to set. | ||||
|      * @param mixed  $value     Value to set for attribute. | ||||
|      * | ||||
|      * @return Ai1ec_Html_Element_Calendar_Page_Selector Instance of self. | ||||
|      */ | ||||
|     public function set( $attribute, $value ) { | ||||
|         // any validation may be provided here | ||||
|         return parent::set( $attribute, $value ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate HTML snippet for inclusion in settings page. | ||||
|      * | ||||
|      * @param string $snippet Particle to append to result. | ||||
|      * | ||||
|      * @return string HTML snippet for page selection. | ||||
|      */ | ||||
|     public function render( $snippet = '', $wrap = true, $hidden = false ) { | ||||
|         $output = '<label class="ai1ec-control-label ai1ec-col-sm-5" for="' . | ||||
|             self::ELEMENT_ID . '">' . Ai1ec_I18n::__( 'Calendar page' ) . '</label>' | ||||
|             . '<div class="ai1ec-col-sm-7">' . | ||||
|             $this->_get_pages_selector() . $this->_get_page_view_link() . '</div>'; | ||||
|         return parent::render( $output, $wrap, $hidden ); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate link to open selected page in new window. | ||||
|      * | ||||
|      * @return string HTML snippet. | ||||
|      */ | ||||
|     protected function _get_page_view_link() { | ||||
|         if ( empty( $this->_args['value'] ) ) { | ||||
|             return ''; | ||||
|         } | ||||
|         $post = get_post( $this->_args['value'] ); | ||||
|         if ( empty( $post->ID ) ) { | ||||
|             return ''; | ||||
|         } | ||||
|         $args = array( | ||||
|             'view'  => Ai1ec_I18n::__( 'View' ), | ||||
|             'link'  => get_permalink( $post->ID ), | ||||
|             'title' => apply_filters( | ||||
|                 'the_title', | ||||
|                 $post->post_title, | ||||
|                 $post->ID | ||||
|             ), | ||||
|         ); | ||||
|         return $this->_registry->get( 'theme.loader' ) | ||||
|             ->get_file( 'setting/calendar-page-selector.twig', $args, true ) | ||||
|             ->get_content(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Generate dropdown selector to choose page. | ||||
|      * | ||||
|      * @return string HTML snippet. | ||||
|      */ | ||||
|     protected function _get_pages_selector() { | ||||
|         $html = '<select id="' . self::ELEMENT_ID . | ||||
|             '" class="ai1ec-form-control" name="' . self::ELEMENT_ID . '">'; | ||||
|         $list = $this->_get_pages(); | ||||
|         foreach ( $list as $key => $value ) { | ||||
|             $html .= '<option value="' . $this->_html->esc_attr( $key ) . '"'; | ||||
|             if ( $this->_args['value'] === $key ) { | ||||
|                 $html .= ' selected="selected"'; | ||||
|             } | ||||
|             $html .= '>' . $this->_html->esc_html( $value ) . '</option>'; | ||||
|         } | ||||
|         $html .= '</select>'; | ||||
|         return $html; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Make a map of page IDs and titles for selection snippet. | ||||
|      * | ||||
|      * @return array Map of page keys and titles. | ||||
|      */ | ||||
|     protected function _get_pages() { | ||||
|         $pages = get_pages(); | ||||
|         if ( ! is_array( $pages ) ) { | ||||
|             $pages = array(); | ||||
|         } | ||||
|         $output = array( | ||||
|             '__auto_page:Calendar' => Ai1ec_I18n::__( | ||||
|                 '- Auto-Create New Page -' | ||||
|             ), | ||||
|         ); | ||||
|         foreach ( $pages as $key => $value ) { | ||||
|             $output[$value->ID] = $value->post_title; | ||||
|         } | ||||
|         return $output; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,36 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Renderer of settings page checkbox option. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @instantiator new | ||||
|  * @since        2.0 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Html_Settings_Checkbox extends Ai1ec_Html_Element_Settings { | ||||
|  | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Html_Element_Settings::render() | ||||
|      */ | ||||
|     public function render( $output = '', $wrap = true, $hidden = false ) { | ||||
|         $attributes = array( | ||||
|             'class' => 'checkbox', | ||||
|         ); | ||||
|         if ( true === $this->_args['value'] ) { | ||||
|             $attributes['checked'] = 'checked'; | ||||
|         } | ||||
|         $args               = $this->_args; | ||||
|         $args['attributes'] = $attributes; | ||||
|         $loader             = $this->_registry->get( 'theme.loader' ); | ||||
|         $file               = $loader->get_file( | ||||
|             'setting/checkbox.twig', | ||||
|             $args, | ||||
|             true | ||||
|         ); | ||||
|         return parent::render( $file->get_content(), $wrap, $hidden ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,28 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Renderer of settings page custom option. | ||||
|  * | ||||
|  * @author       Time.ly Network, Inc. | ||||
|  * @instantiator new | ||||
|  * @since        2.4 | ||||
|  * @package      Ai1EC | ||||
|  * @subpackage   Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Html_Setting_Custom extends Ai1ec_Html_Element_Settings { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Html_Element_Settings::render() | ||||
|      */ | ||||
|     public function render( $output = '', $wrap = true, $hidden = false ) { | ||||
|         $label   = $this->_args['renderer']['label']; | ||||
|         $content = $this->_args['renderer']['content']; | ||||
|         $loader  = $this->_registry->get( 'theme.loader' ); | ||||
|         $file    = $loader->get_file( 'setting/custom.twig', array( | ||||
|             'label'   => $label, | ||||
|             'content' => $content | ||||
|         ), true ); | ||||
|         return parent::render( $file->get_content(), $wrap, $hidden ); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,60 @@ | ||||
| <?php | ||||
|  | ||||
| /** | ||||
|  * Renderer of settings page Enabled views selection snippet. | ||||
|  * | ||||
|  * @author     Time.ly Network, Inc. | ||||
|  * @since      2.0 | ||||
|  * @package    Ai1EC | ||||
|  * @subpackage Ai1EC.Html | ||||
|  */ | ||||
| class Ai1ec_Html_Element_Enabled_Views | ||||
|     extends Ai1ec_Html_Element_Settings { | ||||
|  | ||||
|     /* (non-PHPdoc) | ||||
|      * @see Ai1ec_Html_Element_Settings::render() | ||||
|      */ | ||||
|     public function render( $output = '', $wrap = true, $hidden = false ) { | ||||
|         $this->_convert_values(); | ||||
|         $args = array( | ||||
|             'views'        => $this->_args['value'], | ||||
|             'label'        => $this->_args['renderer']['label'], | ||||
|             'text_enabled' => __( 'Enabled', AI1EC_PLUGIN_NAME ), | ||||
|             'text_default' => __( 'Default', AI1EC_PLUGIN_NAME ), | ||||
|             'text_desktop' => __( 'Desktop', AI1EC_PLUGIN_NAME ), | ||||
|             'text_mobile'  => __( 'Mobile', AI1EC_PLUGIN_NAME ), | ||||
|         ); | ||||
|         $loader = $this->_registry->get( 'theme.loader' ); | ||||
|         return $loader->get_file( 'setting/enabled-views.twig', $args, true ) | ||||
|             ->get_content(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Convert values to bo used in rendering | ||||
|      */ | ||||
|     protected function _convert_values() { | ||||
|         foreach( $this->_args['value'] as &$view ) { | ||||
|             $view['enabled'] = $view['enabled'] ? | ||||
|                 'checked="checked"' : | ||||
|                 ''; | ||||
|             $view['default'] = $view['default'] ? | ||||
|                 'checked="checked"' : | ||||
|                 ''; | ||||
|             // Use mobile settings if available, else fall back to desktop settings. | ||||
|             $view['enabled_mobile'] = isset( $view['enabled_mobile'] ) ? | ||||
|                 ( $view['enabled_mobile'] ? | ||||
|                     'checked="checked"' : | ||||
|                     '' ) : | ||||
|                 $view['enabled']; | ||||
|             $view['default_mobile'] = isset( $view['default_mobile'] ) ? | ||||
|                 ( $view['default_mobile'] ? | ||||
|                     'checked="checked"' : | ||||
|                     '' ) : | ||||
|                 $view['default']; | ||||
|             $view['longname']       = translate_nooped_plural( | ||||
|                 $view['longname'], | ||||
|                 1 | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user