diff --git a/CHANGES.md b/CHANGES.md index bebff68..8ed8a49 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,6 @@ +#### 2.1.0 +* Added: Included updater script. + #### 2.0.1 * Update plugin header to point to new repo in bizbudding GitHub organization @@ -12,4 +15,4 @@ * Fix click in popup causing it to close #### 1.0.0 -* Let's get this party started \ No newline at end of file +* Let's get this party started diff --git a/includes/vendor/plugin-update-checker/Puc/v4/Factory.php b/includes/vendor/plugin-update-checker/Puc/v4/Factory.php new file mode 100755 index 0000000..c66a251 --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4/Factory.php @@ -0,0 +1,271 @@ + 'Plugin Name'), 'plugin'); + return !empty($headers['Name']); + } + + return false; + } + + /** + * Get the name of the theme's directory from a full path to a file inside that directory. + * E.g. "/abc/public_html/wp-content/themes/foo/whatever.php" => "foo". + * + * Note that subdirectories are currently not supported. For example, + * "/xyz/wp-content/themes/my-theme/includes/whatever.php" => NULL. + * + * @param string $absolutePath Normalized path. + * @return string|null Directory name, or NULL if the path doesn't point to a theme. + */ + protected static function getThemeDirectoryName($absolutePath) { + if ( is_file($absolutePath) ) { + $absolutePath = dirname($absolutePath); + } + + if ( file_exists($absolutePath . '/style.css') ) { + return basename($absolutePath); + } + return null; + } + + /** + * Get the name of the hosting service that the URL points to. + * + * @param string $metadataUrl + * @return string|null + */ + private static function getVcsService($metadataUrl) { + $service = null; + + //Which hosting service does the URL point to? + $host = @parse_url($metadataUrl, PHP_URL_HOST); + $path = @parse_url($metadataUrl, PHP_URL_PATH); + //Check if the path looks like "/user-name/repository". + $usernameRepoRegex = '@^/?([^/]+?)/([^/#?&]+?)/?$@'; + if ( preg_match($usernameRepoRegex, $path) ) { + $knownServices = array( + 'github.com' => 'GitHub', + 'bitbucket.org' => 'BitBucket', + 'gitlab.com' => 'GitLab', + ); + if ( isset($knownServices[$host]) ) { + $service = $knownServices[$host]; + } + } + + return $service; + } + + /** + * Get the latest version of the specified class that has the same major version number + * as this factory class. + * + * @param string $class Partial class name. + * @return string|null Full class name. + */ + protected static function getCompatibleClassVersion($class) { + if ( isset(self::$classVersions[$class][self::$latestCompatibleVersion]) ) { + return self::$classVersions[$class][self::$latestCompatibleVersion]; + } + return null; + } + + /** + * Get the specific class name for the latest available version of a class. + * + * @param string $class + * @return null|string + */ + public static function getLatestClassVersion($class) { + if ( !self::$sorted ) { + self::sortVersions(); + } + + if ( isset(self::$classVersions[$class]) ) { + return reset(self::$classVersions[$class]); + } else { + return null; + } + } + + /** + * Sort available class versions in descending order (i.e. newest first). + */ + protected static function sortVersions() { + foreach ( self::$classVersions as $class => $versions ) { + uksort($versions, array(__CLASS__, 'compareVersions')); + self::$classVersions[$class] = $versions; + } + self::$sorted = true; + } + + protected static function compareVersions($a, $b) { + return -version_compare($a, $b); + } + + /** + * Register a version of a class. + * + * @access private This method is only for internal use by the library. + * + * @param string $generalClass Class name without version numbers, e.g. 'PluginUpdateChecker'. + * @param string $versionedClass Actual class name, e.g. 'PluginUpdateChecker_1_2'. + * @param string $version Version number, e.g. '1.2'. + */ + public static function addVersion($generalClass, $versionedClass, $version) { + if ( empty(self::$myMajorVersion) ) { + $nameParts = explode('_', __CLASS__, 3); + self::$myMajorVersion = substr(ltrim($nameParts[1], 'v'), 0, 1); + } + + //Store the greatest version number that matches our major version. + $components = explode('.', $version); + if ( $components[0] === self::$myMajorVersion ) { + + if ( + empty(self::$latestCompatibleVersion) + || version_compare($version, self::$latestCompatibleVersion, '>') + ) { + self::$latestCompatibleVersion = $version; + } + + } + + if ( !isset(self::$classVersions[$generalClass]) ) { + self::$classVersions[$generalClass] = array(); + } + self::$classVersions[$generalClass][$version] = $versionedClass; + self::$sorted = false; + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/Autoloader.php b/includes/vendor/plugin-update-checker/Puc/v4p2/Autoloader.php new file mode 100755 index 0000000..e35b024 --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/Autoloader.php @@ -0,0 +1,49 @@ +rootDir = dirname(__FILE__) . '/'; + $nameParts = explode('_', __CLASS__, 3); + $this->prefix = $nameParts[0] . '_' . $nameParts[1] . '_'; + + $this->libraryDir = realpath($this->rootDir . '../..') . '/'; + $this->staticMap = array( + 'PucReadmeParser' => 'vendor/readme-parser.php', + 'Parsedown' => 'vendor/ParsedownLegacy.php', + ); + if ( version_compare(PHP_VERSION, '5.3.0', '>=') ) { + $this->staticMap['Parsedown'] = 'vendor/Parsedown.php'; + } + + spl_autoload_register(array($this, 'autoload')); + } + + public function autoload($className) { + if ( isset($this->staticMap[$className]) && file_exists($this->libraryDir . $this->staticMap[$className]) ) { + /** @noinspection PhpIncludeInspection */ + include ($this->libraryDir . $this->staticMap[$className]); + return; + } + + if (strpos($className, $this->prefix) === 0) { + $path = substr($className, strlen($this->prefix)); + $path = str_replace('_', '/', $path); + $path = $this->rootDir . $path . '.php'; + + if (file_exists($path)) { + /** @noinspection PhpIncludeInspection */ + include $path; + } + } + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/Extension.php b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/Extension.php new file mode 100755 index 0000000..77891fb --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/Extension.php @@ -0,0 +1,117 @@ +updateChecker = $updateChecker; + if ( isset($panelClass) ) { + $this->panelClass = $panelClass; + } + + add_filter('debug_bar_panels', array($this, 'addDebugBarPanel')); + add_action('debug_bar_enqueue_scripts', array($this, 'enqueuePanelDependencies')); + + add_action('wp_ajax_puc_v4_debug_check_now', array($this, 'ajaxCheckNow')); + } + + /** + * Register the PUC Debug Bar panel. + * + * @param array $panels + * @return array + */ + public function addDebugBarPanel($panels) { + if ( $this->updateChecker->userCanInstallUpdates() ) { + $panels[] = new $this->panelClass($this->updateChecker); + } + return $panels; + } + + /** + * Enqueue our Debug Bar scripts and styles. + */ + public function enqueuePanelDependencies() { + wp_enqueue_style( + 'puc-debug-bar-style-v4', + $this->getLibraryUrl("/css/puc-debug-bar.css"), + array('debug-bar'), + '20161217' + ); + + wp_enqueue_script( + 'puc-debug-bar-js-v4', + $this->getLibraryUrl("/js/debug-bar.js"), + array('jquery'), + '20170516' + ); + } + + /** + * Run an update check and output the result. Useful for making sure that + * the update checking process works as expected. + */ + public function ajaxCheckNow() { + if ( $_POST['uid'] !== $this->updateChecker->getUniqueName('uid') ) { + return; + } + $this->preAjaxReqest(); + $update = $this->updateChecker->checkForUpdates(); + if ( $update !== null ) { + echo "An update is available:"; + echo '
', htmlentities(print_r($update, true)), ''; + } else { + echo 'No updates found.'; + } + exit; + } + + /** + * Check access permissions and enable error display (for debugging). + */ + protected function preAjaxReqest() { + if ( !$this->updateChecker->userCanInstallUpdates() ) { + die('Access denied'); + } + check_ajax_referer('puc-ajax'); + + error_reporting(E_ALL); + @ini_set('display_errors','On'); + } + + /** + * @param string $filePath + * @return string + */ + private function getLibraryUrl($filePath) { + $absolutePath = realpath(dirname(__FILE__) . '/../../../' . ltrim($filePath, '/')); + + //Where is the library located inside the WordPress directory structure? + $absolutePath = wp_normalize_path($absolutePath); + + $pluginDir = wp_normalize_path(WP_PLUGIN_DIR); + $muPluginDir = wp_normalize_path(WPMU_PLUGIN_DIR); + $themeDir = wp_normalize_path(get_theme_root()); + + if ( (strpos($absolutePath, $pluginDir) === 0) || (strpos($absolutePath, $muPluginDir) === 0) ) { + //It's part of a plugin. + return plugins_url(basename($absolutePath), $absolutePath); + } else if ( strpos($absolutePath, $themeDir) === 0 ) { + //It's part of a theme. + $relativePath = substr($absolutePath, strlen($themeDir) + 1); + $template = substr($relativePath, 0, strpos($relativePath, '/')); + $baseUrl = get_theme_root_uri($template); + + if ( !empty($baseUrl) && $relativePath ) { + return $baseUrl . '/' . $relativePath; + } + } + + return ''; + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/Panel.php b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/Panel.php new file mode 100755 index 0000000..a756b4c --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/Panel.php @@ -0,0 +1,165 @@ +'; + + public function __construct($updateChecker) { + $this->updateChecker = $updateChecker; + $title = sprintf( + ' ', + esc_attr($this->updateChecker->getUniqueName('uid')), + $this->updateChecker->slug + ); + parent::__construct($title); + } + + public function render() { + printf( + ' '; + } + + private function displayConfiguration() { + echo '
' . htmlentities(print_r($value, true)) . ''; + } else if ($value === null) { + $value = '
null
';
+ }
+ printf('', htmlentities(print_r($info, true)), ''; + } else { + echo 'Failed to retrieve plugin info from the metadata URL.'; + } + exit; + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/PluginPanel.php b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/PluginPanel.php new file mode 100755 index 0000000..9cfd4b3 --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/PluginPanel.php @@ -0,0 +1,38 @@ +row('Plugin file', htmlentities($this->updateChecker->pluginFile)); + parent::displayConfigHeader(); + } + + protected function getMetadataButton() { + $requestInfoButton = ''; + if ( function_exists('get_submit_button') ) { + $requestInfoButton = get_submit_button( + 'Request Info', + 'secondary', + 'puc-request-info-button', + false, + array('id' => $this->updateChecker->getUniqueName('request-info-button')) + ); + } + return $requestInfoButton; + } + + protected function getUpdateFields() { + return array_merge( + parent::getUpdateFields(), + array('homepage', 'upgrade_notice', 'tested',) + ); + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/ThemePanel.php b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/ThemePanel.php new file mode 100755 index 0000000..25f42ab --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/DebugBar/ThemePanel.php @@ -0,0 +1,21 @@ +row('Theme directory', htmlentities($this->updateChecker->directoryName)); + parent::displayConfigHeader(); + } + + protected function getUpdateFields() { + return array_merge(parent::getUpdateFields(), array('details_url')); + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/Metadata.php b/includes/vendor/plugin-update-checker/Puc/v4p2/Metadata.php new file mode 100755 index 0000000..ca57706 --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/Metadata.php @@ -0,0 +1,132 @@ +validateMetadata($apiResponse); + if ( is_wp_error($valid) ){ + trigger_error($valid->get_error_message(), E_USER_NOTICE); + return false; + } + + foreach(get_object_vars($apiResponse) as $key => $value){ + $target->$key = $value; + } + + return true; + } + + /** + * No validation by default! Subclasses should check that the required fields are present. + * + * @param StdClass $apiResponse + * @return bool|WP_Error + */ + protected function validateMetadata(/** @noinspection PhpUnusedParameterInspection */ $apiResponse) { + return true; + } + + /** + * Create a new instance by copying the necessary fields from another object. + * + * @abstract + * @param StdClass|self $object The source object. + * @return self The new copy. + */ + public static function fromObject(/** @noinspection PhpUnusedParameterInspection */ $object) { + throw new LogicException('The ' . __METHOD__ . ' method must be implemented by subclasses'); + } + + /** + * Create an instance of StdClass that can later be converted back to an + * update or info container. Useful for serialization and caching, as it + * avoids the "incomplete object" problem if the cached value is loaded + * before this class. + * + * @return StdClass + */ + public function toStdClass() { + $object = new stdClass(); + $this->copyFields($this, $object); + return $object; + } + + /** + * Transform the metadata into the format used by WordPress core. + * + * @return object + */ + abstract public function toWpFormat(); + + /** + * Copy known fields from one object to another. + * + * @param StdClass|self $from + * @param StdClass|self $to + */ + protected function copyFields($from, $to) { + $fields = $this->getFieldNames(); + + if ( property_exists($from, 'slug') && !empty($from->slug) ) { + //Let plugins add extra fields without having to create subclasses. + $fields = apply_filters($this->getPrefixedFilter('retain_fields') . '-' . $from->slug, $fields); + } + + foreach ($fields as $field) { + if ( property_exists($from, $field) ) { + $to->$field = $from->$field; + } + } + } + + /** + * @return string[] + */ + protected function getFieldNames() { + return array(); + } + + /** + * @param string $tag + * @return string + */ + protected function getPrefixedFilter($tag) { + return 'puc_' . $tag; + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/OAuthSignature.php b/includes/vendor/plugin-update-checker/Puc/v4p2/OAuthSignature.php new file mode 100755 index 0000000..81e1137 --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/OAuthSignature.php @@ -0,0 +1,88 @@ +consumerKey = $consumerKey; + $this->consumerSecret = $consumerSecret; + } + + /** + * Sign a URL using OAuth 1.0. + * + * @param string $url The URL to be signed. It may contain query parameters. + * @param string $method HTTP method such as "GET", "POST" and so on. + * @return string The signed URL. + */ + public function sign($url, $method = 'GET') { + $parameters = array(); + + //Parse query parameters. + $query = @parse_url($url, PHP_URL_QUERY); + if ( !empty($query) ) { + parse_str($query, $parsedParams); + if ( is_array($parameters) ) { + $parameters = $parsedParams; + } + //Remove the query string from the URL. We'll replace it later. + $url = substr($url, 0, strpos($url, '?')); + } + + $parameters = array_merge( + $parameters, + array( + 'oauth_consumer_key' => $this->consumerKey, + 'oauth_nonce' => $this->nonce(), + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_timestamp' => time(), + 'oauth_version' => '1.0', + ) + ); + unset($parameters['oauth_signature']); + + //Parameters must be sorted alphabetically before signing. + ksort($parameters); + + //The most complicated part of the request - generating the signature. + //The string to sign contains the HTTP method, the URL path, and all of + //our query parameters. Everything is URL encoded. Then we concatenate + //them with ampersands into a single string to hash. + $encodedVerb = urlencode($method); + $encodedUrl = urlencode($url); + $encodedParams = urlencode(http_build_query($parameters, '', '&')); + + $stringToSign = $encodedVerb . '&' . $encodedUrl . '&' . $encodedParams; + + //Since we only have one OAuth token (the consumer secret) we only have + //to use it as our HMAC key. However, we still have to append an & to it + //as if we were using it with additional tokens. + $secret = urlencode($this->consumerSecret) . '&'; + + //The signature is a hash of the consumer key and the base string. Note + //that we have to get the raw output from hash_hmac and base64 encode + //the binary data result. + $parameters['oauth_signature'] = base64_encode(hash_hmac('sha1', $stringToSign, $secret, true)); + + return ($url . '?' . http_build_query($parameters)); + } + + /** + * Generate a random nonce. + * + * @return string + */ + private function nonce() { + $mt = microtime(); + $rand = mt_rand(); + return md5($mt . '_' . $rand); + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/Info.php b/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/Info.php new file mode 100755 index 0000000..dc04d4c --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/Info.php @@ -0,0 +1,127 @@ +sections = (array)$instance->sections; + + return $instance; + } + + /** + * Very, very basic validation. + * + * @param StdClass $apiResponse + * @return bool|WP_Error + */ + protected function validateMetadata($apiResponse) { + if ( + !isset($apiResponse->name, $apiResponse->version) + || empty($apiResponse->name) + || empty($apiResponse->version) + ) { + return new WP_Error( + 'puc-invalid-metadata', + "The plugin metadata file does not contain the required 'name' and/or 'version' keys." + ); + } + return true; + } + + + /** + * Transform plugin info into the format used by the native WordPress.org API + * + * @return object + */ + public function toWpFormat(){ + $info = new stdClass; + + //The custom update API is built so that many fields have the same name and format + //as those returned by the native WordPress.org API. These can be assigned directly. + $sameFormat = array( + 'name', 'slug', 'version', 'requires', 'tested', 'rating', 'upgrade_notice', + 'num_ratings', 'downloaded', 'active_installs', 'homepage', 'last_updated', + ); + foreach($sameFormat as $field){ + if ( isset($this->$field) ) { + $info->$field = $this->$field; + } else { + $info->$field = null; + } + } + + //Other fields need to be renamed and/or transformed. + $info->download_link = $this->download_url; + $info->author = $this->getFormattedAuthor(); + $info->sections = array_merge(array('description' => ''), $this->sections); + + if ( !empty($this->banners) ) { + //WP expects an array with two keys: "high" and "low". Both are optional. + //Docs: https://wordpress.org/plugins/about/faq/#banners + $info->banners = is_object($this->banners) ? get_object_vars($this->banners) : $this->banners; + $info->banners = array_intersect_key($info->banners, array('high' => true, 'low' => true)); + } + + return $info; + } + + protected function getFormattedAuthor() { + if ( !empty($this->author_homepage) ){ + /** @noinspection HtmlUnknownTarget */ + return sprintf('%s', $this->author_homepage, $this->author); + } + return $this->author; + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/Update.php b/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/Update.php new file mode 100755 index 0000000..753954c --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/Update.php @@ -0,0 +1,91 @@ +copyFields($object, $update); + return $update; + } + + /** + * @return string[] + */ + protected function getFieldNames() { + return array_merge(parent::getFieldNames(), self::$extraFields); + } + + /** + * Transform the update into the format used by WordPress native plugin API. + * + * @return object + */ + public function toWpFormat(){ + $update = parent::toWpFormat(); + + $update->id = $this->id; + $update->url = $this->homepage; + $update->tested = $this->tested; + $update->plugin = $this->filename; + + if ( !empty($this->upgrade_notice) ){ + $update->upgrade_notice = $this->upgrade_notice; + } + + return $update; + } + } + +endif; \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/UpdateChecker.php b/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/UpdateChecker.php new file mode 100755 index 0000000..e51906b --- /dev/null +++ b/includes/vendor/plugin-update-checker/Puc/v4p2/Plugin/UpdateChecker.php @@ -0,0 +1,546 @@ +pluginAbsolutePath = $pluginFile; + $this->pluginFile = plugin_basename($this->pluginAbsolutePath); + $this->muPluginFile = $muPluginFile; + + //If no slug is specified, use the name of the main plugin file as the slug. + //For example, 'my-cool-plugin/cool-plugin.php' becomes 'cool-plugin'. + if ( empty($slug) ){ + $slug = basename($this->pluginFile, '.php'); + } + + //Plugin slugs must be unique. + $slugCheckFilter = 'puc_is_slug_in_use-' . $this->slug; + $slugUsedBy = apply_filters($slugCheckFilter, false); + if ( $slugUsedBy ) { + $this->triggerError(sprintf( + 'Plugin slug "%s" is already in use by %s. Slugs must be unique.', + htmlentities($this->slug), + htmlentities($slugUsedBy) + ), E_USER_ERROR); + } + add_filter($slugCheckFilter, array($this, 'getAbsolutePath')); + + //Backwards compatibility: If the plugin is a mu-plugin but no $muPluginFile is specified, assume + //it's the same as $pluginFile given that it's not in a subdirectory (WP only looks in the base dir). + if ( (strpbrk($this->pluginFile, '/\\') === false) && $this->isUnknownMuPlugin() ) { + $this->muPluginFile = $this->pluginFile; + } + + parent::__construct($metadataUrl, dirname($this->pluginFile), $slug, $checkPeriod, $optionName); + } + + /** + * Create an instance of the scheduler. + * + * @param int $checkPeriod + * @return Puc_v4p2_Scheduler + */ + protected function createScheduler($checkPeriod) { + $scheduler = new Puc_v4p2_Scheduler($this, $checkPeriod, array('load-plugins.php')); + register_deactivation_hook($this->pluginFile, array($scheduler, 'removeUpdaterCron')); + return $scheduler; + } + + /** + * Install the hooks required to run periodic update checks and inject update info + * into WP data structures. + * + * @return void + */ + protected function installHooks(){ + //Override requests for plugin information + add_filter('plugins_api', array($this, 'injectInfo'), 20, 3); + + add_filter('plugin_row_meta', array($this, 'addCheckForUpdatesLink'), 10, 2); + add_action('admin_init', array($this, 'handleManualCheck')); + add_action('all_admin_notices', array($this, 'displayManualCheckResult')); + + //Clear the version number cache when something - anything - is upgraded or WP clears the update cache. + add_filter('upgrader_post_install', array($this, 'clearCachedVersion')); + add_action('delete_site_transient_update_plugins', array($this, 'clearCachedVersion')); + + parent::installHooks(); + } + + /** + * Retrieve plugin info from the configured API endpoint. + * + * @uses wp_remote_get() + * + * @param array $queryArgs Additional query arguments to append to the request. Optional. + * @return Puc_v4p2_Plugin_Info + */ + public function requestInfo($queryArgs = array()) { + list($pluginInfo, $result) = $this->requestMetadata('Puc_v4p2_Plugin_Info', 'request_info', $queryArgs); + + if ( $pluginInfo !== null ) { + /** @var Puc_v4p2_Plugin_Info $pluginInfo */ + $pluginInfo->filename = $this->pluginFile; + $pluginInfo->slug = $this->slug; + } + + $pluginInfo = apply_filters($this->getUniqueName('request_info_result'), $pluginInfo, $result); + return $pluginInfo; + } + + /** + * Retrieve the latest update (if any) from the configured API endpoint. + * + * @uses PluginUpdateChecker::requestInfo() + * + * @return Puc_v4p2_Update|null An instance of Plugin_Update, or NULL when no updates are available. + */ + public function requestUpdate() { + //For the sake of simplicity, this function just calls requestInfo() + //and transforms the result accordingly. + $pluginInfo = $this->requestInfo(array('checking_for_updates' => '1')); + if ( $pluginInfo === null ){ + return null; + } + $update = Puc_v4p2_Plugin_Update::fromPluginInfo($pluginInfo); + + $update = $this->filterUpdateResult($update); + + return $update; + } + + /** + * Get the currently installed version of the plugin. + * + * @return string Version number. + */ + public function getInstalledVersion(){ + if ( isset($this->cachedInstalledVersion) ) { + return $this->cachedInstalledVersion; + } + + $pluginHeader = $this->getPluginHeader(); + if ( isset($pluginHeader['Version']) ) { + $this->cachedInstalledVersion = $pluginHeader['Version']; + return $pluginHeader['Version']; + } else { + //This can happen if the filename points to something that is not a plugin. + $this->triggerError( + sprintf( + "Can't to read the Version header for '%s'. The filename is incorrect or is not a plugin.", + $this->pluginFile + ), + E_USER_WARNING + ); + return null; + } + } + + /** + * Get plugin's metadata from its file header. + * + * @return array + */ + protected function getPluginHeader() { + if ( !is_file($this->pluginAbsolutePath) ) { + //This can happen if the plugin filename is wrong. + $this->triggerError( + sprintf( + "Can't to read the plugin header for '%s'. The file does not exist.", + $this->pluginFile + ), + E_USER_WARNING + ); + return array(); + } + + if ( !function_exists('get_plugin_data') ){ + /** @noinspection PhpIncludeInspection */ + require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); + } + return get_plugin_data($this->pluginAbsolutePath, false, false); + } + + /** + * @return array + */ + protected function getHeaderNames() { + return array( + 'Name' => 'Plugin Name', + 'PluginURI' => 'Plugin URI', + 'Version' => 'Version', + 'Description' => 'Description', + 'Author' => 'Author', + 'AuthorURI' => 'Author URI', + 'TextDomain' => 'Text Domain', + 'DomainPath' => 'Domain Path', + 'Network' => 'Network', + + //The newest WordPress version that this plugin requires or has been tested with. + //We support several different formats for compatibility with other libraries. + 'Tested WP' => 'Tested WP', + 'Requires WP' => 'Requires WP', + 'Tested up to' => 'Tested up to', + 'Requires at least' => 'Requires at least', + ); + } + + + /** + * Intercept plugins_api() calls that request information about our plugin and + * use the configured API endpoint to satisfy them. + * + * @see plugins_api() + * + * @param mixed $result + * @param string $action + * @param array|object $args + * @return mixed + */ + public function injectInfo($result, $action = null, $args = null){ + $relevant = ($action == 'plugin_information') && isset($args->slug) && ( + ($args->slug == $this->slug) || ($args->slug == dirname($this->pluginFile)) + ); + if ( !$relevant ) { + return $result; + } + + $pluginInfo = $this->requestInfo(); + $pluginInfo = apply_filters($this->getUniqueName('pre_inject_info'), $pluginInfo); + if ( $pluginInfo ) { + return $pluginInfo->toWpFormat(); + } + + return $result; + } + + protected function shouldShowUpdates() { + //No update notifications for mu-plugins unless explicitly enabled. The MU plugin file + //is usually different from the main plugin file so the update wouldn't show up properly anyway. + return !$this->isUnknownMuPlugin(); + } + + /** + * @param stdClass|null $updates + * @param stdClass $updateToAdd + * @return stdClass + */ + protected function addUpdateToList($updates, $updateToAdd) { + if ( $this->isMuPlugin() ) { + //WP does not support automatic update installation for mu-plugins, but we can + //still display a notice. + $updateToAdd->package = null; + } + return parent::addUpdateToList($updates, $updateToAdd); + } + + /** + * @param stdClass|null $updates + * @return stdClass|null + */ + protected function removeUpdateFromList($updates) { + $updates = parent::removeUpdateFromList($updates); + if ( !empty($this->muPluginFile) && isset($updates, $updates->response) ) { + unset($updates->response[$this->muPluginFile]); + } + return $updates; + } + + /** + * For plugins, the update array is indexed by the plugin filename relative to the "plugins" + * directory. Example: "plugin-name/plugin.php". + * + * @return string + */ + protected function getUpdateListKey() { + if ( $this->isMuPlugin() ) { + return $this->muPluginFile; + } + return $this->pluginFile; + } + + /** + * Alias for isBeingUpgraded(). + * + * @deprecated + * @param WP_Upgrader|null $upgrader The upgrader that's performing the current update. + * @return bool + */ + public function isPluginBeingUpgraded($upgrader = null) { + return $this->isBeingUpgraded($upgrader); + } + + /** + * Is there an update being installed for this plugin, right now? + * + * @param WP_Upgrader|null $upgrader + * @return bool + */ + public function isBeingUpgraded($upgrader = null) { + return $this->upgraderStatus->isPluginBeingUpgraded($this->pluginFile, $upgrader); + } + + /** + * Get the details of the currently available update, if any. + * + * If no updates are available, or if the last known update version is below or equal + * to the currently installed version, this method will return NULL. + * + * Uses cached update data. To retrieve update information straight from + * the metadata URL, call requestUpdate() instead. + * + * @return Puc_v4p2_Plugin_Update|null + */ + public function getUpdate() { + $update = parent::getUpdate(); + if ( isset($update) ) { + /** @var Puc_v4p2_Plugin_Update $update */ + $update->filename = $this->pluginFile; + } + return $update; + } + + /** + * Add a "Check for updates" link to the plugin row in the "Plugins" page. By default, + * the new link will appear after the "Visit plugin site" link. + * + * You can change the link text by using the "puc_manual_check_link-$slug" filter. + * Returning an empty string from the filter will disable the link. + * + * @param array $pluginMeta Array of meta links. + * @param string $pluginFile + * @return array + */ + public function addCheckForUpdatesLink($pluginMeta, $pluginFile) { + $isRelevant = ($pluginFile == $this->pluginFile) + || (!empty($this->muPluginFile) && $pluginFile == $this->muPluginFile); + + if ( $isRelevant && $this->userCanInstallUpdates() ) { + $linkUrl = wp_nonce_url( + add_query_arg( + array( + 'puc_check_for_updates' => 1, + 'puc_slug' => $this->slug, + ), + self_admin_url('plugins.php') + ), + 'puc_check_for_updates' + ); + + $linkText = apply_filters( + $this->getUniqueName('manual_check_link'), + __('Check for updates', 'plugin-update-checker') + ); + if ( !empty($linkText) ) { + /** @noinspection HtmlUnknownTarget */ + $pluginMeta[] = sprintf('%s', esc_attr($linkUrl), $linkText); + } + } + return $pluginMeta; + } + + /** + * Check for updates when the user clicks the "Check for updates" link. + * @see self::addCheckForUpdatesLink() + * + * @return void + */ + public function handleManualCheck() { + $shouldCheck = + isset($_GET['puc_check_for_updates'], $_GET['puc_slug']) + && $_GET['puc_slug'] == $this->slug + && $this->userCanInstallUpdates() + && check_admin_referer('puc_check_for_updates'); + + if ( $shouldCheck ) { + $update = $this->checkForUpdates(); + $status = ($update === null) ? 'no_update' : 'update_available'; + wp_redirect(add_query_arg( + array( + 'puc_update_check_result' => $status, + 'puc_slug' => $this->slug, + ), + self_admin_url('plugins.php') + )); + } + } + + /** + * Display the results of a manual update check. + * @see self::handleManualCheck() + * + * You can change the result message by using the "puc_manual_check_message-$slug" filter. + */ + public function displayManualCheckResult() { + if ( isset($_GET['puc_update_check_result'], $_GET['puc_slug']) && ($_GET['puc_slug'] == $this->slug) ) { + $status = strval($_GET['puc_update_check_result']); + $title = $this->getPluginTitle(); + if ( $status == 'no_update' ) { + $message = sprintf(_x('The %s plugin is up to date.', 'the plugin title', 'plugin-update-checker'), $title); + } else if ( $status == 'update_available' ) { + $message = sprintf(_x('A new version of the %s plugin is available.', 'the plugin title', 'plugin-update-checker'), $title); + } else { + $message = sprintf(__('Unknown update checker status "%s"', 'plugin-update-checker'), htmlentities($status)); + } + printf( + '
%s
This section will be displayed by default when the user clicks 'View version x.y.z details'.
", + "custom_section": "This is a custom section labeled 'Custom Section'." + }, + + "banners": { + "low": "http://w-shadow.com/files/external-update-example/assets/banner-772x250.png", + "high": "http://w-shadow.com/files/external-update-example/assets/banner-1544x500.png" + }, + + "translations": [ + { + "language": "fr_FR", + "version": "4.0", + "updated": "2016-04-22 23:22:42", + "package": "http://example.com/updates/translations/french-language-pack.zip" + }, + { + "language": "de_DE", + "version": "5.0", + "updated": "2016-04-22 23:22:42", + "package": "http://example.com/updates/translations/german-language-pack.zip" + } + ], + + "rating": 90, + "num_ratings": 123, + + "downloaded": 1234, + "active_installs": 12345 +} \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/examples/theme.json b/includes/vendor/plugin-update-checker/examples/theme.json new file mode 100755 index 0000000..df6c8c7 --- /dev/null +++ b/includes/vendor/plugin-update-checker/examples/theme.json @@ -0,0 +1,5 @@ +{ + "version": "2.0", + "details_url": "http://example.com/version-2.0-details.html", + "download_url": "http://example.com/example-theme-2.0.zip" +} \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/js/debug-bar.js b/includes/vendor/plugin-update-checker/js/debug-bar.js new file mode 100755 index 0000000..b8435db --- /dev/null +++ b/includes/vendor/plugin-update-checker/js/debug-bar.js @@ -0,0 +1,52 @@ +jQuery(function($) { + + function runAjaxAction(button, action) { + button = $(button); + var panel = button.closest('.puc-debug-bar-panel-v4'); + var responseBox = button.closest('td').find('.puc-ajax-response'); + + responseBox.text('Processing...').show(); + $.post( + ajaxurl, + { + action : action, + uid : panel.data('uid'), + _wpnonce: panel.data('nonce') + }, + function(data) { + responseBox.html(data); + }, + 'html' + ); + } + + $('.puc-debug-bar-panel-v4 input[name="puc-check-now-button"]').click(function() { + runAjaxAction(this, 'puc_v4_debug_check_now'); + return false; + }); + + $('.puc-debug-bar-panel-v4 input[name="puc-request-info-button"]').click(function() { + runAjaxAction(this, 'puc_v4_debug_request_info'); + return false; + }); + + + // Debug Bar uses the panel class name as part of its link and container IDs. This means we can + // end up with multiple identical IDs if more than one plugin uses the update checker library. + // Fix it by replacing the class name with the plugin slug. + var panels = $('#debug-menu-targets').find('.puc-debug-bar-panel-v4'); + panels.each(function() { + var panel = $(this); + var uid = panel.data('uid'); + var target = panel.closest('.debug-menu-target'); + + //Change the panel wrapper ID. + target.attr('id', 'debug-menu-target-puc-' + uid); + + //Change the menu link ID as well and point it at the new target ID. + $('#debug-bar-menu').find('.puc-debug-menu-link-' + uid) + .closest('.debug-menu-link') + .attr('id', 'debug-menu-link-puc-' + uid) + .attr('href', '#' + target.attr('id')); + }); +}); \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/languages/plugin-update-checker-cs_CZ.mo b/includes/vendor/plugin-update-checker/languages/plugin-update-checker-cs_CZ.mo new file mode 100755 index 0000000..ac6d20e Binary files /dev/null and b/includes/vendor/plugin-update-checker/languages/plugin-update-checker-cs_CZ.mo differ diff --git a/includes/vendor/plugin-update-checker/languages/plugin-update-checker-cs_CZ.po b/includes/vendor/plugin-update-checker/languages/plugin-update-checker-cs_CZ.po new file mode 100755 index 0000000..2c9af18 --- /dev/null +++ b/includes/vendor/plugin-update-checker/languages/plugin-update-checker-cs_CZ.po @@ -0,0 +1,45 @@ +msgid "" +msgstr "" +"Project-Id-Version: plugin-update-checker\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-05-20 10:53+0300\n" +"PO-Revision-Date: 2017-07-05 15:39+0000\n" +"Last-Translator: Vojtěch Sajdl') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "
"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + # + # Static Methods + # + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'sub', 'mark', + 'u', 'xm', 'sup', 'nobr', + 'var', 'ruby', + 'wbr', 'span', + 'time', + ); +} \ No newline at end of file diff --git a/includes/vendor/plugin-update-checker/vendor/ParsedownLegacy.php b/includes/vendor/plugin-update-checker/vendor/ParsedownLegacy.php new file mode 100755 index 0000000..71a033e --- /dev/null +++ b/includes/vendor/plugin-update-checker/vendor/ParsedownLegacy.php @@ -0,0 +1,1535 @@ +DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + $markup = $this->lines($lines); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $DefinitionTypes = array( + '[' => array('Reference'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + private function lines(array $lines) + { + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = true; + } + + continue; + } + + if (strpos($line, "\t") !== false) + { + $parts = explode("\t", $line); + + $line = $parts[0]; + + unset($parts[0]); + + foreach ($parts as $part) + { + $shortage = 4 - mb_strlen($line, 'utf-8') % 4; + + $line .= str_repeat(' ', $shortage); + $line .= $part; + } + } + + $indent = 0; + + while (isset($line[$indent]) and $line[$indent] === ' ') + { + $indent ++; + } + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['incomplete'])) + { + $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if (method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + unset($CurrentBlock['incomplete']); + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{'block'.$blockType}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + $Blocks []= $CurrentBlock; + + $Block['identified'] = true; + } + + if (method_exists($this, 'block'.$blockType.'Continue')) + { + $Block['incomplete'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted'])) + { + $CurrentBlock['element']['text'] .= "\n".$text; + } + else + { + $Blocks []= $CurrentBlock; + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['incomplete']) and method_exists($this, 'block'.$CurrentBlock['type'].'Complete')) + { + $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock); + } + + # ~ + + $Blocks []= $CurrentBlock; + + unset($Blocks[0]); + + # ~ + + $markup = ''; + + foreach ($Blocks as $Block) + { + if (isset($Block['hidden'])) + { + continue; + } + + $markup .= "\n"; + $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']); + } + + $markup .= "\n"; + + # ~ + + return $markup; + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['element']['text']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['text']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!') + { + $Block = array( + 'markup' => $Line['body'], + ); + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['markup'] .= "\n" . $Line['body']; + + if (preg_match('/-->$/', $Line['text'])) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + if (preg_match('/^(['.$Line['text'][0].']{3,})[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches)) + { + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if (isset($matches[2])) + { + $class = 'language-'.$matches[2]; + + $Element['attributes'] = array( + 'class' => $class, + ); + } + + $Block = array( + 'char' => $Line['text'][0], + 'element' => array( + 'name' => 'pre', + 'handler' => 'element', + 'text' => $Element, + ), + ); + + return $Block; + } + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['text']['text'] .= "\n"; + + unset($Block['interrupted']); + } + + if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text'])) + { + $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['text']['text'] .= "\n".$Line['body'];; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + $text = $Block['element']['text']['text']; + + $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8'); + + $Block['element']['text']['text'] = $text; + + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = 1; + + while (isset($Line['text'][$level]) and $Line['text'][$level] === '#') + { + $level ++; + } + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '# '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + ), + ); + + return $Block; + } + } + + # + # List + + protected function blockList($Line) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]'); + + if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'element' => array( + 'name' => $name, + 'handler' => 'elements', + ), + ); + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $matches[2], + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['li']['text'] []= ''; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => 'li', + 'text' => array( + $text, + ), + ); + + $Block['element']['text'] []= & $Block['li']; + + return $Block; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + return $Block; + } + + if ($Line['indent'] > 0) + { + $Block['li']['text'] []= ''; + + $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']); + + $Block['li']['text'] []= $text; + + unset($Block['interrupted']); + + return $Block; + } + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => 'lines', + 'text' => (array) $matches[1], + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches)) + { + if (isset($Block['interrupted'])) + { + $Block['element']['text'] []= ''; + + unset($Block['interrupted']); + } + + $Block['element']['text'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['text'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text'])) + { + $Block = array( + 'element' => array( + 'name' => 'hr' + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (chop($Line['text'], $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped) + { + return; + } + + if (preg_match('/^<(\w*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches)) + { + if (in_array($matches[1], $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'depth' => 0, + 'markup' => $Line['text'], + ); + + $length = strlen($matches[0]); + + $remainder = substr($Line['text'], $length); + + if (trim($remainder) === '') + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + $Block['closed'] = true; + + $Block['void'] = true; + } + } + else + { + if (isset($matches[2]) or in_array($matches[1], $this->voidElements)) + { + return; + } + + if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder)) + { + $Block['closed'] = true; + } + } + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open + { + $Block['depth'] ++; + } + + if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close + { + if ($Block['depth'] > 0) + { + $Block['depth'] --; + } + else + { + $Block['closed'] = true; + } + + $Block['markup'] .= $matches[1]; + } + + if (isset($Block['interrupted'])) + { + $Block['markup'] .= "\n"; + + unset($Block['interrupted']); + } + + $Block['markup'] .= "\n".$Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (preg_match('/^\[(.+?)\]:[ ]*(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches)) + { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => null, + ); + + if (isset($matches[3])) + { + $Data['title'] = $matches[3]; + } + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'hidden' => true, + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted'])) + { + return; + } + + if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '') + { + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + continue; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['text']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'text' => $headerCell, + 'handler' => 'line', + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => 'text-align: '.$alignment.';', + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'handler' => 'elements', + ), + ); + + $Block['element']['text'] []= array( + 'name' => 'thead', + 'handler' => 'elements', + ); + + $Block['element']['text'] []= array( + 'name' => 'tbody', + 'handler' => 'elements', + 'text' => array(), + ); + + $Block['element']['text'][0]['text'] []= array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $HeaderElements, + ); + + return $Block; + } + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches); + + foreach ($matches[0] as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => 'line', + 'text' => $cell, + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: '.$Block['alignments'][$index].';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'handler' => 'elements', + 'text' => $Elements, + ); + + $Block['element']['text'][1]['text'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + $Block = array( + 'element' => array( + 'name' => 'p', + 'text' => $Line['text'], + 'handler' => 'line', + ), + ); + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '"' => array('SpecialCharacter'), + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'), + '>' => array('SpecialCharacter'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!"*_&[:<>`~\\'; + + # + # ~ + # + + public function line($text) + { + $markup = ''; + + $unexaminedText = $text; + + $markerPosition = 0; + + while ($excerpt = strpbrk($unexaminedText, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition += strpos($unexaminedText, $marker); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + $Inline = $this->{'inline'.$inlineType}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) # position is ahead of marker + { + continue; + } + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + $unmarkedText = substr($text, 0, $Inline['position']); + + $markup .= $this->unmarkedText($unmarkedText); + + $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']); + + $text = substr($text, $Inline['position'] + $Inline['extent']); + + $unexaminedText = $text; + + $markerPosition = 0; + + continue 2; + } + + $unexaminedText = substr($excerpt, 1); + + $markerPosition ++; + } + + $markup .= $this->unmarkedText($text); + + return $markup; + } + + # + # ~ + # + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(? strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = 'mailto:' . $url; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => 'line', + 'text' => $matches[1], + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'markup' => $Excerpt['text'][1], + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['text'], + ), + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => 'line', + 'text' => null, + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]|(?R))*)\]/', $remainder, $matches)) + { + $Element['text'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]((?:[^ (]|[(][^ )]+[)])+)(?:[ ]+("[^"]+"|\'[^\']+\'))?[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = $matches[1] ? $matches[1] : $Element['text']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['text']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + $Element['attributes']['href'] = str_replace(array('&', '<'), array('&', '<'), $Element['attributes']['href']); + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w*[ ]*>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'markup' => $matches[0], + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if ($Excerpt['text'][0] === '&' and ! preg_match('/^?\w+;/', $Excerpt['text'])) + { + return array( + 'markup' => '&', + 'extent' => 1, + ); + } + + $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot'); + + if (isset($SpecialCharacter[$Excerpt['text'][0]])) + { + return array( + 'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';', + 'extent' => 1, + ); + } + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'text' => $matches[1], + 'handler' => 'line', + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE)) + { + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $matches[0][0], + 'attributes' => array( + 'href' => $matches[0][0], + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches)) + { + $url = str_replace(array('&', '<'), array('&', '<'), $matches[1]); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # + # ~ + + protected $unmarkedInlineTypes = array("\n" => 'Break', '://' => 'Url'); + + # ~ + + protected function unmarkedText($text) + { + if ($this->breaksEnabled) + { + $text = preg_replace('/[ ]*\n/', "') + { + $markup = $trimmedMarkup; + $markup = substr($markup, 3); + + $position = strpos($markup, "
"); + + $markup = substr_replace($markup, '', $position, 4); + } + + return $markup; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + # + # Static Methods + # + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new self(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'sub', 'mark', + 'u', 'xm', 'sup', 'nobr', + 'var', 'ruby', + 'wbr', 'span', + 'time', + ); +} diff --git a/includes/vendor/plugin-update-checker/vendor/readme-parser.php b/includes/vendor/plugin-update-checker/vendor/readme-parser.php new file mode 100755 index 0000000..d89a06e --- /dev/null +++ b/includes/vendor/plugin-update-checker/vendor/readme-parser.php @@ -0,0 +1,334 @@ +parse_readme_contents( $file_contents ); + } + + function parse_readme_contents( $file_contents ) { + $file_contents = str_replace(array("\r\n", "\r"), "\n", $file_contents); + $file_contents = trim($file_contents); + if ( 0 === strpos( $file_contents, "\xEF\xBB\xBF" ) ) + $file_contents = substr( $file_contents, 3 ); + + // Markdown transformations + $file_contents = preg_replace( "|^###([^#]+)#*?\s*?\n|im", '=$1='."\n", $file_contents ); + $file_contents = preg_replace( "|^##([^#]+)#*?\s*?\n|im", '==$1=='."\n", $file_contents ); + $file_contents = preg_replace( "|^#([^#]+)#*?\s*?\n|im", '===$1==='."\n", $file_contents ); + + // === Plugin Name === + // Must be the very first thing. + if ( !preg_match('|^===(.*)===|', $file_contents, $_name) ) + return array(); // require a name + $name = trim($_name[1], '='); + $name = $this->sanitize_text( $name ); + + $file_contents = $this->chop_string( $file_contents, $_name[0] ); + + + // Requires at least: 1.5 + if ( preg_match('|Requires at least:(.*)|i', $file_contents, $_requires_at_least) ) + $requires_at_least = $this->sanitize_text($_requires_at_least[1]); + else + $requires_at_least = NULL; + + + // Tested up to: 2.1 + if ( preg_match('|Tested up to:(.*)|i', $file_contents, $_tested_up_to) ) + $tested_up_to = $this->sanitize_text( $_tested_up_to[1] ); + else + $tested_up_to = NULL; + + + // Stable tag: 10.4-ride-the-fire-eagle-danger-day + if ( preg_match('|Stable tag:(.*)|i', $file_contents, $_stable_tag) ) + $stable_tag = $this->sanitize_text( $_stable_tag[1] ); + else + $stable_tag = NULL; // we assume trunk, but don't set it here to tell the difference between specified trunk and default trunk + + + // Tags: some tag, another tag, we like tags + if ( preg_match('|Tags:(.*)|i', $file_contents, $_tags) ) { + $tags = preg_split('|,[\s]*?|', trim($_tags[1])); + foreach ( array_keys($tags) as $t ) + $tags[$t] = $this->sanitize_text( $tags[$t] ); + } else { + $tags = array(); + } + + + // Contributors: markjaquith, mdawaffe, zefrank + $contributors = array(); + if ( preg_match('|Contributors:(.*)|i', $file_contents, $_contributors) ) { + $temp_contributors = preg_split('|,[\s]*|', trim($_contributors[1])); + foreach ( array_keys($temp_contributors) as $c ) { + $tmp_sanitized = $this->user_sanitize( $temp_contributors[$c] ); + if ( strlen(trim($tmp_sanitized)) > 0 ) + $contributors[$c] = $tmp_sanitized; + unset($tmp_sanitized); + } + } + + + // Donate Link: URL + if ( preg_match('|Donate link:(.*)|i', $file_contents, $_donate_link) ) + $donate_link = esc_url( $_donate_link[1] ); + else + $donate_link = NULL; + + + // togs, conts, etc are optional and order shouldn't matter. So we chop them only after we've grabbed their values. + foreach ( array('tags', 'contributors', 'requires_at_least', 'tested_up_to', 'stable_tag', 'donate_link') as $chop ) { + if ( $$chop ) { + $_chop = '_' . $chop; + $file_contents = $this->chop_string( $file_contents, ${$_chop}[0] ); + } + } + + $file_contents = trim($file_contents); + + + // short-description fu + if ( !preg_match('/(^(.*?))^[\s]*=+?[\s]*.+?[\s]*=+?/ms', $file_contents, $_short_description) ) + $_short_description = array( 1 => &$file_contents, 2 => &$file_contents ); + $short_desc_filtered = $this->sanitize_text( $_short_description[2] ); + $short_desc_length = strlen($short_desc_filtered); + $short_description = substr($short_desc_filtered, 0, 150); + if ( $short_desc_length > strlen($short_description) ) + $truncated = true; + else + $truncated = false; + if ( $_short_description[1] ) + $file_contents = $this->chop_string( $file_contents, $_short_description[1] ); // yes, the [1] is intentional + + // == Section == + // Break into sections + // $_sections[0] will be the title of the first section, $_sections[1] will be the content of the first section + // the array alternates from there: title2, content2, title3, content3... and so forth + $_sections = preg_split('/^[\s]*==[\s]*(.+?)[\s]*==/m', $file_contents, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY); + + $sections = array(); + for ( $i=1; $i <= count($_sections); $i +=2 ) { + $_sections[$i] = preg_replace('/(^[\s]*)=[\s]+(.+?)[\s]+=/m', '$1|)(.*?)(
|)!s", array( __CLASS__,'decodeit'), $text);
+
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
+ if ( !$markdown ) {
+ // This gets the "inline" code blocks, but can't be used with Markdown.
+ $text = preg_replace_callback("|(`)(.*?)`|", array( __CLASS__, 'encodeit'), $text);
+ // This gets the "block level" code blocks and converts them to PRE CODE
+ $text = preg_replace_callback("!(^|\n)`(.*?)`!s", array( __CLASS__, 'encodeit'), $text);
+ } else {
+ // Markdown can do inline code, we convert bbPress style block level code to Markdown style
+ $text = preg_replace_callback("!(^|\n)([ \t]*?)`(.*?)`!s", array( __CLASS__, 'indent'), $text);
+ }
+ return $text;
+ }
+
+ function indent( $matches ) {
+ $text = $matches[3];
+ $text = preg_replace('|^|m', $matches[2] . ' ', $text);
+ return $matches[1] . $text;
+ }
+
+ function encodeit( $matches ) {
+ if ( function_exists('encodeit') ) // bbPress native
+ return encodeit( $matches );
+
+ $text = trim($matches[2]);
+ $text = htmlspecialchars($text, ENT_QUOTES);
+ $text = str_replace(array("\r\n", "\r"), "\n", $text);
+ $text = preg_replace("|\n\n\n+|", "\n\n", $text);
+ $text = str_replace('<', '<', $text);
+ $text = str_replace('>', '>', $text);
+ $text = "$text
";
+ if ( "`" != $matches[1] )
+ $text = "$text"; + return $text; + } + + function decodeit( $matches ) { + if ( function_exists('decodeit') ) // bbPress native + return decodeit( $matches ); + + $text = $matches[2]; + $trans_table = array_flip(get_html_translation_table(HTML_ENTITIES)); + $text = strtr($text, $trans_table); + $text = str_replace('
' == $matches[1] )
+ $text = "\n$text\n";
+ return "`$text`";
+ }
+
+} // end class
+
+endif;
\ No newline at end of file
diff --git a/wampum-popups.php b/wampum-popups.php
index ce6e358..59e0aad 100755
--- a/wampum-popups.php
+++ b/wampum-popups.php
@@ -8,7 +8,7 @@
*
* @wordpress-plugin
* Plugin Name: Wampum - Popups
- * Description: A lightweight but flexible WordPress popups plugin
+ * Description: A lightweight but flexible WordPress popups plugin
* Plugin URI: https://github.com/bizbudding/wampum-popups
* Author: Mike Hemberger
* Author URI: https://bizbudding.com
@@ -16,10 +16,10 @@
* License: GPL-2.0+
* License URI: http://www.gnu.org/licenses/gpl-2.0.txt
*
- * Version: 2.0.1
+ * Version: 2.1.0
*
* GitHub Plugin URI: https://github.com/bizbudding/wampum-popups
- * GitHub Branch: master
+ * GitHub Branch: master
*/
// Exit if accessed directly.
@@ -31,7 +31,7 @@
* @since 1.0.0
*
* @param string $content The content to output in the popup
- * @param array $args Plugin options (type, style, time)
+ * @param array $args Plugin options (type, style, time)
*/
function wampum_popup( $content, $args = array() ) {
echo Wampum_Popups()->get_wampum_popup( $content, $args );
@@ -48,7 +48,7 @@ function get_wampum_popup( $content, $args = array() ) {
* @since 2.0.0
*
* @param string $content The content to output in the popup
- * @param array $args Plugin options (type & text only)
+ * @param array $args Plugin options (type & text only)
*/
function wampum_popup_link( $content, $args = array() ) {
// Params are backwards cause shortcodes have optional $content second
@@ -138,7 +138,7 @@ public function __wakeup() {
private function setup_constants() {
// Plugin version.
if ( ! defined( 'WAMPUM_POPUPS_VERSION' ) ) {
- define( 'WAMPUM_POPUPS_VERSION', '2.0.1' );
+ define( 'WAMPUM_POPUPS_VERSION', '2.1.0' );
}
// Plugin Folder Path.
if ( ! defined( 'WAMPUM_POPUPS_PLUGIN_DIR' ) ) {
@@ -187,6 +187,17 @@ function setup() {
// Add our custom popup hook
add_action( 'wp_footer', array( $this, 'popups_hook' ) );
+ /**
+ * Setup the updater.
+ *
+ * @uses https://github.com/YahnisElsts/plugin-update-checker/
+ *
+ * @return void
+ */
+ if ( ! class_exists( 'Puc_v4_Factory' ) ) {
+ require_once WAMPUM_POPUPS_INCLUDES_DIR . 'vendor/plugin-update-checker/plugin-update-checker.php';
+ }
+ $updater = Puc_v4_Factory::buildUpdateChecker( 'https://github.com/bizbudding/wampum-popups/', __FILE__, 'wampum-popups' );
}
/**
@@ -224,7 +235,7 @@ function scripts() {
* @return string Image URL
*/
function gallery_image_url( $html, $post_id, $size, $permalink ) {
- $size = apply_filters( 'wampum_popups_gallery_image_size', 'large' );
+ $size = apply_filters( 'wampum_popups_gallery_image_size', 'large' );
$image = wp_get_attachment_image_src( $post_id, $size );
return preg_replace( '/href=\'(.*?)\'/', 'href=\'' . $image[0] . '\'', $html );
}
@@ -302,22 +313,22 @@ function get_wampum_popup( $content = null, $args = array() ) {
// Popup args
$defaults = array(
- 'type' => null, // 'exit' or 'timed' or 'link' or 'button' or 'gallery (internal only)' (REQUIRED)
- 'close_button' => true, // whether or not to show the close button
- 'close_outside' => true, // whether or not to allow close by clicking outside the modal
- 'logged_in' => false, // whether or not to show only to logged in users
- 'logged_out' => false, // whether or not to show only to logged out users
- 'style' => 'modal', // 'modal' or 'slideup'
- 'time' => '4000', // time in milliseconds
- 'width' => '400px', // max-width of popup
- 'aggressive' => false, // ouibounce - true
- 'cookieExpire' => false, // ouibounce - 7
- 'cookieDomain' => false, // ouibounce - .example.com
- 'cookieName' => 'wampumPopupViewed', // ouibounce - 'custom_cookie_name'
- 'delay' => false, // ouibounce - 100
- 'sensitivity' => false, // ouibounce - 40
- 'sitewide' => true, // ouibounce - true (don't be annoying)
- 'timer' => false, // ouibounce - 10
+ 'type' => null, // 'exit' or 'timed' or 'link' or 'button' or 'gallery (internal only)' (REQUIRED)
+ 'close_button' => true, // whether or not to show the close button
+ 'close_outside' => true, // whether or not to allow close by clicking outside the modal
+ 'logged_in' => false, // whether or not to show only to logged in users
+ 'logged_out' => false, // whether or not to show only to logged out users
+ 'style' => 'modal', // 'modal' or 'slideup'
+ 'time' => '4000', // time in milliseconds
+ 'width' => '400px', // max-width of popup
+ 'aggressive' => false, // ouibounce - true
+ 'cookieExpire' => false, // ouibounce - 7
+ 'cookieDomain' => false, // ouibounce - .example.com
+ 'cookieName' => 'wampumPopupViewed', // ouibounce - 'custom_cookie_name'
+ 'delay' => false, // ouibounce - 100
+ 'sensitivity' => false, // ouibounce - 40
+ 'sitewide' => true, // ouibounce - true (don't be annoying)
+ 'timer' => false, // ouibounce - 10
);
$args = shortcode_atts( $defaults, $args, 'wampum_popup' );
@@ -344,8 +355,8 @@ function get_wampum_popup( $content = null, $args = array() ) {
if ( in_array( $args['type'], $ouibounce ) ) {
// Bail if popup is not aggressive and cookie has already been viewed
- $aggressive = filter_var( $args['aggressive'], FILTER_VALIDATE_BOOLEAN );
- $viewed = isset($_COOKIE[$args['cookieName']]) && filter_var( $_COOKIE[$args['cookieName']], FILTER_VALIDATE_BOOLEAN ) == true ? true : false;
+ $aggressive = filter_var( $args['aggressive'], FILTER_VALIDATE_BOOLEAN );
+ $viewed = isset($_COOKIE[$args['cookieName']]) && filter_var( $_COOKIE[$args['cookieName']], FILTER_VALIDATE_BOOLEAN ) == true ? true : false;
if ( ! $aggressive && $viewed ) {
return;
}
@@ -403,26 +414,26 @@ function get_wampum_popup_html( $content, $args, $index = null ) {
*/
function get_localize_script_args( $args ) {
$popup_args = array(
- 'close_button' => $args['close_button'],
- 'close_outside' => $args['close_outside'],
- 'style' => $args['style'],
- 'time' => $args['time'],
- 'type' => $args['type'],
- 'width' => $args['width'],
+ 'close_button' => $args['close_button'],
+ 'close_outside' => $args['close_outside'],
+ 'style' => $args['style'],
+ 'time' => $args['time'],
+ 'type' => $args['type'],
+ 'width' => $args['width'],
);
$ouibounce_args = array(
- 'aggressive' => $args['aggressive'],
- 'cookieExpire' => $args['cookieExpire'],
- 'cookieDomain' => $args['cookieDomain'],
- 'cookieName' => $args['cookieName'],
- 'delay' => $args['delay'],
- 'sensitivity' => $args['sensitivity'],
- 'sitewide' => $args['sitewide'],
- 'timer' => $args['timer'],
+ 'aggressive' => $args['aggressive'],
+ 'cookieExpire' => $args['cookieExpire'],
+ 'cookieDomain' => $args['cookieDomain'],
+ 'cookieName' => $args['cookieName'],
+ 'delay' => $args['delay'],
+ 'sensitivity' => $args['sensitivity'],
+ 'sitewide' => $args['sitewide'],
+ 'timer' => $args['timer'],
);
return array(
- 'wampumpopups' => $popup_args,
- 'ouibounce' => $this->ouibounce_args( $ouibounce_args ),
+ 'wampumpopups' => $popup_args,
+ 'ouibounce' => $this->ouibounce_args( $ouibounce_args ),
);
}
@@ -439,14 +450,14 @@ function ouibounce_args( $ouibounce_args ) {
$args = array();
// Script defaults
$defaults = array(
- 'aggressive' => false, // true
- 'cookieExpire' => false, // 7
- 'cookieDomain' => false, // .example.com
- 'cookieName' => false, // 'custom_cookie_name'
- 'delay' => false, // 100
- 'sensitivity' => false, // 40
- 'sitewide' => true, // true (don't be annoying)
- 'timer' => false, // 10
+ 'aggressive' => false, // true
+ 'cookieExpire' => false, // 7
+ 'cookieDomain' => false, // .example.com
+ 'cookieName' => false, // 'custom_cookie_name'
+ 'delay' => false, // 100
+ 'sensitivity' => false, // 40
+ 'sitewide' => true, // true (don't be annoying)
+ 'timer' => false, // 10
);
$ouibounce_args = wp_parse_args( $ouibounce_args, $defaults );
foreach ( $ouibounce_args as $key => $value ) {
@@ -465,11 +476,11 @@ function gallery_popup() {
}
// Output popup with empty span so it forces display
$args = array(
- 'aggressive' => true,
- 'close_outside' => false,
- 'style' => 'modal',
- 'type' => 'gallery',
- 'width' => 'auto',
+ 'aggressive' => true,
+ 'close_outside' => false,
+ 'style' => 'modal',
+ 'type' => 'gallery',
+ 'width' => 'auto',
);
wampum_popup( '', $args );
}
@@ -477,9 +488,9 @@ function gallery_popup() {
/**
* Add a new hook so devs can safely add a new popup without things breaking if this plugin gets deactivated
*
- * @since 1.1.0
+ * @since 1.1.0
*
- * @return null
+ * @return null
*/
function popups_hook() {