| 1 |
<?php |
| 2 |
defined('WPINC') OR exit; |
| 3 |
|
| 4 |
/* |
| 5 |
Plugin Name: Document Gallery |
| 6 |
Plugin URI: http://wordpress.org/extend/plugins/document-gallery/ |
| 7 |
Description: Display non-images (and images) in gallery format on a page or post with the [dg] shortcode. |
| 8 |
Version: 2.3.7 |
| 9 |
Author: Dan Rossiter |
| 10 |
Author URI: http://danrossiter.org/ |
| 11 |
License: GPLv2 |
| 12 |
Text Domain: document-gallery |
| 13 |
*/ |
| 14 |
|
| 15 |
define('DG_VERSION', '2.3.7'); |
| 16 |
|
| 17 |
// define helper paths & URLs |
| 18 |
define('DG_BASENAME', plugin_basename(__FILE__)); |
| 19 |
define('DG_URL', plugin_dir_url(__FILE__)); |
| 20 |
define('DG_PATH', plugin_dir_path(__FILE__)); |
| 21 |
define('DG_WPINC_PATH', ABSPATH . WPINC . '/'); |
| 22 |
define('DG_WPADMIN_PATH', ABSPATH . 'wp-admin/'); |
| 23 |
|
| 24 |
// init DG options for use throughout plugin |
| 25 |
global $dg_options; |
| 26 |
define('DG_OPTION_NAME', 'document_gallery'); |
| 27 |
$dg_options = get_option(DG_OPTION_NAME, null); |
| 28 |
|
| 29 |
// logging functionality |
| 30 |
include_once DG_PATH . 'inc/class-logger.php'; |
| 31 |
|
| 32 |
// handle activation, updates, and uninstallation |
| 33 |
include_once DG_PATH . 'inc/class-setup.php'; |
| 34 |
register_activation_hook(__FILE__, array('DG_Setup', 'activate')); |
| 35 |
add_action('wpmu_new_blog', array('DG_Setup','activateNewBlog')); |
| 36 |
register_uninstall_hook(__FILE__, array('DG_Setup', 'uninstall')); |
| 37 |
DG_Setup::maybeUpdate(); |
| 38 |
|
| 39 |
// validate options if desired |
| 40 |
if ($dg_options['validation']) { |
| 41 |
add_action('init', array('DocumentGallery', 'addValidation')); |
| 42 |
} |
| 43 |
|
| 44 |
// I18n |
| 45 |
add_action('plugins_loaded', array('DocumentGallery', 'loadTextDomain')); |
| 46 |
|
| 47 |
// cleanup cached data when thumbed attachment deleted |
| 48 |
include_once DG_PATH . 'inc/class-thumber.php'; |
| 49 |
add_action('delete_attachment', array('DG_Thumber', 'deleteThumbMeta')); |
| 50 |
|
| 51 |
if (is_admin()) { |
| 52 |
// admin house keeping |
| 53 |
include_once DG_PATH . 'admin/class-admin.php'; |
| 54 |
|
| 55 |
// add settings link |
| 56 |
add_filter('plugin_action_links_' . DG_BASENAME, array('DG_Admin', 'addSettingsLink')); |
| 57 |
|
| 58 |
// build options page |
| 59 |
add_action('admin_menu', array('DG_Admin', 'addAdminPage')); |
| 60 |
if (DG_Admin::doRegisterSettings()) { |
| 61 |
add_action('admin_init', array('DG_Admin', 'registerSettings')); |
| 62 |
} |
| 63 |
} else { |
| 64 |
// styling for gallery |
| 65 |
add_action('wp_enqueue_scripts', array('DocumentGallery', 'enqueueGalleryStyle')); |
| 66 |
add_action('wp_print_scripts', array('DocumentGallery', 'printCustomStyle')); |
| 67 |
} |
| 68 |
|
| 69 |
// adds 'dg' shortcode |
| 70 |
add_shortcode('dg', array('DocumentGallery', 'doShortcode')); |
| 71 |
|
| 72 |
/** |
| 73 |
* DocumentGallery wraps basic functionality to setup the plugin. |
| 74 |
* |
| 75 |
* @author drossiter |
| 76 |
*/ |
| 77 |
class DocumentGallery { |
| 78 |
|
| 79 |
/*========================================================================== |
| 80 |
* THE SHORTCODE |
| 81 |
*=========================================================================*/ |
| 82 |
|
| 83 |
/** |
| 84 |
* Takes values passed from attributes and returns sutable HTML to represent |
| 85 |
* all valid attachments requested. |
| 86 |
* |
| 87 |
* @param multitype:string $atts Arguments from the user. |
| 88 |
* @return string HTML for the Document Gallery. |
| 89 |
*/ |
| 90 |
public static function doShortcode($atts) { |
| 91 |
include_once 'inc/class-gallery.php'; |
| 92 |
|
| 93 |
$start = microtime(true); |
| 94 |
$gallery = (string)new DG_Gallery($atts); |
| 95 |
DG_Logger::writeLog(DG_LogLevel::Detail, 'Generation Time: ' . sprintf('%.2f', (microtime(true) - $start)) . ' s'); |
| 96 |
|
| 97 |
return $gallery; |
| 98 |
} |
| 99 |
|
| 100 |
/** |
| 101 |
* Enqueue standard DG CSS. |
| 102 |
*/ |
| 103 |
public static function enqueueGalleryStyle() { |
| 104 |
wp_enqueue_style('document-gallery', DG_URL . 'assets/css/style.css', null, DG_VERSION); |
| 105 |
} |
| 106 |
|
| 107 |
/** |
| 108 |
* Prints user's custom CSS. |
| 109 |
*/ |
| 110 |
public static function printCustomStyle() { |
| 111 |
global $dg_options; |
| 112 |
|
| 113 |
if (!empty($dg_options['css']['minified'])) { |
| 114 |
echo "<style type='text/css'>{$dg_options['css']['minified']}</style>" . PHP_EOL; |
| 115 |
} |
| 116 |
} |
| 117 |
|
| 118 |
/*========================================================================== |
| 119 |
* I18n |
| 120 |
*=========================================================================*/ |
| 121 |
|
| 122 |
/** |
| 123 |
* Loads language files into WP core. |
| 124 |
*/ |
| 125 |
public static function loadTextDomain() { |
| 126 |
load_plugin_textdomain('document-gallery', false, dirname(DG_BASENAME) . '/languages/'); |
| 127 |
} |
| 128 |
|
| 129 |
/*========================================================================== |
| 130 |
* HELPER FUNCTIONS |
| 131 |
*=========================================================================*/ |
| 132 |
|
| 133 |
/** |
| 134 |
* @param int $blog ID of the blog to be retrieved in multisite env. |
| 135 |
* @return multitype:unknown Options for the blog. |
| 136 |
*/ |
| 137 |
public static function getOptions($blog = null) { |
| 138 |
global $dg_options; |
| 139 |
return is_null($blog) |
| 140 |
? $dg_options |
| 141 |
: get_blog_option($blog, DG_OPTION_NAME, null); |
| 142 |
} |
| 143 |
|
| 144 |
/** |
| 145 |
* @param multitype:unknown $options |
| 146 |
* @param int $blog ID of the blog to be set in multisite env. |
| 147 |
*/ |
| 148 |
public static function setOptions($options, $blog = null) { |
| 149 |
if (is_null($blog)) { |
| 150 |
global $dg_options; |
| 151 |
update_option(DG_OPTION_NAME, $options); |
| 152 |
$dg_options = $options; |
| 153 |
} else { |
| 154 |
update_blog_option($blog, DG_OPTION_NAME, $options); |
| 155 |
} |
| 156 |
} |
| 157 |
|
| 158 |
/** |
| 159 |
* @param int $blog ID of the blog to be deleted in multisite env. |
| 160 |
*/ |
| 161 |
public static function deleteOptions($blog = null) { |
| 162 |
if (is_null($blog)) { |
| 163 |
delete_option(DG_OPTION_NAME); |
| 164 |
} else { |
| 165 |
delete_blog_option($blog, DG_OPTION_NAME); |
| 166 |
} |
| 167 |
} |
| 168 |
|
| 169 |
/** |
| 170 |
* Adds hook to validate DG options every time save is attempted. |
| 171 |
*/ |
| 172 |
public static function addValidation() { |
| 173 |
add_filter('pre_update_option_' . DG_OPTION_NAME, array('DocumentGallery', 'validateOptionsStructure'), 10, 2); |
| 174 |
} |
| 175 |
|
| 176 |
/** |
| 177 |
* Checks whether the given options match the option schema. |
| 178 |
* @param multivar $new The new options to be validated. |
| 179 |
* @param multivar $old The old options. |
| 180 |
* @return array The options to be saved. |
| 181 |
*/ |
| 182 |
public static function validateOptionsStructure($new, $old) { |
| 183 |
if (self::isValidOptionsStructure($new)) { |
| 184 |
$ret = $new; |
| 185 |
} else { |
| 186 |
$ret = $old; |
| 187 |
DG_Logger::writeLog(DG_LogLevel::Error, 'Attempted to save invalid options.' . PHP_EOL . print_r($new, true), true, true); |
| 188 |
} |
| 189 |
|
| 190 |
return $ret; |
| 191 |
} |
| 192 |
|
| 193 |
/** |
| 194 |
* @param multivar|unknown $o The options structure to validate. |
| 195 |
* @param multivar $schema The schema to validate against (note that only keys matter -- non-array values are ignored). |
| 196 |
* @return bool Whether the given options structure matches the schema. |
| 197 |
*/ |
| 198 |
private static function isValidOptionsStructure($o, $schema = null) { |
| 199 |
if (is_null($schema)) { |
| 200 |
include_once DG_PATH . 'inc/class-setup.php'; |
| 201 |
$schema = DG_Setup::getDefaultOptions(true); |
| 202 |
} |
| 203 |
|
| 204 |
// simple checks first |
| 205 |
$valid = is_array($o) && (count($schema) === count($o)); |
| 206 |
|
| 207 |
if ($valid) { |
| 208 |
foreach ($schema as $sk => $sv) { |
| 209 |
$valid = array_key_exists($sk, $o); |
| 210 |
if (is_array($sv) && !empty($sv)) { |
| 211 |
$valid = $valid && self::isValidOptionsStructure($o[$sk], $sv); |
| 212 |
} |
| 213 |
|
| 214 |
if (!$valid) { |
| 215 |
break; |
| 216 |
} |
| 217 |
} |
| 218 |
} |
| 219 |
|
| 220 |
return $valid; |
| 221 |
} |
| 222 |
|
| 223 |
/** |
| 224 |
* Function takes a GMT timestamp and returns a date/time string in the |
| 225 |
* current timezone and WP format. |
| 226 |
* @param int $timestamp The GMT timestamp to translate. |
| 227 |
* @return string The local time in the WP date/time format. |
| 228 |
*/ |
| 229 |
public static function localDateTimeFromTimestamp($timestamp) { |
| 230 |
static $gmt_offet = null; |
| 231 |
static $wp_format = null; |
| 232 |
if (is_null($gmt_offet)) { |
| 233 |
$gmt_offet = get_option('gmt_offset'); |
| 234 |
$wp_format = get_option('date_format').' '.get_option('time_format'); |
| 235 |
} |
| 236 |
|
| 237 |
return date_i18n($wp_format, $timestamp + $gmt_offet * 3600); |
| 238 |
} |
| 239 |
|
| 240 |
/** |
| 241 |
* Compiles any custom CSS, including minification and escaping HTML. |
| 242 |
* @param string $custom The custom CSS to compile. |
| 243 |
* @return string Compiled CSS. |
| 244 |
*/ |
| 245 |
public static function compileCustomCss($custom) { |
| 246 |
$css = str_replace('>', '>', esc_html($custom)); |
| 247 |
return self::minifyCss($css); |
| 248 |
} |
| 249 |
|
| 250 |
/** |
| 251 |
* Minifies CSS string. |
| 252 |
* Source: http://stackoverflow.com/a/15195752/866618 |
| 253 |
*/ |
| 254 |
private static function minifyCss($css) { |
| 255 |
# remove comments first (simplifies the other regex) |
| 256 |
$re1 = <<<EOS |
| 257 |
(?sx) |
| 258 |
# quotes |
| 259 |
( |
| 260 |
"(?:[^"\\\\]++|\\.)*+" |
| 261 |
| '(?:[^'\\\\]++|\\.)*+' |
| 262 |
) |
| 263 |
| |
| 264 |
# comments |
| 265 |
/\* (?> .*? \*/ ) |
| 266 |
EOS; |
| 267 |
|
| 268 |
$re2 = <<<EOS |
| 269 |
(?six) |
| 270 |
# quotes |
| 271 |
( |
| 272 |
"(?:[^"\\\\]++|\\.)*+" |
| 273 |
| '(?:[^'\\\\]++|\\.)*+' |
| 274 |
) |
| 275 |
| |
| 276 |
# ; before } (and the spaces after it while we're here) |
| 277 |
\s*+ ; \s*+ ( } ) \s*+ |
| 278 |
| |
| 279 |
# all spaces around meta chars/operators |
| 280 |
\s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+ |
| 281 |
| |
| 282 |
# spaces right of ( [ : |
| 283 |
( [[(:] ) \s++ |
| 284 |
| |
| 285 |
# spaces left of ) ] |
| 286 |
\s++ ( [])] ) |
| 287 |
| |
| 288 |
# spaces left (and right) of : |
| 289 |
\s++ ( : ) \s*+ |
| 290 |
# but not in selectors: not followed by a { |
| 291 |
(?! |
| 292 |
(?> |
| 293 |
[^{}"']++ |
| 294 |
| "(?:[^"\\\\]++|\\.)*+" |
| 295 |
| '(?:[^'\\\\]++|\\.)*+' |
| 296 |
)*+ |
| 297 |
{ |
| 298 |
) |
| 299 |
| |
| 300 |
# spaces at beginning/end of string |
| 301 |
^ \s++ | \s++ \z |
| 302 |
| |
| 303 |
# double spaces to single |
| 304 |
(\s)\s+ |
| 305 |
EOS; |
| 306 |
|
| 307 |
$css = preg_replace("%$re1%", '$1', $css); |
| 308 |
return preg_replace("%$re2%", '$1$2$3$4$5$6$7', $css); |
| 309 |
} |
| 310 |
|
| 311 |
/** |
| 312 |
* Blocks instantiation. All functions are static. |
| 313 |
*/ |
| 314 |
private function __construct() { |
| 315 |
|
| 316 |
} |
| 317 |
} |
| 318 |
|
| 319 |
?> |