public static function update() { $file_id = (int) $_REQUEST['file_id']; $file = MediaFile::find_by_id($file_id); if (isset($_REQUEST['slug'])) { self::simulate_temporary_episode_slug($_REQUEST['slug']); } $info = $file->determine_file_size(); $file->save(); $result = array(); $result['file_url'] = $file->get_file_url(); $result['file_id'] = $file_id; $result['reachable'] = podlove_is_resolved_and_reachable_http_status($info['http_code']); $result['file_size'] = $file->size; if (!$result['reachable']) { $info['certinfo'] = print_r($info['certinfo'], true); $info['php_open_basedir'] = ini_get('open_basedir'); $info['php_safe_mode'] = ini_get('safe_mode'); $info['php_curl'] = in_array('curl', get_loaded_extensions()); $info['curl_exec'] = function_exists('curl_exec'); $errorLog = "--- # Can't reach {$file->get_file_url()}\n"; $errorLog .= "--- # Please include this output when you report a bug\n"; foreach ($info as $key => $value) { $errorLog .= "{$key}: {$value}\n"; } \Podlove\Log::get()->addError($errorLog); } Ajax::respond_with_json($result); }
/** * Register Event for Auphonic Webhook */ public function auphonic_webhook() { if (!isset($_REQUEST['podlove-auphonic-production']) || empty($_REQUEST['podlove-auphonic-production']) || empty($_POST)) { return; } if ($_POST['status_string'] !== 'Done') { return; } $post_id = (int) $_REQUEST['podlove-auphonic-production']; $episodes_to_be_remote_published = get_option('podlove_episodes_to_be_remote_published'); if (!is_array($episodes_to_be_remote_published) || !isset($episodes_to_be_remote_published[$post_id])) { return; } $episode = $episodes_to_be_remote_published[$post_id]; $auth_key = $episode['auth_key']; $action = $episode['action']; if ($_REQUEST['authkey'] !== $auth_key) { \Podlove\Log::get()->addWarning('Auphonic webhook failed. AuthKey mismatch.', ['post_id' => $post_id]); return; } // Update episode with production results $this->update_production_data($post_id); if ($action == 'publish') { wp_publish_post($post_id); } unset($episodes_to_be_remote_published[$post_id]); update_option('podlove_episodes_to_be_remote_published', $episodes_to_be_remote_published); }
public function register_database_logger() { global $wpdb; $log = Log::get(); // write logs to database $log->pushHandler(new WPDBHandler($wpdb, $log->get_log_level())); // send critical logs via email // $log->pushHandler( new WPMailHandler( get_option( 'admin_email' ), "Podlove | Critical notice for " . get_option( 'blogname' ), Logger::CRITICAL ) ); }
private function validate_post(\WP_Post $post) { $episode = Model\Episode::find_or_create_by_post_id($post->ID); if ($episode && $episode->is_valid()) { Log::get()->addInfo('Validate episode', array('episode_id' => $episode->id)); $episode->refetch_files(); update_post_meta($post->ID, '_podlove_last_validated_at', time()); } }
/** * Apply Twig to given template * * @param string $html File path or HTML string. * @param array $vars optional variables for Twig context * @return string rendered template string */ public static function apply_to_html($html, $vars = array()) { // file loader for internal use $file_loader = new \Twig_Loader_Filesystem(); $file_loader->addPath(implode(DIRECTORY_SEPARATOR, array(\Podlove\PLUGIN_DIR, 'templates')), 'core'); // other modules can register their own template directories/namespaces $file_loader = apply_filters('podlove_twig_file_loader', $file_loader); // database loader for user templates $db_loader = new TwigLoaderPodloveDatabase(); $loaders = array($file_loader, $db_loader); $loaders = apply_filters('podlove_twig_loaders', $loaders); $loader = new \Twig_Loader_Chain($loaders); $twig = new \Twig_Environment($loader, array('autoescape' => false)); $twig->addExtension(new \Twig_Extensions_Extension_I18n()); $twig->addExtension(new \Twig_Extensions_Extension_Date()); $formatBytesFilter = new \Twig_SimpleFilter('formatBytes', function ($string) { return \Podlove\format_bytes($string, 0); }); $padLeftFilter = new \Twig_SimpleFilter('padLeft', function ($string, $padChar, $length) { while (strlen($string) < $length) { $string = $padChar . $string; } return $string; }); $twig->addFilter($formatBytesFilter); $twig->addFilter($padLeftFilter); // add functions foreach (self::$template_tags as $tag) { $func = new \Twig_SimpleFunction($tag, function () use($tag) { return $tag(); }); $twig->addFunction($func); } $context = ['option' => $vars]; // add podcast to global context $context = array_merge($context, ['podcast' => new Podcast(Model\Podcast::get())]); // Apply filters to twig templates $context = apply_filters('podlove_templates_global_context', $context); // add podcast to global context if we are in an episode if ($episode = Model\Episode::find_one_by_property('post_id', get_the_ID())) { $context = array_merge($context, array('episode' => new Episode($episode))); } try { return $twig->render($html, $context); } catch (\Twig_Error $e) { $message = $e->getRawMessage(); $line = $e->getTemplateLine(); $template = $e->getTemplateFile(); \Podlove\Log::get()->addError($message, ['type' => 'twig', 'line' => $line, 'template' => $template]); } return ""; }
function podlove_handle_media_file_tracking(\Podlove\Model\MediaFile $media_file) { if (\Podlove\get_setting('tracking', 'mode') !== "ptm_analytics") { return; } if (strtoupper($_SERVER['REQUEST_METHOD']) === 'HEAD') { return; } $intent = new Model\DownloadIntent(); $intent->media_file_id = $media_file->id; $intent->accessed_at = date('Y-m-d H:i:s'); $ptm_source = trim(podlove_get_query_var('ptm_source')); $ptm_context = trim(podlove_get_query_var('ptm_context')); if ($ptm_source) { $intent->source = $ptm_source; } if ($ptm_context) { $intent->context = $ptm_context; } // set user agent $ua_string = trim($_SERVER['HTTP_USER_AGENT']); if ($agent = Model\UserAgent::find_or_create_by_uastring($ua_string)) { $intent->user_agent_id = $agent->id; } // save HTTP range header // @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35 for spec if (isset($_SERVER['HTTP_RANGE'])) { $intent->httprange = $_SERVER['HTTP_RANGE']; } // get ip, but don't store it $ip_string = $_SERVER['REMOTE_ADDR']; try { $ip = IP\Address::factory($_SERVER['REMOTE_ADDR']); if (method_exists($ip, 'as_IPv6_address')) { $ip = $ip->as_IPv6_address(); } $ip_string = $ip->format(IP\Address::FORMAT_COMPACT); } catch (\InvalidArgumentException $e) { \Podlove\Log::get()->addWarning('Could not use IP "' . $_SERVER['REMOTE_ADDR'] . '"' . $e->getMessage()); } // Generate a hash from IP address and UserAgent so we can identify // identical requests without storing an IP address. if (function_exists('openssl_digest')) { $intent->request_id = openssl_digest($ip_string . $ua_string, 'sha256'); } else { $intent->request_id = sha1($ip_string . $ua_string); } $intent = $intent->add_geo_data($ip_string); $intent->save(); }
function podlove_validate_image_cache() { set_time_limit(5 * MINUTE_IN_SECONDS); PHP_Timer::start(); $cache_files = glob(trailingslashit(Image::cache_dir()) . "*" . DIRECTORY_SEPARATOR . "*" . DIRECTORY_SEPARATOR . "cache.yml"); foreach ($cache_files as $cache_file) { $cache = Yaml::parse(file_get_contents($cache_file)); $validator = new HttpHeaderValidator($cache['source'], $cache['etag'], $cache['last-modified']); $validator->validate(); if ($validator->hasChanged()) { wp_schedule_single_event(time(), 'podlove_refetch_cached_image', [$cache['source'], $cache['filename']]); } } $time = PHP_Timer::stop(); \Podlove\Log::get()->addInfo(sprintf('Finished validating %d images in %s', count($cache_files), PHP_Timer::secondsToTimeString($time))); }
public function request($url, $params = array()) { $defaults = array('user-agent' => self::user_agent(), 'stream' => false, 'decompress' => false, 'filename' => null, 'sslcertificates' => ABSPATH . WPINC . '/certificates/ca-bundle.crt'); $params = wp_parse_args($params, $defaults); if (!isset($params['_redirection']) || $params['_redirection']) { if (!self::curl_can_follow_redirects()) { $url = self::resolve_redirects($url, 5); } } $this->response = $this->curl->request($url, $params); if (is_wp_error($this->response)) { Log::get()->addError('Curl error', array('url' => $url, 'error' => $this->response->get_error_message())); } elseif (substr($this->response['response']['code'], 0, 1) >= 4) { Log::get()->addError('Curl error', array('url' => $url, 'response code' => $this->response['response']['code'])); Log::get()->addDebug(print_r($this->response, true)); } }
public function refetch_files() { $valid_files = array(); foreach (EpisodeAsset::all() as $asset) { if ($file = MediaFile::find_by_episode_id_and_episode_asset_id($this->id, $asset->id)) { $file->determine_file_size(); $file->save(); if ($file->is_valid()) { $valid_files[] = $file->id; } } } if (empty($valid_files) && get_post_status($this->post_id) == 'publish') { Log::get()->addAlert('All assets for this episode are invalid!', array('episode_id' => $this->id)); } }
public function download_source() { // for download_url() require_once ABSPATH . 'wp-admin/includes/file.php'; $result = $this->download_url($this->source_url); if (is_wp_error($result)) { \Podlove\Log::get()->addWarning(sprintf(__('Unable to download image. %s.'), $result->get_error_message()), ['url' => $this->source_url]); return; } list($temp_file, $response) = $result; if (is_wp_error($temp_file)) { \Podlove\Log::get()->addWarning(sprintf(__('Unable to download image. %s.'), $temp_file->get_error_message()), ['url' => $this->source_url]); } if (!wp_mkdir_p($this->upload_basedir)) { \Podlove\Log::get()->addWarning(sprintf(__('Unable to create directory %s. Is its parent directory writable by the server?'), $this->upload_basedir)); } $this->save_cache_data($response); $move_new_file = @rename($temp_file, $this->original_file()); if (false === $move_new_file) { \Podlove\Log::get()->addWarning(sprintf(__('The downloaded image could not be moved to %s.'), $this->original_file())); } @unlink($temp_file); $this->add_donotbackup_dotfile(); }
/** * POST $data to the given $url * * @param string $url ADN API URL * @param array $data */ public function post($url, $data) { $data_string = json_encode($data); $curl = new Http\Curl(); $curl->request($url, array('method' => 'POST', 'timeout' => '5000', 'body' => $data_string, 'headers' => array('Content-type' => 'application/json', 'Content-Length' => \Podlove\PHP\strlen($data_string)))); $response = $curl->get_response(); $body = json_decode($response['body']); if ($body->meta->code !== 200) { \Podlove\Log::get()->addWarning(sprintf('Error: App.net Module failed to Post: %s (Code %s)', str_replace("'", "''", $body->meta->error_message), $body->meta->code)); } }
/** * Main Cron function call. */ public function do_validations() { set_time_limit(1800); // set max_execution_time to half an hour Log::get()->addInfo('Begin scheduled feed validation.'); $this->renewFeedTransients(); Log::get()->addInfo('End scheduled feed validation.'); }
public static function logValidation($feedid, $errors_and_warnings, $redirected = FALSE) { $feed = \Podlove\Model\Feed::find_by_id($feedid); $feed_subscribe_url = $redirected === FALSE ? $feed->get_subscribe_url() : $feed->redirect_url; if ($redirected === TRUE) { $redirected = ' (Redirected)'; } foreach ($errors_and_warnings['warnings'] as $warning_key => $warning) { \Podlove\Log::get()->addInfo('Warning: ' . $warning['text'] . ', line ' . $warning['line'] . ' in Feed <a href="' . $feed_subscribe_url . '">' . $feed->name . $redirected . '</a>.'); } foreach ($errors_and_warnings['errors'] as $error_key => $error) { \Podlove\Log::get()->addError('Error: ' . $error['text'] . ', line ' . $error['line'] . ' in Feed <a href="' . $feed_subscribe_url . '">' . $feed->name . $redirected . '</a>.'); } }
// bring in form $core_modules = []; foreach (Modules\Base::get_core_module_names() as $module) { $core_modules[$module] = "on"; } return array_merge($new_val, $core_modules); }, 10, 2); // fire activation and deactivation hooks for modules add_action('update_option_podlove_active_modules', function ($old_val, $new_val) { $deactivated_modules = array_keys(array_diff_assoc($old_val, $new_val)); $activated_modules = array_keys(array_diff_assoc($new_val, $old_val)); if ($deactivated_modules) { foreach ($deactivated_modules as $deactivated_module) { Log::get()->addInfo('Deactivate module "' . $deactivated_module . '"'); do_action('podlove_module_was_deactivated', $deactivated_module); do_action('podlove_module_was_deactivated_' . $deactivated_module); } } if ($activated_modules) { foreach ($activated_modules as $activated_module) { Log::get()->addInfo('Activate module "' . $activated_module . '"'); // init module before firing hooks $class = Modules\Base::get_class_by_module_name($activated_module); if (class_exists($class)) { $class::instance()->load(); } do_action('podlove_module_was_activated', $activated_module); do_action('podlove_module_was_activated_' . $activated_module); } } }, 10, 2);
/** * Validate media file headers. * * @todo $this->id not available for first validation before media_file has been saved * @param array $response curl response */ private function validate_request($response) { // skip unsaved media files if (!$this->id) { return; } $header = $response['header']; if ($response['error']) { Log::get()->addError('Curl Error: ' . $response['error'], array('media_file_id' => $this->id)); } // skip validation if ETag did not change if ((int) $header["http_code"] === 304) { return; } // look for ETag and safe for later if (podlove_is_resolved_and_reachable_http_status($header["http_code"]) && preg_match('/ETag:\\s*"([^"]+)"/i', $response['response'], $matches)) { $this->etag = $matches[1]; } else { $this->etag = NULL; } do_action('podlove_media_file_content_has_changed', $this->id); // verify HTTP header if (!preg_match("/^[23]\\d\\d\$/", $header["http_code"])) { Log::get()->addError('Unexpected http response when trying to access remote media file.', array('media_file_id' => $this->id, 'http_code' => $header["http_code"])); return; } // check that content length exists and hasn't changed if (!isset($header['download_content_length']) || $header['download_content_length'] <= 0) { Log::get()->addWarning('Unable to read "Content-Length" header. Impossible to determine file size.', array('media_file_id' => $this->id, 'mime_type' => $header['content_type'], 'expected_mime_type' => $mime_type)); } elseif ($header['download_content_length'] != $this->size) { Log::get()->addInfo('Change of media file content length detected.', array('media_file_id' => $this->id, 'old_size' => $this->size, 'new_size' => $header['download_content_length'])); } // check if mime type matches asset mime type $mime_type = $this->episode_asset()->file_type()->mime_type; if ($header['content_type'] != $mime_type) { Log::get()->addWarning('Media file mime type does not match expected asset mime type.', array('media_file_id' => $this->id, 'mime_type' => $header['content_type'], 'expected_mime_type' => $mime_type)); } }