PluginProbe
Document Gallery / 2.2.4
Document Gallery v2.2.4
trunk 0.8 0.8.5 1.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1 1.2 1.2.1 1.3 1.3.1 1.4 1.4.1 1.4.2 1.4.3 2.0 2.0.1 2.0.10 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 94 releases
← All changes | document-gallery.php +330 -288 1.2.12.2.4 View file →
@@ -1,320 +1,362 @@
1 1 <?php
2 +defined('WPINC') OR exit;
3 +
2 4 /*
3 -Plugin Name: Document Gallery
4 -Description: Display non-images (and images) in gallery format on a page or post with the [dg] shortcode.
5 -Version: 1.2.1
6 -Author: Dan Rossiter
7 -Author URI: http://danrossiter.org/
8 -License: GPL2
9 -*/
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.2.4
9 + Author: Dan Rossiter
10 + Author URI: http://danrossiter.org/
11 + License: GPLv2
12 + Text Domain: document-gallery
13 + */
10 14
11 -define( 'DG_URL', plugin_dir_url( __FILE__ ) );
15 +define('DG_VERSION', '2.2.4');
12 16
13 -// CREATE GALLERY STRING //
14 -function dg_get_attachment_icons($atts) {
15 - extract( shortcode_atts( array(
16 - 'descriptions' => FALSE,
17 - 'orderby' => 'menu_order',
18 - 'order' => 'ASC',
19 - 'attachment_pg' => FALSE, // default: link directly to file (true to link to attachment pg)
20 - 'images' => FALSE, // if enabled, all images attached to current page will be included also
21 - 'ids' => FALSE // comma-separated list of attachment ids
22 - ), $atts) );
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 23
24 - // INIT
25 - $attachments = array();
26 - $errs = array();
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);
27 28
29 +// logging functionality
30 +include_once DG_PATH . 'inc/class-logger.php';
28 31
29 - // ATTRIBUTE VALIDATION
30 - if( strtolower($descriptions) == "false" ){ $descriptions = FALSE; }
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();
31 38
32 - $order = strtoupper( $order );
33 - if($order != 'ASC' && $order != 'DEC')
34 - $errs[] = "The order attribute must be either ASC or DEC. You entered $order.";
39 +// validate options if desired
40 +if ($dg_options['validation']) {
41 + add_action('init', array('DocumentGallery', 'addValidation'));
42 +}
35 43
36 - if( strtolower($attachment_pg) == "false" ){ $attachment_pg = FALSE; }
44 +// I18n
45 +add_action('plugins_loaded', array('DocumentGallery', 'loadTextDomain'));
37 46
38 - if( strtolower($images) == "false" ){ $images = FALSE; }
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'));
39 50
40 - if( strtolower($ids) == "false" ){ $ids = FALSE; }
51 +if (is_admin()) {
52 + // admin house keeping
53 + include_once DG_PATH . 'admin/class-admin.php';
41 54
42 - // http://www.youtube.com/watch?v=ClnSMCdw6E8
43 - if( $errs ) return implode(' ', $errs);
44 - // END VALIDATION (WE MADE IT!)
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 + if (empty($dg_options['css']['text'])) {
66 + add_action('wp_enqueue_scripts', array('DocumentGallery', 'enqueueGalleryStyle'));
67 + } else {
68 + add_action('template_redirect', array('DocumentGallery', 'buildCustomCss'));
69 + add_action('wp_enqueue_scripts', array('DocumentGallery', 'enqueueCustomStyle'));
70 + add_filter('query_vars', array('DocumentGallery', 'addCustomStyleQueryVar'));
71 + }
72 +}
45 73
74 +// adds 'dg' shortcode
75 +add_shortcode('dg', array('DocumentGallery', 'doShortcode'));
46 76
47 - // LET'S GET SOME DOCUMENTS!
48 - if( $ids && ( $ids = explode( ',', $ids ) ) ){
49 - $attachments = dg_get_attachments_by_ids( $ids );
50 - }
77 +/**
78 + * DocumentGallery wraps basic functionality to setup the plugin.
79 + *
80 + * @author drossiter
81 + */
82 +class DocumentGallery {
51 83
52 - // if 'ids' was used, skip
53 - if( !$attachments ){
54 - $args = array(
55 - 'numberposts' => -1,
56 - 'orderby' => $orderby,
57 - 'order' => $order,
58 - 'post_type' => 'attachment',
59 - 'post_mime_type' => 'application,video,text,audio',
60 - 'post_parent' => get_the_ID() );
61 - if( $images ) $args['post_mime_type'] .= ',image';
84 + /**
85 + * @var str Name of the query var used to check whether we should print custom CSS.
86 + */
87 + private static $query_var = 'document-gallery-css';
62 88
63 - $attachments = get_posts($args);
64 - }
65 -
66 - if ( $attachments ) { // DOCUMENT LOOP
67 - $attachment_str = array( PHP_EOL.'<!-- Generated using Document Gallery. Get yours here: '.
68 - 'http://wordpress.org/extend/plugins/document-gallery -->'.PHP_EOL );
89 + /*==========================================================================
90 + * THE SHORTCODE
91 + *=========================================================================*/
69 92
70 - $count = 0;
71 - foreach( $attachments as $attachment ) { //setup array for more than one file attachment
72 - $url = $attachment->guid;
73 - $filename = basename( $url );
93 + /**
94 + * Takes values passed from attributes and returns sutable HTML to represent
95 + * all valid attachments requested.
96 + *
97 + * @param multitype:string $atts Arguments from the user.
98 + * @return string HTML for the Document Gallery.
99 + */
100 + public static function doShortcode($atts) {
101 + include_once 'inc/class-gallery.php';
74 102
75 - if( $attachment_pg ) // link to attachment page
76 - $url = get_attachment_link( $attachment->ID );
103 + $start = microtime(true);
104 + $gallery = (string)new DG_Gallery($atts);
105 + DG_Logger::writeLog(DG_LogLevel::Detail, 'Generation Time: ' . sprintf('%.2f', (microtime(true) - $start)) . ' s');
77 106
78 - $title = get_the_title( $attachment->ID );
79 - $icon = dg_get_attachment_image( $attachment->ID, $title, $filename );
80 -
81 - if($descriptions) { // start wrapper
82 - $attachment_str[] = '<div class="document-icon-wrapper descriptions">'.PHP_EOL.
83 - ' <div class="document-icon">'.PHP_EOL;
84 - } else { // no description
85 - if( $count % 4 == 0 ) { // start wrapper
86 - $attachment_str[] = '<div id="document-icon-wrapper">'.PHP_EOL;
87 - }
88 - $attachment_str[] = ' <div class="document-icon">'.PHP_EOL;
89 - }
107 + return $gallery;
108 + }
90 109
91 - $attachment_str[] = " <a href=\"$url\">$icon<br>$title</a>".PHP_EOL;
110 + /**
111 + * Enqueue standard DG CSS.
112 + */
113 + public static function enqueueGalleryStyle() {
114 + wp_enqueue_style('document-gallery', DG_URL . 'assets/css/style.css', null, DG_VERSION);
115 + }
92 116
93 - if($descriptions) { // end icon & add description
94 - $attachment_str[] = ' </div>'.PHP_EOL.
95 - " <p>$attachment->post_content</p>".
96 - PHP_EOL.'</div>'.PHP_EOL;
97 - } else { // end icon
98 - $attachment_str[] = ' </div>'.PHP_EOL;
99 - if( ++$count % 4 == 0 ) { // end wrapper
100 - $attachment_str[] = '</div>'.PHP_EOL;
101 - }
102 - }
103 - } // end looping attachments
117 + /**
118 + * Enqueue user's custom DG CSS.
119 + */
120 + public static function enqueueCustomStyle() {
121 + global $dg_options;
122 + wp_register_style('document-gallery', add_query_arg(self::$query_var, 1, home_url('/')),
123 + null, DG_VERSION . ':' . $dg_options['css']['version']);
124 + wp_enqueue_style('document-gallery');
125 + }
104 126
105 - // for galleries w/ number of docs != mult of 4
106 - if( $count % 4 != 0 && !$descriptions ){ // end wrapper
107 - $attachment_str[] = '</div>'.PHP_EOL;
108 - }
127 + /**
128 + * Add query custom CSS query string.
129 + * Taken from here: http://ottopress.com/2010/dont-include-wp-load-please/
130 + * @param multitype:string $vars Variables to be added.
131 + * @return multitype:string All variables to be included in query string.
132 + */
133 + public static function addCustomStyleQueryVar($vars) {
134 + $vars[] = self::$query_var;
135 + return $vars;
136 + }
109 137
110 - // join array & return
111 - return implode( '', $attachment_str );
112 - } // END DOCUMENT LOOP
113 -
114 - return PHP_EOL.'<!-- Document Gallery: No attachments to display. -->'.PHP_EOL;
115 -}
116 -add_shortcode('dg', 'dg_get_attachment_icons');
117 -// Depreciated as of v1.0. left for backward compatibility
118 -add_shortcode('document gallery', 'dg_get_attachment_icons');
138 + /**
139 + * Constructs user's custom CSS dynamically, then instructs
140 + * browser to cache for a year. Cache is busted by versioning
141 + * CSS any time the user makes a change.
142 + */
143 + public static function buildCustomCss() {
144 + if (1 == intval(get_query_var(self::$query_var))) {
145 + global $dg_options;
146 +
147 + header('Content-type: text/css');
148 + header('Cache-Control: no-transform,public,maxage=' . 31536000);
149 + header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT');
150 + header('Last-Modified: ' . $dg_options['css']['last-modified']);
151 + header('ETag: ' . $dg_options['css']['etag']);
152 +
153 + echo $dg_options['css']['minified'];
154 + exit;
155 + }
156 + }
119 157
158 + /*==========================================================================
159 + * I18n
160 + *=========================================================================*/
120 161
162 + /**
163 + * Loads language files into WP core.
164 + */
165 + public static function loadTextDomain() {
166 + load_plugin_textdomain('document-gallery', false, dirname(DG_BASENAME) . '/languages/');
167 + }
121 168
122 -// HELPERS //
123 -function dg_get_attachments_by_ids( $ids ){
124 - $attachments = array();
125 - foreach( $ids as $id ){
126 - $attachment = get_post( $id );
127 - if( $attachment->post_type == 'attachment' )
128 - $attachments[] = $attachment;
129 - // else: not an attachment so skip
130 - }
131 - return $attachments;
132 -}
169 + /*==========================================================================
170 + * HELPER FUNCTIONS
171 + *=========================================================================*/
172 +
173 + /**
174 + * @param int $blog ID of the blog to be retrieved in multisite env.
175 + * @return multitype:unknown Options for the blog.
176 + */
177 + public static function getOptions($blog = null) {
178 + global $dg_options;
179 + return is_null($blog)
180 + ? $dg_options
181 + : get_blog_option($blog, DG_OPTION_NAME, null);
182 + }
183 +
184 + /**
185 + * @param multitype:unknown $options
186 + * @param int $blog ID of the blog to be set in multisite env.
187 + */
188 + public static function setOptions($options, $blog = null) {
189 + if (is_null($blog)) {
190 + global $dg_options;
191 + update_option(DG_OPTION_NAME, $options);
192 + $dg_options = $options;
193 + } else {
194 + update_blog_option($blog, DG_OPTION_NAME, $options);
195 + }
196 + }
197 +
198 + /**
199 + * @param int $blog ID of the blog to be deleted in multisite env.
200 + */
201 + public static function deleteOptions($blog = null) {
202 + if (is_null($blog)) {
203 + delete_option(DG_OPTION_NAME);
204 + } else {
205 + delete_blog_option($blog, DG_OPTION_NAME);
206 + }
207 + }
208 +
209 + /**
210 + * Adds hook to validate DG options every time save is attempted.
211 + */
212 + public static function addValidation() {
213 + add_filter('pre_update_option_' . DG_OPTION_NAME, array('DocumentGallery', 'validateOptionsStructure'), 10, 2);
214 + }
215 +
216 + /**
217 + * Checks whether the given options match the option schema.
218 + * @param multivar $new The new options to be validated.
219 + * @param multivar $old The old options.
220 + * @return array The options to be saved.
221 + */
222 + public static function validateOptionsStructure($new, $old) {
223 + if (self::isValidOptionsStructure($new)) {
224 + $ret = $new;
225 + } else {
226 + $ret = $old;
227 + DG_Logger::writeLog(DG_LogLevel::Error, 'Attempted to save invalid options.' . PHP_EOL . print_r($new, true), true, true);
228 + }
229 +
230 + return $ret;
231 + }
232 +
233 + /**
234 + * @param multivar|unknown $o The options structure to validate.
235 + * @param multivar $schema The schema to validate against (note that only keys matter -- non-array values are ignored).
236 + * @return bool Whether the given options structure matches the schema.
237 + */
238 + private static function isValidOptionsStructure($o, $schema = null) {
239 + if (is_null($schema)) {
240 + include_once DG_PATH . 'inc/class-setup.php';
241 + $schema = DG_Setup::getDefaultOptions(true);
242 + }
243 +
244 + // simple checks first
245 + $valid = is_array($o) && (count($schema) === count($o));
246 +
247 + if ($valid) {
248 + foreach ($schema as $sk => $sv) {
249 + $valid = array_key_exists($sk, $o);
250 + if (is_array($sv) && !empty($sv)) {
251 + $valid = $valid && self::isValidOptionsStructure($o[$sk], $sv);
252 + }
253 +
254 + if (!$valid) {
255 + break;
256 + }
257 + }
258 + }
259 +
260 + return $valid;
261 + }
262 +
263 + /**
264 + * Function takes a GMT timestamp and returns a date/time string in the
265 + * current timezone and WP format.
266 + * @param int $timestamp The GMT timestamp to translate.
267 + * @return string The local time in the WP date/time format.
268 + */
269 + public static function localDateTimeFromTimestamp($timestamp) {
270 + static $gmt_offet = null;
271 + static $wp_format = null;
272 + if (is_null($gmt_offet)) {
273 + $gmt_offet = get_option('gmt_offset');
274 + $wp_format = get_option('date_format').' '.get_option('time_format');
275 + }
276 +
277 + return date($wp_format, $timestamp + $gmt_offet * 3600);
278 + }
279 +
280 + /**
281 + * Compiles any custom CSS plus the default CSS together,
282 + * minifying in the process.
283 + * @param string $custom The custom CSS to compile.
284 + * @return string Compiled CSS, including both standard and any custom.
285 + */
286 + public static function compileCustomCss($custom) {
287 + $css = file_get_contents(DG_PATH . 'assets/css/style.css');
288 + $css .= str_replace('&gt;', '>', esc_html($custom));
289 +
290 + return $css;
291 + }
292 +
293 + /**
294 + * Removes all comments & space from CSS string.
295 + * Source: http://stackoverflow.com/a/15195752/866618
296 + */
297 + private static function minifyCss($css) {
298 + # remove comments first (simplifies the other regex)
299 + $re1 = <<<EOS
300 +(?sx)
301 + # quotes
302 + (
303 + "(?:[^"\\]++|\\.)*+"
304 + | '(?:[^'\\]++|\\.)*+'
305 + )
306 +|
307 + # comments
308 + /\* (?> .*? \*/ )
309 +EOS;
133 310
134 -// pass in $title & $url to avoid mult function calls
135 -function dg_get_attachment_image( $id, $title, $filename ) {
136 - $filetype = wp_check_filetype( $filename );
311 + $re2 = <<<EOS
312 +(?six)
313 + # quotes
314 + (
315 + "(?:[^"\\]++|\\.)*+"
316 + | '(?:[^'\\]++|\\.)*+'
317 + )
318 +|
319 + # ; before } (and the spaces after it while we're here)
320 + \s*+ ; \s*+ ( } ) \s*+
321 +|
322 + # all spaces around meta chars/operators
323 + \s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+
324 +|
325 + # spaces right of ( [ :
326 + ( [[(:] ) \s++
327 +|
328 + # spaces left of ) ]
329 + \s++ ( [])] )
330 +|
331 + # spaces left (and right) of :
332 + \s++ ( : ) \s*+
333 + # but not in selectors: not followed by a {
334 + (?!
335 + (?>
336 + [^{}"']++
337 + | "(?:[^"\\]++|\\.)*+"
338 + | '(?:[^'\\]++|\\.)*+'
339 + )*+
340 + {
341 + )
342 +|
343 + # spaces at beginning/end of string
344 + ^ \s++ | \s++ \z
345 +|
346 + # double spaces to single
347 + (\s)\s+
348 +EOS;
137 349
138 - // identify extension
139 - switch( $filetype['ext'] ) {
140 - // Most Common First
141 - case 'pdf':
142 - $icon = 'pdf.png';
143 - break;
144 - // MS Office
145 - case 'doc':
146 - case 'docx':
147 - case 'docm':
148 - case 'dotx':
149 - case 'dotm':
150 - $icon = 'msdoc.png';
151 - break;
152 - case 'ppt':
153 - case 'pot':
154 - case 'pps':
155 - case 'pptx':
156 - case 'pptm':
157 - case 'ppsx':
158 - case 'ppsm':
159 - case 'potx':
160 - case 'potm':
161 - case 'ppam':
162 - case 'sldx':
163 - case 'sldm':
164 - $icon = 'msppt.png';
165 - break;
166 - case 'xla':
167 - case 'xls':
168 - case 'xlt':
169 - case 'xlw':
170 - case 'xlsx':
171 - case 'xlsm':
172 - case 'xlsb':
173 - case 'xltx':
174 - case 'xltm':
175 - case 'xlam':
176 - $icon = 'msxls.png';
177 - break;
178 - case 'mdb':
179 - $icon = 'msaccess.png';
180 - break;
181 - // Video formats
182 - case 'avi':
183 - $icon = 'avi.png';
184 - break;
185 - case 'divx':
186 - $icon = 'divx.png';
187 - break;
188 - case 'flv':
189 - $icon = 'flv.png';
190 - break;
191 - case 'qt':
192 - case 'mov':
193 - $icon = 'mov.png';
194 - break;
195 - case 'asf':
196 - case 'asx':
197 - case 'wax':
198 - case 'wmv':
199 - case 'wmx':
200 - $icon = 'wmv.png';
201 - break;
202 - case 'mkv':
203 - $icon = 'mkv.png';
204 - break;
205 - // Audio formats
206 - case 'mp3':
207 - $icon = 'mp3.png';
208 - break;
209 - case 'wav':
210 - $icon = 'wav.png';
211 - break;
212 - case 'ogg':
213 - case 'oga':
214 - $icon = 'ogg.png';
215 - break;
216 - case 'midi':
217 - case 'mid':
218 - $icon = 'midi.png';
219 - break;
220 - case 'wma':
221 - $icon = 'wma.png';
222 - break;
223 - // Text formats
224 - case 'rtx':
225 - $icon = 'rtx.png';
226 - break;
227 - case 'ics':
228 - $icon = 'ics.png';
229 - break;
230 - case 'csv':
231 - $icon = 'csv.png';
232 - break;
233 - // Msc application formats
234 - case 'html':
235 - case 'htm': // death to all who use this!
236 - $icon = 'html.png';
237 - break;
238 - case 'css':
239 - $icon = 'css.png';
240 - break;
241 - case 'js':
242 - $icon = 'javascript.png';
243 - break;
244 - case 'class':
245 - $icon = 'java.png';
246 - break;
247 - case 'zip':
248 - $icon = 'zip.png';
249 - break;
250 - case 'tar':
251 - case 'gzip':
252 - case 'gz':
253 - case 'bz2': // not yet WP-supported
254 - case 'tgz': // not yet WP-supported
255 - $icon = 'compressed.png';
256 - break;
257 - case 'rar': // RAWR!!!
258 - $icon = 'rar.png';
259 - break;
260 - case '7z':
261 - $icon = '7zip.png';
262 - break;
263 - case 'exec':
264 - $icon = 'exec.png';
265 - break;
266 - case 'rtf':
267 - $icon = 'rtf.png';
268 - break;
269 - case 'swf':
270 - $icon = 'shockwave.png';
271 - break;
272 - // OpenOffice formats
273 - case 'odt':
274 - $icon = 'opendocument-text.png';
275 - break;
276 - case 'odp':
277 - $icon = 'opendocument-presentation.png';
278 - break;
279 - case 'ods':
280 - $icon = 'opendocument-spreadsheet.png';
281 - break;
282 - case 'odg':
283 - $icon = 'opendocument-graphics.png';
284 - break;
285 - case 'odb':
286 - $icon = 'opendocument-database.png';
287 - break;
288 - case 'odf':
289 - $icon = 'opendocument-formula.png';
290 - break;
291 - // fallback to default icons if not recognized
292 - default:
293 - // handle images
294 - if( preg_match( '/^image/', $filetype['type'] ) &&
295 - ( $icon = wp_get_attachment_image( $id, 'thumbnail', false ) ) )
296 - return $icon;
350 + $css = preg_replace("%$re1%", '$1', $css);
351 + return preg_replace("%$re2%", '$1$2$3$4$5$6$7', $css);
352 + }
297 353
298 - // fallback to wp defaults - get_attachment_icon is DEPRECIATED! (replaced in dg v1.1)
299 - if( $icon = wp_get_attachment_image( $id, null, true ) )
300 - return $icon;
354 + /**
355 + * Blocks instantiation. All functions are static.
356 + */
357 + private function __construct() {
301 358
302 - return "<!-- Failed to retrive icon for attachment #$id -->"; // everything failed. This is bad...
303 - }
304 -
305 - return '<img src="'.DG_URL."icons/$icon\" title=\"$title\" alt=\"$title\" />";
359 + }
306 360 }
307 361
308 -// Filtering attachment_icon was considered, then dismissed in v1.0.3 because it would mean almost
309 -// doubling the amount of processing for each icon. The native WP function would create the icon,
310 -// then 99% of the time this function would replace it. Better to just call the native WP function
311 -// at the end when needed. Filter would look like this:
312 -// add_filter( 'attachment_icon', 'dg_get_attachment_icon', 10, 2 );v
313 -
314 -// ADD SOME STYLING //
315 -function dg_add_header_css() {
316 - wp_enqueue_style( 'document-gallery-css', plugins_url('style.css', __FILE__) );
317 -}
318 -add_action( 'wp_print_styles', 'dg_add_header_css');
319 -
320 -?>
362 +?>