/** * Wraps JSON encoding functionality, utilizing native functions if available. * * @param unknown $decoded Value to be encoded. * * @return string The JSON string. */ public static function jsonEncode($decoded) { if (!isset(self::$nativeJsonEncode)) { self::$nativeJsonEncode = function_exists('json_encode'); } // do encoding return self::$nativeJsonEncode ? json_encode($decoded) : self::_jsonEncode($decoded); }
/** * Uses WP_Image_Editor_Imagick to generate thumbnails. * * @param int $ID The attachment ID to retrieve thumbnail from. * @param int $pg The page to get the thumbnail of. * * @return bool|string False on failure, URL to thumb on success. */ public function getThumbnail($ID, $pg = 1) { $doc_path = get_attached_file($ID); $img = new DG_Image_Editor_Imagick($doc_path, $pg - 1); $err = $img->load(); if (is_wp_error($err)) { DG_Logger::writeLog(DG_LogLevel::Error, __('Failed to open file in Imagick: ', 'document-gallery') . $err->get_error_message()); return false; } $temp_file = DG_Util::getTempFile(); $err = $img->save($temp_file, 'image/png'); if (is_wp_error($err)) { DG_Logger::writeLog(DG_LogLevel::Error, __('Failed to save image in Imagick: ', 'document-gallery') . $err->get_error_message()); return false; } return $temp_file; }
/** * Get thumbnail for document with given ID using Ghostscript. Imagick could * also handle this, but is *much* slower. * * @param int $ID The attachment ID to retrieve thumbnail from. * @param int $pg The page number to make thumbnail of -- index starts at 1. * * @return bool|string False on failure, URL to thumb on success. */ public function getThumbnail($ID, $pg = 1) { static $gs = null; if (is_null($gs)) { $options = DG_Thumber::getOptions(); $gs = $options['gs']; if (false !== $gs) { $gs = escapeshellarg($gs) . ' -sDEVICE=png16m -dFirstPage=%1$d' . ' -dLastPage=%1$d -dBATCH -dNOPAUSE -dPDFFitPage -sOutputFile=%2$s %3$s 2>&1'; } } if (false === $gs) { return false; } $doc_path = get_attached_file($ID); $temp_path = DG_Util::getTempFile(); exec(sprintf($gs, $pg, $temp_path, $doc_path), $out, $ret); if ($ret != 0) { DG_Logger::writeLog(DG_LogLevel::Error, __('Ghostscript failed: ', 'document-gallery') . print_r($out)); @unlink($temp_path); return false; } return $temp_path; }
/** * Uses wp_read_video_metadata() and wp_read_audio_metadata() to retrieve * an embedded image to use as a thumbnail. * * @param string $ID The attachment ID to retrieve thumbnail from. * @param int $pg Unused. * * @return bool|string False on failure, URL to thumb on success. */ public function getThumbnail($ID, $pg = 1) { include_once DG_WPADMIN_PATH . 'includes/media.php'; $doc_path = get_attached_file($ID); $mime_type = get_post_mime_type($ID); if (DG_Util::startsWith($mime_type, 'video/')) { $metadata = wp_read_video_metadata($doc_path); } elseif (DG_Util::startsWith($mime_type, 'audio/')) { $metadata = wp_read_audio_metadata($doc_path); } // unsupported mime type || no embedded image present if (!isset($metadata) || empty($metadata['image']['data'])) { return false; } $ext = 'jpg'; switch ($metadata['image']['mime']) { case 'image/gif': $ext = 'gif'; break; case 'image/png': $ext = 'png'; break; } $temp_file = DG_Util::getTempFile($ext); if (!($fp = @fopen($temp_file, 'wb'))) { DG_Logger::writeLog(DG_LogLevel::Error, __('Could not open file: ', 'document-gallery') . $temp_file); return false; } if (!@fwrite($fp, $metadata['image']['data'])) { DG_Logger::writeLog(DG_LogLevel::Error, __('Could not write file: ', 'document-gallery') . $temp_file); fclose($fp); return false; } fclose($fp); return $temp_file; }
/** * Processes the POST request, generating a ThumberResponse, validating, and passing the result to $callback. * If not using client.php as the webhook, whoever receives webhook response should first invoke this method to * validate response. */ public function receiveThumbResponse() { $resp = parent::receiveThumbResponse(); if (is_null($resp)) { return; } $nonce = $resp->getNonce(); $split = explode(DG_ThumberCoThumber::NonceSeparator, $nonce); if ($resp->getSuccess() && count($split) === 2) { $ID = absint($split[0]); $tmpfile = DG_Util::getTempFile(); file_put_contents($tmpfile, $resp->getDecodedData()); DG_Thumber::setThumbnail($ID, $tmpfile, array(__CLASS__, 'getThumberThumbnail')); DG_Logger::writeLog(DG_LogLevel::Detail, "Received thumbnail from Thumber for attachment #{$split[0]}."); } else { $ID = count($split) > 0 ? $split[0] : $nonce; DG_Logger::writeLog(DG_LogLevel::Warning, "Thumber was unable to process attachment #{$ID}: " . $resp->getError()); } }
/** * @param $method ReflectionMethod The method name. * @return bool Whether the method name matches the filter pointer pattern. */ private static function isFilterPointerMethod($method) { // NOTE: ReflectionClass returns methods that are static OR public -- must reduce to an AND return $method->isPublic() && $method->isStatic() && DG_Util::endsWith($method->name, self::$feature_pointer_method_suffix); }
/** * @return bool Whether this instance represents a successful thumb generation. */ public function isSuccess() { return !empty($this->relative_path) && !DG_Util::startsWith($this->getPath(), DG_PATH); }
/** * Truncates all blog logs to the current purge interval. * * TODO: This is a memory hog. Consider switching to stream filter. */ public static function purgeExpiredEntries() { self::writeLog(DG_LogLevel::Detail, 'Beginning scheduled log file purge.'); $blogs = array(null); if (is_multisite()) { $blogs = DG_Util::getBlogIds(); } // truncate each blog's log file $time = time(); foreach ($blogs as $blog) { $blog_num = !is_null($blog) ? $blog : get_current_blog_id(); $options = self::getOptions($blog); $purge_interval = $options['purge_interval'] * DAY_IN_SECONDS; // purging is disabled for this blog if ($purge_interval <= 0) { continue; } // do perge for this blog $file = self::getLogFileName($blog_num); if (file_exists($file)) { $fp = @fopen($file, 'r'); if ($fp !== false) { $truncate = false; $offset = 0; // find the first non-expired entry while (($fields = fgetcsv($fp)) !== false) { if (!is_null($fields) && $time > $fields[0] + $purge_interval) { // we've reached the recent entries -- nothing beyond here will be removed break; } $truncate = true; $offset = @ftell($fp); if (false === $offset) { break; } } @fclose($fp); // if any expired entries exist -- remove them from the file if ($truncate) { self::writeLog(DG_LogLevel::Detail, "Purging log entries for blog #{$blog_num}."); $data = file_get_contents($file, false, null, $offset); file_put_contents($file, $data, LOCK_EX); } } } } }
/** * Save a Meta Box. */ public static function saveMetaBox($post_id) { // Check if our nonce is set. // Verify that the nonce is valid. // If this is an autosave, our form has not been submitted, so we don't want to do anything. if (!isset($_POST[DG_OPTION_NAME . '_meta_box_nonce']) || !wp_verify_nonce($_POST[DG_OPTION_NAME . '_meta_box_nonce'], DG_OPTION_NAME . '_meta_box') || defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } global $dg_options; $responseArr = array('result' => false); if (isset($_POST[DG_OPTION_NAME]['entry'])) { $ID = intval($_POST[DG_OPTION_NAME]['entry']); } else { $ID = -1; } if (isset($_POST[DG_OPTION_NAME]['upload']) && isset($_FILES['file']) && isset($dg_options['thumber']['thumbs'][$ID])) { $old_path = $dg_options['thumber']['thumbs'][$ID]['thumb_path']; $uploaded_filename = self::validateUploadedFile(); if ($uploaded_filename && DG_Thumber::setThumbnail($ID, $uploaded_filename)) { if ($dg_options['thumber']['thumbs'][$ID]['thumb_path'] !== $old_path) { @unlink($old_path); } $responseArr['result'] = true; $responseArr['url'] = $dg_options['thumber']['thumbs'][$ID]['thumb_url']; } } if (isset($_POST[DG_OPTION_NAME]['ajax'])) { echo DG_Util::jsonEncode($responseArr); wp_die(); } }
/** * Takes the provided value and returns a sanitized value. * * @param string $value The new_window value to be sanitized. * @param string &$err String to be initialized with error, if any. * * @return bool The sanitized new_window value. */ private static function sanitizeNewWindow($value, &$err) { $ret = DG_Util::toBool($value); if (is_null($ret)) { $err = sprintf(self::$binary_err, 'new_window', 'true', 'false', $value); } return $ret; }
/** * @filter dg_thumbers Allows developers to filter the Thumbers used * for specific filetypes. Index is the regex to match file extensions * supported and the value is anything that can be accepted by call_user_func(). * The function must take two parameters, 1st is the int ID of the attachment * to get a thumbnail for, 2nd is the page to take a thumbnail of * (may not be relevant for some filetypes). * * @return array */ private static function getThumbers() { static $thumbers = null; if (is_null($thumbers)) { $options = self::getOptions(); $active = $options['active']; $thumbers = array(); // Audio/Video embedded images if ($active['av']) { $exts = implode('|', self::getAudioVideoExts()); $thumbers[$exts] = array(__CLASS__, 'getAudioVideoThumbnail'); } // Ghostscript if ($active['gs'] && self::isGhostscriptAvailable()) { $exts = implode('|', self::getGhostscriptExts()); $thumbers[$exts] = array(__CLASS__, 'getGhostscriptThumbnail'); } // Imagick if ($active['imagick'] && self::isImagickAvailable()) { include_once DG_PATH . 'inc/class-image-editor-imagick.php'; if ($exts = DG_Image_Editor_Imagick::query_formats()) { $exts = implode('|', $exts); $thumbers[$exts] = array(__CLASS__, 'getImagickThumbnail'); } } // allow users to filter thumbers used $thumbers = apply_filters('dg_thumbers', $thumbers); // strip out anything that can't be called $thumbers = array_filter($thumbers, 'is_callable'); // log which thumbers are being used if (DG_Logger::logEnabled()) { if (count($thumbers) > 0) { $entry = __('Thumbnail Generators: ', 'document-gallery'); foreach ($thumbers as $k => $v) { $thumber = DG_Util::callableToString($v); // TODO: The following works for all internal regexes, but may have unpredictable // results if developer adds additional thumbnail generators using different regexes $filetypes = str_replace('|', ', ', $k); $entry .= PHP_EOL . "{$thumber}: {$filetypes}"; } } else { $entry = __('No thumbnail generators enabled.', 'document-gallery'); } DG_Logger::writeLog(DG_LogLevel::Detail, $entry); } } return $thumbers; }
/** * @return int The limit, which may or may not be a member of $limit_options. */ function dg_get_limit_param() { global $dg_options; $limit = isset($_REQUEST['limit']) ? DG_Util::posint($_REQUEST['limit']) : $dg_options['meta']['items_per_page']; if ($limit !== $dg_options['meta']['items_per_page']) { $dg_options['meta']['items_per_page'] = $limit; DocumentGallery::setOptions($dg_options); } return $limit; }
/** * Render the Thumber Advanced section. */ function dg_render_thumber_co_section() { ?> <p><?php _e('<em>Thumber.co</em> is an external service capable of generating ' . 'thumbnails for the tricky file types that Document Gallery can\'t handle on its own. Files such as ' . 'Word, PowerPoint, and PDFs can all be processed, allowing you to provide a more complete experience ' . 'to your users. Further information, including a complete list of supported file types is available ' . '<a href="https://www.thumber.co/about" target="_blank">here</a>.', 'document-gallery'); ?> </p> <p><?php _e('Once you <a href="https://www.thumber.co/subscriptions">register for a Thumber.co subscription</a>, simply enter your credentials below to get started!'); ?> </p> <?php if (!DG_Util::isPublicSite()) { ?> <p> <em><?php _e('NOTE: It appears that you are on a private server not accessible from outside your local network. ' . 'Thumber must be able to access your site in order for thumbnail conversions to work properly.', 'document-gallery'); ?> </em> </p> <?php } ?> <?php if (!in_array('sha256', hash_algos())) { ?> <p> <em><?php _e('NOTE: Your server does not support SHA-256 hashing. You will not be able to communicate with Thumber.', 'document-gallery'); ?> </em> </p> <?php } }
/** * Enqueue script for Document Gallery frontend. */ public static function enqueueGalleryScript() { DG_Util::enqueueAsset('document-gallery', 'assets/js/gallery.js', array('jquery')); }
/** * Runs when DG is uninstalled. */ public static function uninstall() { if (!current_user_can('activate_plugins')) { return; } check_admin_referer('bulk-plugins'); $blogs = array(null); if (is_multisite()) { $blogs = DG_Util::getBlogIds(); } foreach ($blogs as $blog) { self::_uninstall($blog); } wp_clear_scheduled_hook(DG_Logger::PurgeLogsAction); }
/** * Returns a list of x, where x may be any of the fields within a * term object, when provided with a list of term names (not slugs). * (http://codex.wordpress.org/Function_Reference/get_term_by#Return_Values) * * Also appends an entry onto $errs if any invalid names are found. * * @param string $x Field to retrieve from matched term. * @param string $taxon The taxon these terms are a member of. * @param string[] $term_idents Terms to retrieve, identified by either slug or name. * * @return WP_Term[] All matched terms. */ private function getTermXByNames($x, $taxon, $term_idents) { $ret = array(); $valid = true; // taxons may optionally be prefixed by 'tax_' -- // this is only useful when avoiding collisions with other attributes if (!taxonomy_exists($taxon)) { if (DG_Util::startsWith($taxon, 'tax_') && ($tmp = substr($taxon, 4)) && taxonomy_exists($tmp)) { $taxon = $tmp; } else { $this->errs[] = sprintf(DG_GallerySanitization::getUnaryErr(), 'taxon', $taxon); $valid = false; } } // only check terms if we first have a valid taxon if ($valid) { foreach ($term_idents as $ident) { $term = get_term_by('slug', $ident, $taxon); if (!$term) { $term = get_term_by('name', $ident, $taxon); } if ($term) { $ret[] = $term->{$x}; } else { $this->errs[] = sprintf(__('%s is not a valid term slug/name in %s.', 'document-gallery'), $ident, $taxon); } } } return $ret; }
/** * Adds assets/css/style.css as registered TinyMCE CSS * * @param string $stylesheets Comma-delimited list of stylesheets. * * @return string Comma-delimited list of stylesheets. */ public static function dg_plugin_mce_css($stylesheets) { if (!empty($stylesheets)) { $stylesheets .= ','; } $stylesheets .= str_replace(',', '%2C', DG_Util::getAssetPath('assets/css/style.css')); return $stylesheets; }