Files

549 lines
17 KiB
PHP

<?php
defined( 'WPINC' ) OR exit;
/**
* Holds functions that handle DG setup / uninstallation.
*
* @author drossiter
*/
class DG_Setup {
/**
* The default DG options to be used on install and when validating structure of options.
*
* @param $skeleton bool When true, expensive values are not calculated. Only keys may be trusted when returning skeleton.
*
* @return mixed[][] Contains default options for DG.
*/
public static function getDefaultOptions( $skeleton = false ) {
include_once DG_PATH . 'inc/class-thumber.php';
$gs = $donate_link = null;
if ( ! $skeleton ) {
$gs = DG_GhostscriptThumber::getGhostscriptExecutable();
$donate_link = self::getDonateLink();
}
return array(
'thumber' => array(
// Ghostscript path
'gs' => $gs,
// which thumbnail generation methods are available
'active' => DG_Thumber::getDefaultThumbers( $skeleton ),
// max width to generate thumbnails
'width' => 200,
// max height to generate thumbnails
'height' => 200
),
'thumber-co' => array(
'uid' => null,
'secret' => null,
'subscription' => array(),
'direct_upload' => false,
'mime_types' => array()
),
'gallery' => array(
// default: link directly to file (true to link to attachment pg)
'attachment_pg' => false,
// # of columns to be used in gallery
'columns' => 4,
// include the attachment description in output
'descriptions' => false,
// include thumbnail of actual document in gallery display
'fancy' => true,
// the max number of thumbnails to return
'limit' => -1,
// comma-delimited list of all mime types to be included
'mime_types' => implode( ',', self::getDefaultMimeTypes() ),
// whether to open documents in new window
'new_window' => false,
// ascending/descending order for included documents
'order' => 'ASC',
// which property to order by
'orderby' => 'menu_order',
// whether to paginate galleries with a "limit"
'paginate' => true,
// the status the post must be in when returned by DG
'post_status' => 'any',
// the type of post to be returned
'post_type' => 'attachment',
// AND or OR
'relation' => 'AND',
// how many documents to skip
'skip' => 0
),
'css' => array(
// plain text of CSS to be edited by user
'text' => ''
),
'meta' => array(
// current DG version
'version' => DG_VERSION,
// items per page at Thumbnail Management tab
'items_per_page' => 10,
// URL to donate to plugin development
'donate_link' => $donate_link
),
// logging options
'logging' => array(
// TODO: more granular -- log_level instead of blanket enable/disable
'enabled' => defined( 'WP_DEBUG' ) && WP_DEBUG,
// max age of log entry (days)
'purge_interval' => 7
)
);
}
/**
* @return string[] The default MIME types to include in gallery.
*/
public static function getDefaultMimeTypes() {
return array( 'application', 'video', 'text', 'audio', 'image' );
}
/**
* Runs every page load, updates as needed.
*/
public static function maybeUpdate() {
global $dg_options;
if ( is_null( $dg_options ) ) {
return;
}
// version has historically been in two locations -- must check both to continue supporting upgrading from those old versions
$old_version = isset( $dg_options['version'] ) ? $dg_options['version'] : $dg_options['meta']['version'];
if ( ! is_null( $dg_options ) && DG_VERSION !== $old_version ) {
DG_Logger::writeLog( DG_LogLevel::Detail, "Upgrading Document Gallery from version $old_version to " . DG_VERSION );
$blogs = array( null );
if ( is_multisite() ) {
$blogs = DG_Util::getBlogIds();
}
foreach ( $blogs as $blog ) {
self::_update( $blog );
}
}
}
/**
* Runs when update is needed, updating the given blog. If $blog is null,
* active blog is updated.
*
* @param int $blog Blog to update or null if updating current blog.
*/
private static function _update( $blog ) {
$options = DocumentGallery::getOptions( $blog );
if ( is_null( $options ) ) {
return;
}
// version-specific updates
self::twoPointTwo( $options );
self::twoPointThree( $options );
self::threePointZeroBeta( $options );
self::threePointOne( $options );
self::threePointTwo( $options );
self::threePointThree( $options );
self::threePointFour( $options );
self::threePointFive( $options );
self::fourPointZero( $options );
self::fourPointOne( $options );
self::fourPointOnePointFive( $options );
// update plugin meta data
$options['meta']['version'] = DG_VERSION;
$options['meta']['donate_link'] = self::getDonateLink();
// remove previously-failed thumbs
DG_Thumb::purgeFailedThumbs();
DocumentGallery::setOptions( $options, $blog );
}
/**
* The 'created_timestamp' key in each thumb record is being moved
* to 'timestamp' as part of a move to store timestamp for failed
* thumbnails in addition to successful ones.
*
* The defaults sub-branch in the gallery branch is being flattened into its parent.
*
* @param mixed[][] $options The options to be modified.
*/
private static function twoPointTwo( &$options ) {
if ( isset( $options['version'] ) && version_compare( $options['version'], '2.2', '<' ) ) {
$thumbs = array();
// "created_timestamp" moving to just "timestamp"
foreach ( $options['thumber']['thumbs'] as $id => $thumb ) {
if ( false === $thumb ) {
continue;
}
$thumbs[ $id ] = array(
'timestamp' => $thumb['created_timestamp'],
'thumb_url' => $thumb['thumb_url'],
'thumb_path' => $thumb['thumb_path'],
'thumber' => $thumb['thumber']
);
}
$options['thumber']['thumbs'] = $thumbs;
// adding default thumbnail generation timeout
$options['thumber']['timeout'] = 30;
// flatten out "defaults" level
$options['gallery'] = $options['gallery']['defaults'];
// adding "validation" branch
$options['validation'] = false;
// adding "logging" branch
$options['logging'] = false;
}
}
/**
* Some of the data previously stored along with custom CSS is no longer needed.
*
* @param mixed[][] $options The options to be modified.
*/
private static function twoPointThree( &$options ) {
if ( isset( $options['version'] ) && version_compare( $options['version'], '2.3', '<' ) ) {
include_once DG_PATH . 'inc/class-thumber.php';
unset( $options['css']['last-modified'] );
unset( $options['css']['etag'] );
unset( $options['css']['version'] );
// need to recalculate minified, excluding static CSS which was previously included
//$options['css']['minified'] = DocumentGallery::compileCustomCss($options['css']['text']);
// if user inadvertantly enabled google drive viewer on system where it's not supported
// then avoid locking it in the on state
if ( $options['thumber']['active']['google'] ) {
$options['thumber']['active']['google'] = false /* DG_Thumber::isGoogleDriveAvailable() */
;
}
$options['gallery']['post_status'] = 'any';
$options['gallery']['post_type'] = 'attachment';
$options['gallery']['limit'] = - 1;
}
}
/**
* Creating new meta branch in options to store plugin meta information.
*
* "Localpost" no longer supported. Replaced by "id" attribute.
* "Images" no longer supported. Replaced by "mime_types" attribute.
* "Ids" still supported, but not stored in DB.
*
* Google thumber no longer supported.
*
* Added "columns" attribute.
* Added "mime_types" attribute.
*
* @param mixed[][] $options The options to be modified.
*/
private static function threePointZeroBeta( &$options ) {
if ( isset( $options['version'] ) /*&& version_compare($options['version'], '3.0.0-beta', '<')*/ ) {
$options['meta'] = array( 'version' => $options['version'] );
unset( $options['version'] );
$images = $options['gallery']['images'];
unset( $options['gallery']['localpost'] );
unset( $options['gallery']['ids'] );
unset( $options['gallery']['images'] );
unset( $options['thumber']['active']['google'] );
$defaults = self::getDefaultOptions();
$options['gallery']['columns'] = $defaults['gallery']['columns'];
$options['gallery']['mime_types'] = $defaults['gallery']['mime_types'];
if ( $images ) {
$options['gallery']['mime_types'] .= ',image';
}
}
}
/**
* Flat logging option split out into multiple options in a nested array.
*
* Added scheduled log purge event to handle rollovers.
*
* @param mixed[][] $options The options to be modified.
*/
private static function threePointOne( &$options ) {
if ( version_compare( $options['meta']['version'], '3.1', '<' ) ) {
$logging_enabled = $options['logging'];
$options['logging'] = array(
'enabled' => $logging_enabled,
'purge_interval' => 7
);
// purge log entries regularly
wp_schedule_event( time(), 'daily', DG_Logger::PurgeLogsAction );
}
}
/**
* Adds 'new_window' under gallery options.
*
* @param mixed[][] $options The options to be modified.
*/
private static function threePointTwo( &$options ) {
if ( version_compare( $options['meta']['version'], '3.2', '<' ) ) {
$options['gallery']['new_window'] = false;
}
}
/**
* Removes minified CSS. Fixing corrupt data for boolean fields that may have gotten strings.
*
* @param mixed[][] $options The options to be modified.
*/
private static function threePointThree( &$options ) {
if ( version_compare( $options['meta']['version'], '3.3', '<' ) ) {
unset( $options['css']['minified'] );
$defaults = self::getDefaultOptions();
foreach ( $defaults as $class => $block ) {
if ( is_array($block) ) {
foreach ( $block as $prop => $value ) {
if ( is_bool( $value ) && isset( $options[$class][$prop] ) && ! is_bool( $options[$class][$prop] ) ) {
$options[$class][$prop] = DG_Util::toBool( $options[$class][$prop], $value );
}
}
} elseif ( is_bool( $block ) && isset( $options[$class] ) && ! is_bool( $options[$class] ) ) {
$options[$class] = DG_Util::toBool( $options[$class], $block );
}
}
}
}
/**
* Removes the validation option. Validation is now non-optional.
*
* @param mixed[][] $options The options to be modified.
*/
private static function threePointFour( &$options ) {
if ( version_compare( $options['meta']['version'], '3.4', '<' ) ) {
unset( $options['validation'] );
if ( ! DocumentGallery::isValidOptionsStructure( $options ) ) {
DG_Logger::writeLog(
DG_LogLevel::Error,
'Found invalid options structure. Reverting to default options.',
false,
true );
$options = self::getDefaultOptions();
}
}
}
/**
* There is no longer a concept of gallery load timeout. Missing thumbnails are asynchronously generated after
* a gallery is first rendered via AJAX requests.
*
* @param mixed[][] $options The options to be modified.
*/
private static function threePointFive( &$options ) {
if ( version_compare( $options['meta']['version'], '3.5', '<' ) ) {
unset( $options['thumber']['timeout'] );
}
}
/**
* Adds the meta items_per_page default value.
* Paginate & skip options were added.
* Moving cached thumbs into postmeta table.
*
* @param mixed[][] $options The options to be modified.
*/
private static function fourPointZero( &$options ) {
if ( version_compare( $options['meta']['version'], '4.0', '<' ) ) {
$options['gallery']['paginate'] = true;
$options['gallery']['skip'] = 0;
$options['meta']['items_per_page'] = 10;
$upload_dir = wp_upload_dir();
$upload_len = strlen( $upload_dir['basedir'] );
$dimensions = $options['thumber']['width'] . 'x' . $options['thumber']['height'];
foreach ( $options['thumber']['thumbs'] as $id => $thumb ) {
$thumb_obj = new DG_Thumb();
$thumb_obj->setPostId( $id );
$thumb_obj->setTimestamp( $thumb['timestamp'] );
$thumb_obj->setDimensions( $dimensions );
if ( isset( $thumb['thumb_path'] ) ) {
$thumb_obj->setRelativePath( substr( $thumb['thumb_path'], $upload_len + 1 ) );
$thumb_obj->setGenerator( DG_Util::callableToString( $thumb['thumber'] ) );
}
$thumb_obj->save();
}
unset( $options['thumber']['thumbs'] );
}
}
/**
* Adds integration w/ Thumber.co service.
* Update existing thumbs to match new thumbnail generation architecture.
*
* @param mixed[][] $options The options to be modified.
*/
private static function fourPointOne( &$options ) {
if ( version_compare( $options['meta']['version'], '4.1', '<' ) ) {
$options['thumber']['active']['thumber-co'] = false;
$options['thumber-co'] = array(
'uid' => null,
'secret' => null,
'subscription' => array(),
'direct_upload' => false,
'mime_types' => array()
);
$old_thumbs = DG_Thumb::getThumbs( $options['thumber']['width'] . 'x' . $options['thumber']['height'] );
DG_Thumb::purgeThumbs( null, null, false );
foreach ( $old_thumbs as $thumb ) {
if ( $thumb->isSuccess() ) {
$generator = $thumb->getGenerator();
if ( $generator == 'DG_Thumber::getGhostscriptThumbnail' ) {
$thumb->setGenerator( 'DG_GhostscriptThumber' );
} elseif ( $generator == 'DG_Thumber::getImagickThumbnail' ) {
$thumb->setGenerator( 'DG_ImagickThumber' );
} elseif ( $generator == 'DG_Thumber::getAudioVideoThumbnail' ) {
$thumb->setGenerator( 'DG_AudioVideoThumber' );
}
$thumb->save();
}
}
}
}
/**
* Cleans up the mess created in the fourPointOne upgrade script. The
* thumbnail files were removed, while the thumbnail DB entries were left.
*
* @param mixed[][] $options The options to be modified.
*/
private static function fourPointOnePointFive( &$options ) {
if ( version_compare( $options['meta']['version'], '4.1.5', '<' ) ) {
$thumbs = DG_Thumb::getThumbs( $options['thumber']['width'] . 'x' . $options['thumber']['height'] );
$ids = array();
foreach ( $thumbs as $thumb ) {
if ( $thumb->isSuccess() && !@file_exists( $thumb->getPath() ) ) {
$ids[] = $thumb->getPostId();
}
}
DG_Thumb::purgeThumbs( $ids );
}
}
/**
* Sets up Document Gallery on all blog(s) activated.
*
* @param bool $networkwide Whether this is a network-wide update (multisite only).
*/
public static function activate( $networkwide ) {
$blogs = array( null );
if ( is_multisite() ) {
// check if it is a network activation
if ( $networkwide ) {
$blogs = DG_Util::getBlogIds();
}
}
foreach ( $blogs as $blog ) {
self::_activate( $blog );
}
// handle purging log entries regularly
wp_schedule_event( time(), 'daily', DG_Logger::PurgeLogsAction );
}
/**
* Hooked into wpmu_new_blog to handle activating a new blog when plugin
* is already network activated.
* See discussion: https://core.trac.wordpress.org/ticket/14170
*
* @param int $blog Blog ID.
*/
public static function activateNewBlog( $blog ) {
if ( is_plugin_active_for_network( DG_BASENAME ) ) {
self::_activate( $blog );
}
}
/**
* Runs activation setup for Document Gallery on all blog(s) it is activated on.
*
* @param int $blog Blog to update or null if updating current blog.
*/
private static function _activate( $blog ) {
$options = DocumentGallery::getOptions( $blog );
// first activation
if ( is_null( $options ) ) {
DocumentGallery::setOptions( self::getDefaultOptions(), $blog );
}
}
/**
* Runs when DG is uninstalled.
*/
public static function uninstall() {
if ( ! current_user_can( 'activate_plugins' ) ) {
return;
}
// TODO: Unclear why this stopped working, but it is now never true,
// blocking uninstall, so has to go at least for now.
//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 );
}
/**
* Runs when DG is uninstalled for an individual blog.
*/
private static function _uninstall( $blog ) {
DG_Thumb::purgeThumbs( null, $blog );
DocumentGallery::deleteOptions( $blog );
}
/**
* NOTE: This is expensive as is involves file I/O reading the README. Only use when
* the equivalent value in options array is not viable.
* @return string URL where users can donate to plugin.
*/
private static function getDonateLink() {
$data = get_file_data( DG_PATH . 'README.txt', array( 'donate' => 'Donate link' ) );
return $data['donate'];
}
/**
* Blocks instantiation. All functions are static.
*/
private function __construct() {
}
}