Document Gallery - Version 3.0.0-beta

Version Description

Download this release

Release Info

Developer dan.rossiter
Plugin Icon 128x128 Document Gallery
Version 3.0.0-beta
Comparing to
See all releases

Code changes from version 2.3.7 to 3.0.0-beta

README.txt CHANGED
@@ -59,8 +59,7 @@ customize behavior with various attributes, seen below:
59
 
60
  `[dg [fancy=<true/false>] [attachment_pg=<true/false>]
61
  [category/custom_taxon_name=<**comma-separated list of taxon values**> [relation=<AND/OR>]]
62
- [descriptions=<true/false>] [ids=<**comma-separated list of ID #s**>]
63
- [images=<true/false>] [localpost=<true/false>] [order=<ASC/DESC>] [orderby=<**see below**>]]`
64
 
65
  Though the shortcode above may seem far from "short," none of the attributes are
66
  required and most users will find that the plugin meets your needs "out of the box"
@@ -77,7 +76,7 @@ under `Settings -> Document Gallery`.
77
 
78
  This option determines whether each document icon will link to the actual file
79
  or to its attachment page. If you want the user to be able to click on the
80
- icon and directly rective the option to download then use `attachment_pg=false`
81
  (the default). If you have information on the attachment page that you want the
82
  link to go to, use `attachment_pg=true`.
83
 
@@ -91,6 +90,12 @@ or `taxon_name=taxon_value`. Multiple values for a single taxon may be separated
91
  by commas. Note that if a taxon value contains spaces then the entire comma-
92
  delimited list must be quoted.
93
 
 
 
 
 
 
 
94
  **Descriptions Option**
95
 
96
  If `true`, each document will take its own line with the description displayed
@@ -106,17 +111,51 @@ The success in generating thumbs will depend mostly on what your server supports
106
  To fine-tune how thumbnails are generated, visit `Settings -> Document Gallery`
107
  in your site's dashboard.
108
 
109
- *NOTE: By default, the most universally-supported option for generating thumbnails,
110
- [Google Drive Viewer](https://docs.google.com/viewer) is disabled by default
111
- in order to protect your privacy, since using it requires sending your documents
112
- to Google's servers. If you're not working with confidential documents, you are
113
- encouraged to enable this for optimum performance.*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  **Limit Option** *(New in Version 2.3)*
116
 
117
  As the name suggests, this value will limit how many results are returned in the gallery.
118
  If set to *-1*, the limit is infinite.
119
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  **Order Option**
121
 
122
  This option works alongside the `orderby` option to determine whether the
@@ -143,31 +182,6 @@ documents are displayed in ascending or descending order.
143
  * `none` - No order (available with Version 2.8).
144
  * `post__in` - Preserve post ID order given in the post__in array.
145
 
146
- **Images Option** *(New in Version 1.2)*
147
-
148
- This option will tell the plugin to include all images attached to to a page or
149
- post in addition to all documents.
150
-
151
- **IDs Option** *(New in Version 1.2)*
152
-
153
- This is an advanced option intended for experienced WordPress users. If this
154
- option is used, the plugin will ignore attached documents, instead including
155
- all attachments defined by the `ids` attribute (e.g.: `ids=10,2,4,42`).
156
-
157
- *Note: If this attribute is used, the `order`, `orderby`, `images` and other
158
- attributes which generally determine which attachments to include or how to
159
- order them will be ignored. Order is defined by the order the ids are
160
- provided.*
161
-
162
- **Localpost Option** *(New in Version 1.4)*
163
-
164
- By default a document gallery only looks at attachments of the page/post where
165
- the `[dg]` shortcode is used. If you would like to search beyond that local scope,
166
- you must set `localpost=false`.
167
-
168
- This option would probably be useful especially when querying with the *category
169
- or taxonomy* option, though it can be used with any options you chose.
170
-
171
  **Relation Option** *(New in Version 1.4)*
172
 
173
  The relation option should only be used when also using the *category or custom
@@ -273,7 +287,6 @@ These tags are as follows:
273
  * **%title_attribute%**: The escaped title (above), safe for using HTML tag attributes.
274
  * **%description%**: The attachment description (only present when rendering descriptions).
275
 
276
-
277
  **Filter Thumbnail Generation Methods**
278
 
279
  Document Gallery provides the `dg_thumbers` filter, which allows developers to
@@ -349,9 +362,35 @@ function dg_get_audio_video_thumbnail($ID, $pg) {
349
  return $temp_file;
350
  }`
351
 
 
 
 
 
 
 
 
 
 
352
  == Frequently Asked Questions ==
353
 
354
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  = Q: Why is [insert thumbnail generation method] enabled on one of my WordPress installs, but not on another one? =
356
 
357
  A: Document Gallery works very hard behind the scenes to ensure that it enables
@@ -373,30 +412,6 @@ thumbnail, this is much more work than is needed. Ghostscript, on the other hand
373
  can handle reading only one page into memory, thus doing much less work before
374
  returning our thumbnail.
375
 
376
- = Q: Why isn't Google Drive Viewer enabled by default? =
377
-
378
- A: Google Drive Viewer is the most commonly-supported thumbnail generation method,
379
- alongside the Audio/Video generation, but is disabled by default. The reason
380
- for this is that in order to use this method, Document Gallery has to send your
381
- document over to Google's servers, where Google will generate the thumbnail for
382
- you. For most users, this shouldn't be a big deal, but since some users
383
- retain sensitive documents on their site, this was made opt-in to avoid
384
- compromising anyone's security. If you don't have sensitive documents, I
385
- would recommend enabling it, since it's currently the only way to generate a
386
- thumbnail for any of the Microsoft Office files, as well as some less common
387
- file types.
388
-
389
- = Q: Ghostscript is installed on my server, but it's not working! =
390
-
391
- A: Document Gallery does a pretty good job of detecting where Ghostscript is installed,
392
- but on some installs it may need a little help. To check whether this is the case,
393
- navigate to `Dashboard -> Settings -> Document Gallery` and see if there is a notice
394
- next to the Ghostscript checkbox indicating that your server is not properly configured.
395
- If that notice does exist, the next step is to go to the `Advanced` tab on that same page
396
- and see if the Ghostscript path is set. If it is not, you'll need to manually fill it
397
- with the location for your Ghostscript install (eg: `/usr/local/bin/gs`). Once that
398
- change is saved, the Ghostscript checkbox should be enabled on the first tab.
399
-
400
  == Screenshots ==
401
 
402
  1. This is an example of "fancy" thumbnails. The images are a copy of the front
@@ -416,6 +431,23 @@ To see a list of features planned for the future as well as to propose your own
416
  ideas for future Document Gallery development, take a look at our
417
  [issue tracker](https://github.com/thenadz/document-gallery/issues).
418
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  = 2.3.7 =
420
  * **Bug Fix:** There was an issue that resulted in an error being thrown in certain situations.
421
 
59
 
60
  `[dg [fancy=<true/false>] [attachment_pg=<true/false>]
61
  [category/custom_taxon_name=<**comma-separated list of taxon values**> [relation=<AND/OR>]]
62
+ [descriptions=<true/false>] [order=<ASC/DESC>] [orderby=<**see below**>]]`
 
63
 
64
  Though the shortcode above may seem far from "short," none of the attributes are
65
  required and most users will find that the plugin meets your needs "out of the box"
76
 
77
  This option determines whether each document icon will link to the actual file
78
  or to its attachment page. If you want the user to be able to click on the
79
+ icon and directly receive the option to download then use `attachment_pg=false`
80
  (the default). If you have information on the attachment page that you want the
81
  link to go to, use `attachment_pg=true`.
82
 
90
  by commas. Note that if a taxon value contains spaces then the entire comma-
91
  delimited list must be quoted.
92
 
93
+ **Columns Option** *(New in Version 3.0)*
94
+
95
+ The columns option does what it sounds like -- sets how many columns to use in
96
+ rendering your gallery. With `columns=-1`, you will get an infinite number of
97
+ columns. In other words, only 1 row with all icons.
98
+
99
  **Descriptions Option**
100
 
101
  If `true`, each document will take its own line with the description displayed
111
  To fine-tune how thumbnails are generated, visit `Settings -> Document Gallery`
112
  in your site's dashboard.
113
 
114
+ **ID Option** *(New in Version 3.0)*
115
+
116
+ This option indicates from which parent post/page to retrieve attachments.
117
+ If not explicitly set, the default will be the post/page where the shortcode
118
+ is being used.
119
+
120
+ If you do not wish to filter by parent, `id=-1` will match all attachments.
121
+
122
+ **IDs Option** *(New in Version 1.2)*
123
+
124
+ This is an advanced option intended for experienced WordPress users. If this
125
+ option is used, the plugin will ignore attached documents, instead including
126
+ all attachments defined by the `ids` attribute (e.g.: `ids=10,2,4,42`).
127
+
128
+ *Note: If this attribute is used, order defaults to the order of IDs given
129
+ rather than menu_order unless order is explicitly stated.*
130
+
131
+ **Images Option** *(New in Version 1.2)*
132
+
133
+ This option will tell the plugin to include all images attached to to a page or
134
+ post in addition to all documents.
135
+
136
+ **Include/Exclude Options** *(New in Version 3.0)*
137
+
138
+ As the name suggests, these options allow for explicitly adding or removing
139
+ matched attachments in your gallery. Like with the IDs options above, these
140
+ options take a comma-delimited list of attachment IDs.
141
 
142
  **Limit Option** *(New in Version 2.3)*
143
 
144
  As the name suggests, this value will limit how many results are returned in the gallery.
145
  If set to *-1*, the limit is infinite.
146
 
147
+ **MIME Types Option** *(New in Version 3.0)*
148
+
149
+ This is a comma-delimited list of all
150
+ [MIME types](http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types)
151
+ to be included in the gallery. Most users will not need to modify this value.
152
+
153
+ One example use-case would be to include images in your gallery (which are
154
+ not included by default). To do this, you would simply set
155
+ `mime_types=application,video,text,audio,image`, where "image" is the only difference
156
+ from the default value. You could also create a gallery which only includes PDFs
157
+ by setting `mime_types=application/pdf`.
158
+
159
  **Order Option**
160
 
161
  This option works alongside the `orderby` option to determine whether the
182
  * `none` - No order (available with Version 2.8).
183
  * `post__in` - Preserve post ID order given in the post__in array.
184
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  **Relation Option** *(New in Version 1.4)*
186
 
187
  The relation option should only be used when also using the *category or custom
287
  * **%title_attribute%**: The escaped title (above), safe for using HTML tag attributes.
288
  * **%description%**: The attachment description (only present when rendering descriptions).
289
 
 
290
  **Filter Thumbnail Generation Methods**
291
 
292
  Document Gallery provides the `dg_thumbers` filter, which allows developers to
362
  return $temp_file;
363
  }`
364
 
365
+ **Filter Inclusion of Default Document Gallery CSS**
366
+
367
+ If you wish to completely replace Document Gallery styling with your own CSS, you can prevent any any
368
+ CSS being loaded by returning false in `dg_use_default_gallery_style` filter, like so:
369
+ `add_filter('dg_use_default_gallery_style', '__return_false');`
370
+
371
+ *NOTE: By design, this will **NOT** disable inclusion of any custom CSS set at
372
+ `Dashboard -> Settings -> Document Gallery`*
373
+
374
  == Frequently Asked Questions ==
375
 
376
 
377
+ = Q: Ghostscript is installed on my server, but it's not working! =
378
+
379
+ A: Document Gallery does a pretty good job of detecting where Ghostscript is installed,
380
+ but on some installs it may need a little help. To check whether this is the case,
381
+ navigate to `Dashboard -> Settings -> Document Gallery` and see if there is a notice
382
+ next to the Ghostscript checkbox indicating that your server is not properly configured.
383
+ If that notice does exist, the next step is to go to the `Advanced` tab on that same page
384
+ and see if the Ghostscript path is set. If it is not, you'll need to manually fill it
385
+ with the location for your Ghostscript install (eg: `/usr/local/bin/gs`). Once that
386
+ change is saved, the Ghostscript checkbox should be enabled on the first tab.
387
+
388
+ = Q: Why are all of my document icons in a single column? =
389
+
390
+ A: Assuming that you do not have the `columns` attribute set to 1, the likely cause
391
+ of this behavior is that descriptions are enabled. To fix this, simply use
392
+ `[dg descriptions=false]`.
393
+
394
  = Q: Why is [insert thumbnail generation method] enabled on one of my WordPress installs, but not on another one? =
395
 
396
  A: Document Gallery works very hard behind the scenes to ensure that it enables
412
  can handle reading only one page into memory, thus doing much less work before
413
  returning our thumbnail.
414
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
415
  == Screenshots ==
416
 
417
  1. This is an example of "fancy" thumbnails. The images are a copy of the front
431
  ideas for future Document Gallery development, take a look at our
432
  [issue tracker](https://github.com/thenadz/document-gallery/issues).
433
 
434
+ = 3.0 =
435
+ * **Enhancement:** Thumbnails can now be manually overridden. To do this, either navigate to
436
+ `Dashboard -> Settings -> Document Gallery -> Thumbnail Management` and add the image
437
+ to the target attachment, or set the thumbnail in the attachment edit window.
438
+ * **Enhancement:** Users can now specify the number of columns for a gallery.
439
+ * **Enhancement:** Users can now create galleries with specific filetype(s) by using the `mime_types`
440
+ option. Thanks for suggesting this functionality,
441
+ [mepmepmep](https://wordpress.org/support/topic/dynamic-gallery-for-all-documents-of-a-certain-type)!
442
+ * **Enhancement:** Options to `include` or `exclude` specific attachments in a gallery have been added.
443
+ * **Enhancement:** The document gallery CSS has been modified to make all icon images responsive.
444
+ We've also added the `dg_use_default_gallery_style` so that developers may completely disabled
445
+ Document Gallery styles and replace it with their own.
446
+ * **Deprecation:** The deprecated `dg_doc_icon` filter has been removed. Developers should use
447
+ `dg_icon_template`.
448
+ * **Deprecation:** The `localpost` option has been deprecated and will be removed at a future date.
449
+ If you are currently using `localpost=false` then it should be replaced by `id=-1`.
450
+
451
  = 2.3.7 =
452
  * **Bug Fix:** There was an issue that resulted in an error being thrown in certain situations.
453
 
admin/class-admin.php CHANGED
@@ -6,19 +6,19 @@ class DG_Admin {
6
  * @var string The hook for the Document Gallery settings page.
7
  */
8
  private static $hook;
9
-
10
  /**
11
  * @var string The current tab being rendered.
12
  */
13
  private static $current;
14
-
15
  /**
16
  * NOTE: This should only ever be accessed through getTabs().
17
  *
18
  * @var multitype:string Associative array containing all tab names, keyed by tab slug.
19
  */
20
  private static $tabs;
21
-
22
  /**
23
  * Returns reference to tabs array, initializing if needed.
24
  *
@@ -32,26 +32,26 @@ class DG_Admin {
32
  'Logging' => __('Logging', 'document-gallery'),
33
  'Advanced' => __('Advanced', 'document-gallery'));
34
  }
35
-
36
  return self::$tabs;
37
  }
38
-
39
  /**
40
  * Renders Document Gallery options page.
41
  */
42
  public static function renderOptions() { ?>
43
  <div class="wrap">
44
- <h2><?php echo __('Document Gallery Settings', 'document-gallery'); ?></h2>
45
 
46
- <h2 class="nav-tab-wrapper">
47
  <?php foreach (self::getTabs() as $tab => $name) {
48
  $class = ($tab == self::$current) ? ' nav-tab-active' : '';
49
  echo '<a class="nav-tab '.$tab.'-tab'.$class.'" href="?page=' . DG_OPTION_NAME . '&tab='.$tab.'">'.$name.'</a>';
50
  } ?>
51
  </h2>
52
 
53
- <form method="post" action="options.php" id="tab-<?php echo self::$current?>">
54
- <input type="hidden" name="<?php echo DG_OPTION_NAME; ?>[tab]" value="<?php echo self::$current; ?>" />
55
  <?php
56
  settings_fields(DG_OPTION_NAME);
57
  do_settings_sections(DG_OPTION_NAME);
@@ -71,6 +71,22 @@ class DG_Admin {
71
  $settings = '<a href="options-general.php?page=' . DG_OPTION_NAME . '">' .
72
  __('Settings', 'document-gallery') . '</a>';
73
  array_unshift($links, $settings);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  return $links;
75
  }
76
 
@@ -84,15 +100,19 @@ class DG_Admin {
84
  'manage_options', DG_OPTION_NAME, array(__CLASS__, 'renderOptions'));
85
  add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueueScriptsAndStyles'));
86
  }
87
-
88
  /**
89
  * Enqueues styles and scripts for the admin settings page.
90
  */
91
  public static function enqueueScriptsAndStyles($hook) {
92
  if ($hook !== DG_Admin::$hook) return;
93
-
94
  wp_enqueue_style('document-gallery-admin', DG_URL . 'assets/css/admin.css', null, DG_VERSION);
95
  wp_enqueue_script('document-gallery-admin', DG_URL . 'assets/js/admin.js', array('jquery'), DG_VERSION, true);
 
 
 
 
96
  }
97
 
98
  /**
@@ -111,7 +131,7 @@ class DG_Admin {
111
  $funct = 'register' . self::$current . 'Settings';
112
  DG_Admin::$funct();
113
  }
114
-
115
  /**
116
  * Registers settings for the general tab.
117
  */
@@ -148,6 +168,19 @@ class DG_Admin {
148
  'description' => __('Link to attachment page rather than to file', 'document-gallery')
149
  ));
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  add_settings_field(
152
  'gallery_defaults_descriptions', 'descriptions',
153
  array(__CLASS__, 'renderCheckboxField'),
@@ -172,30 +205,6 @@ class DG_Admin {
172
  'description' => __('Use auto-generated document thumbnails', 'document-gallery')
173
  ));
174
 
175
- add_settings_field(
176
- 'gallery_defaults_images', 'images',
177
- array(__CLASS__, 'renderCheckboxField'),
178
- DG_OPTION_NAME, 'gallery_defaults',
179
- array (
180
- 'label_for' => 'label_gallery_defaults_images',
181
- 'name' => 'gallery_defaults][images',
182
- 'value' => esc_attr($defaults['images']),
183
- 'option_name' => DG_OPTION_NAME,
184
- 'description' => __('Include image attachments in gallery', 'document-gallery')
185
- ));
186
-
187
- add_settings_field(
188
- 'gallery_defaults_localpost', 'localpost',
189
- array(__CLASS__, 'renderCheckboxField'),
190
- DG_OPTION_NAME, 'gallery_defaults',
191
- array (
192
- 'label_for' => 'label_gallery_defaults_localpost',
193
- 'name' => 'gallery_defaults][localpost',
194
- 'value' => esc_attr($defaults['localpost']),
195
- 'option_name' => DG_OPTION_NAME,
196
- 'description' => __('Only look for attachments in post where [dg] is used', 'document-gallery')
197
- ));
198
-
199
  add_settings_field(
200
  'gallery_defaults_order', 'order',
201
  array(__CLASS__, 'renderSelectField'),
@@ -245,8 +254,22 @@ class DG_Admin {
245
  'value' => esc_attr($defaults['limit']),
246
  'type' => 'number" min="-1" step="1',
247
  'option_name' => DG_OPTION_NAME,
248
- 'description' => __('Limit the number of documents included. -1 means no limit.', 'document-gallery')));
 
249
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  add_settings_field(
251
  'gallery_defaults_post_status', 'post_status',
252
  array(__CLASS__, 'renderSelectField'),
@@ -272,7 +295,7 @@ class DG_Admin {
272
  'option_name' => DG_OPTION_NAME,
273
  'description' => __('Which post type to look for when querying documents.', 'document-gallery')
274
  ));
275
-
276
  add_settings_field(
277
  'thumbnail_generation_av', 'Audio/Video',
278
  array(__CLASS__, 'renderCheckboxField'),
@@ -315,21 +338,6 @@ class DG_Admin {
315
  'disabled' => !DG_Thumber::isImagickAvailable()
316
  ));
317
 
318
- add_settings_field(
319
- 'thumbnail_generation_google', 'Google Drive Viewer',
320
- array(__CLASS__, 'renderCheckboxField'),
321
- DG_OPTION_NAME, 'thumbnail_generation',
322
- array (
323
- 'label_for' => 'label_thumbnail_generation_google',
324
- 'name' => 'thumbnail_generation][google',
325
- 'value' => esc_attr($active['google']),
326
- 'option_name' => DG_OPTION_NAME,
327
- 'description' => DG_Thumber::isGoogleDriveAvailable()
328
- ? __('Use <a href="https://drive.google.com/viewer" target="_blank">Google Drive Viewer</a> to generate thumbnails for MS Office files and many other file types remotely.', 'document-gallery')
329
- : __('Your server does not allow remote HTTP access.', 'document-gallery'),
330
- 'disabled' => !DG_Thumber::isGoogleDriveAvailable()
331
- ));
332
-
333
  add_settings_field(
334
  'thumbnail_generation_width', 'Max Thumbnail Dimensions',
335
  array(__CLASS__, 'renderMultiTextField'),
@@ -351,7 +359,7 @@ class DG_Admin {
351
  'description' => __('The max width and height (in pixels) that thumbnails will be generated.', 'document-gallery'))
352
  ));
353
  }
354
-
355
  /**
356
  * Registers settings for the thumbnail management tab.
357
  */
@@ -360,7 +368,7 @@ class DG_Admin {
360
  'thumbnail_table', '',
361
  array(__CLASS__, 'renderThumbnailSection'), DG_OPTION_NAME);
362
  }
363
-
364
  /**
365
  * Registers settings for the logging tab.
366
  */
@@ -369,17 +377,17 @@ class DG_Admin {
369
  'logging_table', '',
370
  array(__CLASS__, 'renderLoggingSection'), DG_OPTION_NAME);
371
  }
372
-
373
  /**
374
  * Registers settings for the advanced tab.
375
  */
376
  private static function registerAdvancedSettings() {
377
  global $dg_options;
378
-
379
  add_settings_section(
380
  'advanced', __('Advanced Thumbnail Generation', 'document-gallery'),
381
  array(__CLASS__, 'renderAdvancedSection'), DG_OPTION_NAME);
382
-
383
  add_settings_field(
384
  'advanced_logging', 'Logging',
385
  array(__CLASS__, 'renderCheckboxField'),
@@ -391,7 +399,7 @@ class DG_Admin {
391
  'option_name' => DG_OPTION_NAME,
392
  'description' => __('Whether to log debug and error information related to Document Gallery.', 'document-gallery')
393
  ));
394
-
395
  add_settings_field(
396
  'advanced_validation', 'Option Validation',
397
  array(__CLASS__, 'renderCheckboxField'),
@@ -435,7 +443,7 @@ class DG_Admin {
435
  'advanced_options_dump', __('Options Array Dump', 'document-gallery'),
436
  array(__CLASS__, 'renderOptionsDumpSection'), DG_OPTION_NAME);
437
  }
438
-
439
  /**
440
  * Validates submitted options, sanitizing any invalid options.
441
  * @param array $values User-submitted new options.
@@ -466,12 +474,12 @@ class DG_Admin {
466
 
467
  // handle gallery shortcode defaults
468
  $errs = array();
469
- $ret['gallery'] = DG_Gallery::sanitizeDefaults($values['gallery_defaults'], $errs, true);
470
 
471
  foreach ($errs as $k => $v) {
472
  add_settings_error(DG_OPTION_NAME, str_replace('_', '-', $k), $v);
473
  }
474
-
475
  // handle setting width
476
  if (isset($values['thumbnail_generation']['width'])) {
477
  $width = (int)$values['thumbnail_generation']['width'];
@@ -484,7 +492,7 @@ class DG_Admin {
484
 
485
  unset($values['thumbnail_generation']['width']);
486
  }
487
-
488
  // handle setting height
489
  if (isset($values['thumbnail_generation']['height'])) {
490
  $height = (int)$values['thumbnail_generation']['height'];
@@ -497,7 +505,7 @@ class DG_Admin {
497
 
498
  unset($values['thumbnail_generation']['width']);
499
  }
500
-
501
  // delete thumb cache to force regeneration if max dimensions changed
502
  if ($ret['thumber']['width'] !== $dg_options['thumber']['width'] ||
503
  $ret['thumber']['height'] !== $dg_options['thumber']['height']) {
@@ -506,7 +514,7 @@ class DG_Admin {
506
  @unlink($v['thumb_path']);
507
  }
508
  }
509
-
510
  $ret['thumber']['thumbs'] = array();
511
  $thumbs_cleared = true;
512
  }
@@ -538,7 +546,7 @@ class DG_Admin {
538
 
539
  return $ret;
540
  }
541
-
542
  /**
543
  * Validates thumbnail management settings, sanitizing any invalid options.
544
  * @param array $values User-submitted new options.
@@ -547,27 +555,111 @@ class DG_Admin {
547
  private static function validateThumbnailSettings($values) {
548
  global $dg_options;
549
  $ret = $dg_options;
550
-
551
- if (isset($values['ids'])) {
 
 
 
 
 
 
 
 
 
552
  $deleted = array_values(array_intersect(array_keys($dg_options['thumber']['thumbs']), $values['ids']));
553
-
554
  foreach ($deleted as $k) {
555
  if (isset($ret['thumber']['thumbs'][$k]['thumber'])) {
556
  @unlink($ret['thumber']['thumbs'][$k]['thumb_path']);
557
  }
558
-
559
  unset($ret['thumber']['thumbs'][$k]);
560
  }
561
-
562
- if (isset($values['ajax'])) {
563
- echo '[' . implode(',', $deleted) . ']';
564
- add_filter('wp_redirect', array(__CLASS__, '_exit'), 1, 0);
 
 
 
 
 
 
 
 
 
 
 
 
565
  }
566
  }
567
-
 
 
 
 
 
568
  return $ret;
569
  }
570
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
  /**
572
  * Validates logging settings, sanitizing any invalid options.
573
  * @param array $values User-submitted new options.
@@ -580,7 +672,7 @@ class DG_Admin {
580
  }
581
  return $dg_options;
582
  }
583
-
584
  /**
585
  * Validates advanced settings, sanitizing any invalid options.
586
  * @param array $values User-submitted new options.
@@ -589,7 +681,7 @@ class DG_Admin {
589
  private static function validateAdvancedSettings($values) {
590
  global $dg_options;
591
  $ret = $dg_options;
592
-
593
  // handle setting the Ghostscript path
594
  if (isset($values['gs']) &&
595
  0 != strcmp($values['gs'], $ret['thumber']['gs'])) {
@@ -600,7 +692,7 @@ class DG_Admin {
600
  __('Invalid Ghostscript path given: ', 'document-gallery') . $values['gs']);
601
  }
602
  }
603
-
604
  // handle setting timeout
605
  if (isset($values['timeout'])) {
606
  $timeout = (int)$values['timeout'];
@@ -611,7 +703,7 @@ class DG_Admin {
611
  __('Invalid timeout given: ', 'document-gallery') . $values['timeout']);
612
  }
613
  }
614
-
615
  // validation checkbox
616
  $ret['validation'] = isset($values['validation']);
617
 
@@ -620,7 +712,7 @@ class DG_Admin {
620
 
621
  return $ret;
622
  }
623
-
624
  /**
625
  * @return bool Whether to register settings.
626
  */
@@ -658,13 +750,13 @@ class DG_Admin {
658
  __('Enter custom CSS styling for use with document galleries. To see which ids and classes you can style, take a look at <a href="%s" target="_blank">style.css</a>.'),
659
  DG_URL . 'assets/css/style.css'); ?></p>
660
  <table class="form-table">
661
- <tbody>
662
- <tr valign="top">
663
- <td>
664
- <textarea name="<?php echo DG_OPTION_NAME; ?>[css]" rows="10" cols="50" class="large-text code"><?php echo $dg_options['css']['text']; ?></textarea>
665
- </td>
666
- </tr>
667
- </tbody>
668
  </table>
669
  <?php }
670
 
@@ -676,11 +768,11 @@ class DG_Admin {
676
  <p><?php _e('Unless you <em>really</em> know what you\'re doing, you should not touch these values.', 'document-gallery'); ?></p>
677
  <?php if (!DG_Thumber::isExecAvailable()) : ?>
678
  <p>
679
- <em><?php _e('NOTE: <code>exec()</code> is not accessible. Ghostscript will not function.', 'document-gallery'); ?></em>
680
  </p>
681
  <?php endif; ?>
682
  <?php }
683
-
684
  /**
685
  * Renders a readonly textfield containing a dump of current DG options.
686
  */
@@ -690,13 +782,13 @@ class DG_Admin {
690
  _e('The following <em>readonly text</em> should be provided when <a href="http://wordpress.org/support/plugin/document-gallery" target="_blank">reporting a bug</a>:', 'documet-gallery');
691
  ?></p>
692
  <table class="form-table">
693
- <tbody>
694
- <tr valign="top">
695
- <td>
696
- <textarea readonly="true" rows="10" cols="50" id="options-dump" class="large-text code"><?php var_dump($dg_options); ?></textarea>
697
- </td>
698
- </tr>
699
- </tbody>
700
  </table>
701
  <?php }
702
 
@@ -709,20 +801,19 @@ class DG_Admin {
709
 
710
  $URL_params = array('page' => DG_OPTION_NAME, 'tab' => 'Thumbnail');
711
  $att_ids = array();
712
-
713
  if (isset($_REQUEST['orderby']) && in_array(strtolower($_REQUEST['orderby']), array('title', 'date'))) {
714
  $orderby = strtolower($_REQUEST['orderby']);
715
  $URL_params['orderby'] = $orderby;
716
-
717
- switch ($orderby)
718
- {
719
  case 'date':
720
  foreach ($options['thumbs'] as $key => $node) {
721
  $keyArray[$key] = $node['timestamp'];
722
  $options['thumbs'][$key]['thumb_id'] = $att_ids[] = $key;
723
  }
724
  break;
725
-
726
  case 'title':
727
  foreach ($options['thumbs'] as $key => $node) {
728
  $keyArray[$key] = basename($node['thumb_path']);
@@ -730,7 +821,7 @@ class DG_Admin {
730
  }
731
  break;
732
  }
733
-
734
  $order = strtolower($_REQUEST['order']);
735
  if (!isset($_REQUEST['order']) || !in_array($order, array('asc', 'desc'))) {
736
  $order = 'asc';
@@ -755,7 +846,7 @@ class DG_Admin {
755
  } else {
756
  $limit = intval($_REQUEST['limit']);
757
  }
758
-
759
  $URL_params['limit'] = $limit;
760
  $select_limit = '';
761
  foreach ($limit_options as $l_o) {
@@ -767,7 +858,7 @@ class DG_Admin {
767
  if ($sheet <= 0 || $sheet > $lastsheet) {
768
  $sheet = 1;
769
  }
770
-
771
  $offset = ($sheet - 1) * $limit;
772
 
773
  $att_ids = array_slice($att_ids, $offset, $limit);
@@ -785,7 +876,7 @@ class DG_Admin {
785
  $titles[$att->ID] = $att->post_title.'.'.$path_parts['extension'];
786
  }
787
  unset($atts);
788
-
789
  $thead = '<tr>'.
790
  '<th scope="col" class="manage-column column-cb check-column">'.
791
  '<label class="screen-reader-text" for="cb-select-all-%1$d">'.__('Select All', 'document-gallery').'</label>'.
@@ -793,6 +884,7 @@ class DG_Admin {
793
  '</th>'.
794
  '<th scope="col" class="manage-column column-icon">'.__('Thumbnail', 'document-gallery').'</th>'.
795
  '<th scope="col" class="manage-column column-title '.(($orderby != 'title')?'sortable desc':'sorted '.$order).'"><a href="?'.http_build_query(array_merge($URL_params, array('orderby'=>'title','order'=>(($orderby != 'title')?'asc':(($order == 'asc')?'desc':'asc'))))).'"><span>'.__('File name', 'document-gallery').'</span><span class="sorting-indicator"></span></th>'.
 
796
  '<th scope="col" class="manage-column column-date '.(($orderby != 'date')?'sortable asc':'sorted '.$order).'"><a href="?'.http_build_query(array_merge($URL_params, array('orderby'=>'date','order'=>(($orderby != 'date')?'desc':(($order == 'asc')?'desc':'asc'))))).'"><span>'.__('Date', 'document-gallery').'</span><span class="sorting-indicator"></span></th>'.
797
  '</tr>';
798
 
@@ -811,51 +903,132 @@ class DG_Admin {
811
  '<span class="displaying-num"><select dir="rtl" class="limit_per_page">'.$select_limit.'</select> '.__('items per page', 'document-gallery').'</span>'.
812
  '</div>'.
813
  '<br class="clear" />';
814
-
815
- // Avoiding json_encode to avoid compatibility issues on some systems
816
- $json_like = '';
817
- foreach ($URL_params as $k => $v) {
818
- $json_like .= '"'.$k.'":"'.$v.'",';
819
- }
820
  ?>
821
 
822
  <script type="text/javascript">
823
- var URL_params = <?php echo '{'.trim($json_like,', ').'}'; ?>;
824
- </script>
825
  <div class="thumbs-list-wrapper">
826
- <div>
827
- <div class="tablenav top"><?php echo $pagination; ?></div>
828
- <table id="ThumbsTable" class="wp-list-table widefat fixed media"
829
- cellpadding="0" cellspacing="0">
830
- <thead>
831
  <?php printf($thead, 1); ?>
832
  </thead>
833
- <tfoot>
834
  <?php printf($thead, 2); ?>
835
  </tfoot>
836
- <tbody><?php
837
  $i = 0;
838
  foreach ($options['thumbs'] as $v) {
839
  if ($i < $offset) { $i++; continue; }
840
  if (++$i > $offset + $limit) { break; }
841
-
842
- $icon = isset($v['thumb_url']) ? $v['thumb_url'] : DG_URL . 'assets/icons/missing.png';
843
  $title = isset($titles[$v['thumb_id']]) ? $titles[$v['thumb_id']] : '';
844
  $date = DocumentGallery::localDateTimeFromTimestamp($v['timestamp']);
845
 
846
- echo '<tr><td scope="row" class="check-column"><input type="checkbox" class="cb-ids" name="' . DG_OPTION_NAME . '[ids][]" value="' .
847
  $v['thumb_id'].'"></td><td class="column-icon media-icon"><img src="' .
848
  $icon.'" />'.'</td><td class="title column-title">' .
849
  ($title ? '<strong><a href="' . home_url('/?attachment_id='.$v['thumb_id']).'" target="_blank" title="'.__('View', 'document-gallery').' \'' .
850
  $title.'\' '.__('attachment page', 'document-gallery').'">'.$title.'</a></strong>' : __('Attachment not found', 'document-gallery')) .
 
 
 
 
 
 
 
 
 
851
  '</td><td class="date column-date">'.$date.'</td></tr>'.PHP_EOL;
852
  } ?>
853
  </tbody>
854
- </table>
855
- <div class="tablenav bottom"><?php echo $pagination; ?></div>
856
- </div>
857
  </div>
858
  <?php }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
859
  /**
860
  * Render the Logging table.
861
  */
@@ -932,16 +1105,16 @@ var URL_params = <?php echo '{'.trim($json_like,', ').'}'; ?>;
932
  echo '<div class="noLog">'.__('There are no log entries at this time.', 'document-gallery').'<br />'.__('For Your information:', 'document-gallery').' <strong><i>'.__('Logging', 'document-gallery').'</i></strong> '.(DG_Logger::logEnabled()?'<span class="loggingON">'.__('is turned ON', 'document-gallery').'!</span>':'<span class="loggingOFF">'.__('is turned OFF', 'document-gallery').'!</span>').'</div>';
933
  }
934
  }
935
-
936
  /**
937
  * Takes label name and returns SPAN tag.
938
  * @param string $e label name.
939
  * @return string SPAN tag
940
  */
941
  private static function getLogLabelSpan($e) {
942
- return '<span class="logLabel ' . strtolower($e) . '">' . strtoupper($e) . '</span>';
943
  }
944
-
945
  /**
946
  * Render a checkbox field.
947
  * @param array $args
@@ -953,7 +1126,7 @@ var URL_params = <?php echo '{'.trim($json_like,', ').'}'; ?>;
953
  $args['name'],
954
  $args['label_for'],
955
  checked($args['value'], 1, false),
956
- $args['disabled'] ? 'disabled="disabled"' : '',
957
  $args['description']);
958
  }
959
 
@@ -970,7 +1143,7 @@ var URL_params = <?php echo '{'.trim($json_like,', ').'}'; ?>;
970
  $args['label_for'],
971
  $args['description']);
972
  }
973
-
974
  /**
975
  * Accepts a two-dimensional array where each inner array consists of valid arguments for renderTextField.
976
  * @param array $args
@@ -1001,7 +1174,7 @@ var URL_params = <?php echo '{'.trim($json_like,', ').'}'; ?>;
1001
 
1002
  print '</select> ' . $args['description'];
1003
  }
1004
-
1005
  /**
1006
  * Wraps the PHP exit language construct.
1007
  */
6
  * @var string The hook for the Document Gallery settings page.
7
  */
8
  private static $hook;
9
+
10
  /**
11
  * @var string The current tab being rendered.
12
  */
13
  private static $current;
14
+
15
  /**
16
  * NOTE: This should only ever be accessed through getTabs().
17
  *
18
  * @var multitype:string Associative array containing all tab names, keyed by tab slug.
19
  */
20
  private static $tabs;
21
+
22
  /**
23
  * Returns reference to tabs array, initializing if needed.
24
  *
32
  'Logging' => __('Logging', 'document-gallery'),
33
  'Advanced' => __('Advanced', 'document-gallery'));
34
  }
35
+
36
  return self::$tabs;
37
  }
38
+
39
  /**
40
  * Renders Document Gallery options page.
41
  */
42
  public static function renderOptions() { ?>
43
  <div class="wrap">
44
+ <h2><?php echo __('Document Gallery Settings', 'document-gallery'); ?></h2>
45
 
46
+ <h2 class="nav-tab-wrapper">
47
  <?php foreach (self::getTabs() as $tab => $name) {
48
  $class = ($tab == self::$current) ? ' nav-tab-active' : '';
49
  echo '<a class="nav-tab '.$tab.'-tab'.$class.'" href="?page=' . DG_OPTION_NAME . '&tab='.$tab.'">'.$name.'</a>';
50
  } ?>
51
  </h2>
52
 
53
+ <form method="post" action="options.php" id="tab-<?php echo self::$current?>">
54
+ <input type="hidden" name="<?php echo DG_OPTION_NAME; ?>[tab]" value="<?php echo self::$current; ?>" />
55
  <?php
56
  settings_fields(DG_OPTION_NAME);
57
  do_settings_sections(DG_OPTION_NAME);
71
  $settings = '<a href="options-general.php?page=' . DG_OPTION_NAME . '">' .
72
  __('Settings', 'document-gallery') . '</a>';
73
  array_unshift($links, $settings);
74
+
75
+ return $links;
76
+ }
77
+
78
+ /**
79
+ * Adds donate link to main plugin view.
80
+ */
81
+ public static function addDonateLink($links, $file) {
82
+ if ($file === DG_BASENAME) {
83
+ global $dg_options;
84
+
85
+ $donate = '<strong><a href="' . $dg_options['meta']['donate_link'] . '">' .
86
+ __('Donate', 'document-gallery') . '</a></strong>';
87
+ $links[] = $donate;
88
+ }
89
+
90
  return $links;
91
  }
92
 
100
  'manage_options', DG_OPTION_NAME, array(__CLASS__, 'renderOptions'));
101
  add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueueScriptsAndStyles'));
102
  }
103
+
104
  /**
105
  * Enqueues styles and scripts for the admin settings page.
106
  */
107
  public static function enqueueScriptsAndStyles($hook) {
108
  if ($hook !== DG_Admin::$hook) return;
109
+
110
  wp_enqueue_style('document-gallery-admin', DG_URL . 'assets/css/admin.css', null, DG_VERSION);
111
  wp_enqueue_script('document-gallery-admin', DG_URL . 'assets/js/admin.js', array('jquery'), DG_VERSION, true);
112
+ wp_localize_script('document-gallery-admin', 'dg_admin_vars', array('upload_limit' => wp_max_upload_size()));
113
+ if ($hook == 'post.php') {
114
+ wp_localize_script('document-gallery-admin', 'ajax_object', array('ajax_url' => admin_url('admin-ajax.php')));
115
+ }
116
  }
117
 
118
  /**
131
  $funct = 'register' . self::$current . 'Settings';
132
  DG_Admin::$funct();
133
  }
134
+
135
  /**
136
  * Registers settings for the general tab.
137
  */
168
  'description' => __('Link to attachment page rather than to file', 'document-gallery')
169
  ));
170
 
171
+ add_settings_field(
172
+ 'gallery_defaults_columns', 'columns',
173
+ array(__CLASS__, 'renderTextField'),
174
+ DG_OPTION_NAME, 'gallery_defaults',
175
+ array (
176
+ 'label_for' => 'label_gallery_defaults_columns',
177
+ 'name' => 'gallery_defaults][columns',
178
+ 'value' => esc_attr($defaults['columns']),
179
+ 'type' => 'number" min="1" step="1',
180
+ 'option_name' => DG_OPTION_NAME,
181
+ 'description' => __('The number of columns to display when not rendering descriptions.', 'document-gallery')
182
+ ));
183
+
184
  add_settings_field(
185
  'gallery_defaults_descriptions', 'descriptions',
186
  array(__CLASS__, 'renderCheckboxField'),
205
  'description' => __('Use auto-generated document thumbnails', 'document-gallery')
206
  ));
207
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  add_settings_field(
209
  'gallery_defaults_order', 'order',
210
  array(__CLASS__, 'renderSelectField'),
254
  'value' => esc_attr($defaults['limit']),
255
  'type' => 'number" min="-1" step="1',
256
  'option_name' => DG_OPTION_NAME,
257
+ 'description' => __('Limit the number of documents included. -1 means no limit.', 'document-gallery')
258
+ ));
259
 
260
+ add_settings_field(
261
+ 'gallery_defaults_mime_types', 'mime_types',
262
+ array(__CLASS__, 'renderTextField'),
263
+ DG_OPTION_NAME, 'gallery_defaults',
264
+ array (
265
+ 'label_for' => 'label_gallery_defaults_mime_types',
266
+ 'name' => 'gallery_defaults][mime_types',
267
+ 'value' => esc_attr($defaults['mime_types']),
268
+ 'type' => 'text',
269
+ 'option_name' => DG_OPTION_NAME,
270
+ 'description' => __('Comma-delimited list of <a href="http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types">MIME types</a>.', 'document-gallery')
271
+ ));
272
+
273
  add_settings_field(
274
  'gallery_defaults_post_status', 'post_status',
275
  array(__CLASS__, 'renderSelectField'),
295
  'option_name' => DG_OPTION_NAME,
296
  'description' => __('Which post type to look for when querying documents.', 'document-gallery')
297
  ));
298
+
299
  add_settings_field(
300
  'thumbnail_generation_av', 'Audio/Video',
301
  array(__CLASS__, 'renderCheckboxField'),
338
  'disabled' => !DG_Thumber::isImagickAvailable()
339
  ));
340
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
  add_settings_field(
342
  'thumbnail_generation_width', 'Max Thumbnail Dimensions',
343
  array(__CLASS__, 'renderMultiTextField'),
359
  'description' => __('The max width and height (in pixels) that thumbnails will be generated.', 'document-gallery'))
360
  ));
361
  }
362
+
363
  /**
364
  * Registers settings for the thumbnail management tab.
365
  */
368
  'thumbnail_table', '',
369
  array(__CLASS__, 'renderThumbnailSection'), DG_OPTION_NAME);
370
  }
371
+
372
  /**
373
  * Registers settings for the logging tab.
374
  */
377
  'logging_table', '',
378
  array(__CLASS__, 'renderLoggingSection'), DG_OPTION_NAME);
379
  }
380
+
381
  /**
382
  * Registers settings for the advanced tab.
383
  */
384
  private static function registerAdvancedSettings() {
385
  global $dg_options;
386
+
387
  add_settings_section(
388
  'advanced', __('Advanced Thumbnail Generation', 'document-gallery'),
389
  array(__CLASS__, 'renderAdvancedSection'), DG_OPTION_NAME);
390
+
391
  add_settings_field(
392
  'advanced_logging', 'Logging',
393
  array(__CLASS__, 'renderCheckboxField'),
399
  'option_name' => DG_OPTION_NAME,
400
  'description' => __('Whether to log debug and error information related to Document Gallery.', 'document-gallery')
401
  ));
402
+
403
  add_settings_field(
404
  'advanced_validation', 'Option Validation',
405
  array(__CLASS__, 'renderCheckboxField'),
443
  'advanced_options_dump', __('Options Array Dump', 'document-gallery'),
444
  array(__CLASS__, 'renderOptionsDumpSection'), DG_OPTION_NAME);
445
  }
446
+
447
  /**
448
  * Validates submitted options, sanitizing any invalid options.
449
  * @param array $values User-submitted new options.
474
 
475
  // handle gallery shortcode defaults
476
  $errs = array();
477
+ $ret['gallery'] = DG_Gallery::sanitizeDefaults(null, $values['gallery_defaults'], $errs);
478
 
479
  foreach ($errs as $k => $v) {
480
  add_settings_error(DG_OPTION_NAME, str_replace('_', '-', $k), $v);
481
  }
482
+
483
  // handle setting width
484
  if (isset($values['thumbnail_generation']['width'])) {
485
  $width = (int)$values['thumbnail_generation']['width'];
492
 
493
  unset($values['thumbnail_generation']['width']);
494
  }
495
+
496
  // handle setting height
497
  if (isset($values['thumbnail_generation']['height'])) {
498
  $height = (int)$values['thumbnail_generation']['height'];
505
 
506
  unset($values['thumbnail_generation']['width']);
507
  }
508
+
509
  // delete thumb cache to force regeneration if max dimensions changed
510
  if ($ret['thumber']['width'] !== $dg_options['thumber']['width'] ||
511
  $ret['thumber']['height'] !== $dg_options['thumber']['height']) {
514
  @unlink($v['thumb_path']);
515
  }
516
  }
517
+
518
  $ret['thumber']['thumbs'] = array();
519
  $thumbs_cleared = true;
520
  }
546
 
547
  return $ret;
548
  }
549
+
550
  /**
551
  * Validates thumbnail management settings, sanitizing any invalid options.
552
  * @param array $values User-submitted new options.
555
  private static function validateThumbnailSettings($values) {
556
  global $dg_options;
557
  $ret = $dg_options;
558
+ $responseArr = array('result' => false);
559
+
560
+ if (isset($values['entry'])) {
561
+ $ID = intval($values['entry']);
562
+ } else {
563
+ $ID = -1;
564
+ }
565
+
566
+ // Thumbnail(s) cleanup;
567
+ // cleanup value is a marker
568
+ if ( isset($values['cleanup']) && isset($values['ids']) ) {
569
  $deleted = array_values(array_intersect(array_keys($dg_options['thumber']['thumbs']), $values['ids']));
570
+
571
  foreach ($deleted as $k) {
572
  if (isset($ret['thumber']['thumbs'][$k]['thumber'])) {
573
  @unlink($ret['thumber']['thumbs'][$k]['thumb_path']);
574
  }
575
+
576
  unset($ret['thumber']['thumbs'][$k]);
577
  }
578
+
579
+ $responseArr['result'] = true;
580
+ $responseArr['deleted'] = $deleted;
581
+ }
582
+ // Thumbnail file manual refresh (one at a time)
583
+ // upload value is a marker
584
+ elseif ( isset($values['upload']) && isset($_FILES['file']) && isset($ret['thumber']['thumbs'][$ID]) ) {
585
+ $old_path = $ret['thumber']['thumbs'][$ID]['thumb_path'];
586
+ $uploaded_filename = self::validateUploadedFile();
587
+ if ($uploaded_filename && DG_Thumber::setThumbnail($ID, $uploaded_filename)) {
588
+ if ($dg_options['thumber']['thumbs'][$ID]['thumb_path'] !== $old_path) {
589
+ @unlink($old_path);
590
+ }
591
+ $responseArr['result'] = true;
592
+ $responseArr['url'] = $dg_options['thumber']['thumbs'][$ID]['thumb_url'];
593
+ $ret['thumber']['thumbs'][$ID] = $dg_options['thumber']['thumbs'][$ID];
594
  }
595
  }
596
+
597
+ if (isset($values['ajax'])) {
598
+ echo DG_Util::jsonEncode($responseArr);
599
+ add_filter('wp_redirect', array(__CLASS__, '_exit'), 1, 0);
600
+ }
601
+
602
  return $ret;
603
  }
604
+
605
+ /**
606
+ * Validates uploaded file as a semi for potential thumbnail.
607
+ * @param str $var File field name.
608
+ * @return bool|str False on failure, path to temp file on success.
609
+ */
610
+ public static function validateUploadedFile($var = 'file') {
611
+ // checking if any file was delivered
612
+ if (!isset($_FILES[$var]))
613
+ return false;
614
+ // we gonna process only first one
615
+ if ( !is_array($_FILES[$var]['error']) ) {
616
+ $upload_err = $_FILES[$var]['error'];
617
+ $upload_path = $_FILES[$var]['tmp_name'];
618
+ $upload_size = $_FILES[$var]['size'];
619
+ $upload_type = $_FILES[$var]['type'];
620
+ $upload_name = $_FILES[$var]['name'];
621
+ } else {
622
+ $upload_err = $_FILES[$var]['error'][0];
623
+ $upload_path = $_FILES[$var]['tmp_name'][0];
624
+ $upload_size = $_FILES[$var]['size'][0];
625
+ $upload_type = $_FILES[$var]['type'][0];
626
+ $upload_name = $_FILES[$var]['name'][0];
627
+ }
628
+ $info = getimagesize($upload_path);
629
+ if ($info) {
630
+ if ($info['mime']!=$upload_type) {// in DG_Thumber::getExt() we'll define and set appropriate extension
631
+ DG_Logger::writeLog(
632
+ DG_LogLevel::Warning,
633
+ __('File extension doesn\'t match the MIME type of the image: ', 'document-gallery') .
634
+ $upload_name.' - '.$info['mime']);
635
+ }
636
+ if ($upload_size>wp_max_upload_size()) {
637
+ DG_Logger::writeLog(
638
+ DG_LogLevel::Warning,
639
+ __('Uploaded file size exceeds the allowable limit: ', 'document-gallery') .
640
+ $upload_name.' - '.$upload_size.'b');
641
+ return false;
642
+ }
643
+ } else {
644
+ DG_Logger::writeLog(
645
+ DG_LogLevel::Warning,
646
+ __('Uploaded file is not an image: ', 'document-gallery') .
647
+ $upload_name);
648
+ return false;
649
+ }
650
+ if ($upload_err == UPLOAD_ERR_OK && $upload_size > 0) {
651
+ $temp_file = $upload_path;
652
+ } else {
653
+ DG_Logger::writeLog(
654
+ DG_LogLevel::Error,
655
+ __('Failed to get uploaded file: ', 'document-gallery') .
656
+ $upload_err);
657
+ return false;
658
+ }
659
+
660
+ return $temp_file;
661
+ }
662
+
663
  /**
664
  * Validates logging settings, sanitizing any invalid options.
665
  * @param array $values User-submitted new options.
672
  }
673
  return $dg_options;
674
  }
675
+
676
  /**
677
  * Validates advanced settings, sanitizing any invalid options.
678
  * @param array $values User-submitted new options.
681
  private static function validateAdvancedSettings($values) {
682
  global $dg_options;
683
  $ret = $dg_options;
684
+
685
  // handle setting the Ghostscript path
686
  if (isset($values['gs']) &&
687
  0 != strcmp($values['gs'], $ret['thumber']['gs'])) {
692
  __('Invalid Ghostscript path given: ', 'document-gallery') . $values['gs']);
693
  }
694
  }
695
+
696
  // handle setting timeout
697
  if (isset($values['timeout'])) {
698
  $timeout = (int)$values['timeout'];
703
  __('Invalid timeout given: ', 'document-gallery') . $values['timeout']);
704
  }
705
  }
706
+
707
  // validation checkbox
708
  $ret['validation'] = isset($values['validation']);
709
 
712
 
713
  return $ret;
714
  }
715
+
716
  /**
717
  * @return bool Whether to register settings.
718
  */
750
  __('Enter custom CSS styling for use with document galleries. To see which ids and classes you can style, take a look at <a href="%s" target="_blank">style.css</a>.'),
751
  DG_URL . 'assets/css/style.css'); ?></p>
752
  <table class="form-table">
753
+ <tbody>
754
+ <tr valign="top">
755
+ <td>
756
+ <textarea name="<?php echo DG_OPTION_NAME; ?>[css]" rows="10" cols="50" class="large-text code"><?php echo $dg_options['css']['text']; ?></textarea>
757
+ </td>
758
+ </tr>
759
+ </tbody>
760
  </table>
761
  <?php }
762
 
768
  <p><?php _e('Unless you <em>really</em> know what you\'re doing, you should not touch these values.', 'document-gallery'); ?></p>
769
  <?php if (!DG_Thumber::isExecAvailable()) : ?>
770
  <p>
771
+ <em><?php _e('NOTE: <code>exec()</code> is not accessible. Ghostscript will not function.', 'document-gallery'); ?></em>
772
  </p>
773
  <?php endif; ?>
774
  <?php }
775
+
776
  /**
777
  * Renders a readonly textfield containing a dump of current DG options.
778
  */
782
  _e('The following <em>readonly text</em> should be provided when <a href="http://wordpress.org/support/plugin/document-gallery" target="_blank">reporting a bug</a>:', 'documet-gallery');
783
  ?></p>
784
  <table class="form-table">
785
+ <tbody>
786
+ <tr valign="top">
787
+ <td>
788
+ <textarea readonly="true" rows="10" cols="50" id="options-dump" class="large-text code"><?php print_r($dg_options); ?></textarea>
789
+ </td>
790
+ </tr>
791
+ </tbody>
792
  </table>
793
  <?php }
794
 
801
 
802
  $URL_params = array('page' => DG_OPTION_NAME, 'tab' => 'Thumbnail');
803
  $att_ids = array();
804
+
805
  if (isset($_REQUEST['orderby']) && in_array(strtolower($_REQUEST['orderby']), array('title', 'date'))) {
806
  $orderby = strtolower($_REQUEST['orderby']);
807
  $URL_params['orderby'] = $orderby;
808
+
809
+ switch ($orderby) {
 
810
  case 'date':
811
  foreach ($options['thumbs'] as $key => $node) {
812
  $keyArray[$key] = $node['timestamp'];
813
  $options['thumbs'][$key]['thumb_id'] = $att_ids[] = $key;
814
  }
815
  break;
816
+
817
  case 'title':
818
  foreach ($options['thumbs'] as $key => $node) {
819
  $keyArray[$key] = basename($node['thumb_path']);
821
  }
822
  break;
823
  }
824
+
825
  $order = strtolower($_REQUEST['order']);
826
  if (!isset($_REQUEST['order']) || !in_array($order, array('asc', 'desc'))) {
827
  $order = 'asc';
846
  } else {
847
  $limit = intval($_REQUEST['limit']);
848
  }
849
+
850
  $URL_params['limit'] = $limit;
851
  $select_limit = '';
852
  foreach ($limit_options as $l_o) {
858
  if ($sheet <= 0 || $sheet > $lastsheet) {
859
  $sheet = 1;
860
  }
861
+
862
  $offset = ($sheet - 1) * $limit;
863
 
864
  $att_ids = array_slice($att_ids, $offset, $limit);
876
  $titles[$att->ID] = $att->post_title.'.'.$path_parts['extension'];
877
  }
878
  unset($atts);
879
+
880
  $thead = '<tr>'.
881
  '<th scope="col" class="manage-column column-cb check-column">'.
882
  '<label class="screen-reader-text" for="cb-select-all-%1$d">'.__('Select All', 'document-gallery').'</label>'.
884
  '</th>'.
885
  '<th scope="col" class="manage-column column-icon">'.__('Thumbnail', 'document-gallery').'</th>'.
886
  '<th scope="col" class="manage-column column-title '.(($orderby != 'title')?'sortable desc':'sorted '.$order).'"><a href="?'.http_build_query(array_merge($URL_params, array('orderby'=>'title','order'=>(($orderby != 'title')?'asc':(($order == 'asc')?'desc':'asc'))))).'"><span>'.__('File name', 'document-gallery').'</span><span class="sorting-indicator"></span></th>'.
887
+ '<th scope="col" class="manage-column column-thumbupload"></th>'.
888
  '<th scope="col" class="manage-column column-date '.(($orderby != 'date')?'sortable asc':'sorted '.$order).'"><a href="?'.http_build_query(array_merge($URL_params, array('orderby'=>'date','order'=>(($orderby != 'date')?'desc':(($order == 'asc')?'desc':'asc'))))).'"><span>'.__('Date', 'document-gallery').'</span><span class="sorting-indicator"></span></th>'.
889
  '</tr>';
890
 
903
  '<span class="displaying-num"><select dir="rtl" class="limit_per_page">'.$select_limit.'</select> '.__('items per page', 'document-gallery').'</span>'.
904
  '</div>'.
905
  '<br class="clear" />';
 
 
 
 
 
 
906
  ?>
907
 
908
  <script type="text/javascript">
909
+ var URL_params = <?php echo DG_Util::jsonEncode($URL_params); ?>;
910
+ </script>
911
  <div class="thumbs-list-wrapper">
912
+ <div>
913
+ <div class="tablenav top"><?php echo $pagination; ?></div>
914
+ <table id="ThumbsTable" class="wp-list-table widefat fixed media"
915
+ cellpadding="0" cellspacing="0">
916
+ <thead>
917
  <?php printf($thead, 1); ?>
918
  </thead>
919
+ <tfoot>
920
  <?php printf($thead, 2); ?>
921
  </tfoot>
922
+ <tbody><?php
923
  $i = 0;
924
  foreach ($options['thumbs'] as $v) {
925
  if ($i < $offset) { $i++; continue; }
926
  if (++$i > $offset + $limit) { break; }
927
+
928
+ $icon = isset($v['thumb_url']) ? $v['thumb_url'] : DG_Thumber::getDefaultThumbnail($v['thumb_id']);
929
  $title = isset($titles[$v['thumb_id']]) ? $titles[$v['thumb_id']] : '';
930
  $date = DocumentGallery::localDateTimeFromTimestamp($v['timestamp']);
931
 
932
+ echo '<tr data-entry="'.$v['thumb_id'].'"><td scope="row" class="check-column"><input type="checkbox" class="cb-ids" name="' . DG_OPTION_NAME . '[ids][]" value="' .
933
  $v['thumb_id'].'"></td><td class="column-icon media-icon"><img src="' .
934
  $icon.'" />'.'</td><td class="title column-title">' .
935
  ($title ? '<strong><a href="' . home_url('/?attachment_id='.$v['thumb_id']).'" target="_blank" title="'.__('View', 'document-gallery').' \'' .
936
  $title.'\' '.__('attachment page', 'document-gallery').'">'.$title.'</a></strong>' : __('Attachment not found', 'document-gallery')) .
937
+ '</td><td class="column-thumbupload">' .
938
+ '<span class="manual-download">' .
939
+ '<span class="dashicons dashicons-upload"></span>' .
940
+ '<span class="html5dndmarker">Drop file here<span> or </span></span>' .
941
+ '<span class="buttons-area">' .
942
+ '<input id="upload-button'.$v['thumb_id'].'" type="file" />' .
943
+ '<input id="trigger-button'.$v['thumb_id'].'" type="button" value="Select File" class="button" />' .
944
+ '</span>' .
945
+ '</span>' .
946
  '</td><td class="date column-date">'.$date.'</td></tr>'.PHP_EOL;
947
  } ?>
948
  </tbody>
949
+ </table>
950
+ <div class="tablenav bottom"><?php echo $pagination; ?></div>
951
+ </div>
952
  </div>
953
  <?php }
954
+
955
+ /**
956
+ * Adds meta box to the attchements' edit pages.
957
+ */
958
+ public static function addMetaBox() {
959
+ $screens = array( 'attachment' );
960
+ foreach ( $screens as $screen ) {
961
+ add_meta_box(
962
+ DG_OPTION_NAME.'_gen_box',
963
+ __( '<b>Thumbnail</b> for <i><b>Document Gallery</b></i>', 'document-gallery' ),
964
+ array(__CLASS__, 'renderMetaBox'),
965
+ $screen,
966
+ 'normal'
967
+ );
968
+ }
969
+ DG_Admin::$hook = 'post.php';
970
+ add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueueScriptsAndStyles'));
971
+ }
972
+
973
+ /**
974
+ * Render a Meta Box.
975
+ */
976
+ public static function renderMetaBox($post) {
977
+ global $dg_options;
978
+ wp_nonce_field( DG_OPTION_NAME.'_meta_box', DG_OPTION_NAME.'_meta_box_nonce' );
979
+ $ID = $post->ID;
980
+ $icon = isset($dg_options['thumber']['thumbs'][$ID]['thumb_url']) ? $dg_options['thumber']['thumbs'][$ID]['thumb_url'] : DG_Thumber::getDefaultThumbnail($ID);
981
+
982
+ echo '<table id="ThumbsTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">'.
983
+ '<tbody><tr data-entry="'.$ID.'"><td class="column-icon media-icon"><img src="' .
984
+ $icon.'" />'.'</td><td class="column-thumbupload">' .
985
+ '<span class="manual-download">' .
986
+ '<span class="dashicons dashicons-upload"></span>' .
987
+ '<span class="html5dndmarker">Drop file here<span> or </span></span>' .
988
+ '<span class="buttons-area">' .
989
+ '<input id="upload-button'.$ID.'" type="file" />' .
990
+ '<input id="trigger-button'.$ID.'" type="button" value="Select File" class="button" />' .
991
+ '</span>' .
992
+ '</span>' .
993
+ '</td></tr></tbody></table>'.
994
+ (empty($dg_options['thumber']['thumbs'][$ID]) ? '<span class="dashicons dashicons-info"></span><span class="">Please note this attachment hasn&#39;t been used in any Document Gallery instance and so there is no autogenerated thumbnail, in the meantime default one is used instead.</span>' : '').PHP_EOL;
995
+ }
996
+
997
+ /**
998
+ * Save a Meta Box.
999
+ */
1000
+ public static function saveMetaBox($post_id) {
1001
+ // Check if our nonce is set.
1002
+ // Verify that the nonce is valid.
1003
+ // If this is an autosave, our form has not been submitted, so we don't want to do anything.
1004
+ 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) ) {
1005
+ return;
1006
+ }
1007
+
1008
+ global $dg_options;
1009
+ $responseArr = array('result' => false);
1010
+ if (isset($_POST[DG_OPTION_NAME]['entry'])) {
1011
+ $ID = intval($_POST[DG_OPTION_NAME]['entry']);
1012
+ } else {
1013
+ $ID = -1;
1014
+ }
1015
+ if ( isset($_POST[DG_OPTION_NAME]['upload']) && isset($_FILES['file']) && isset($dg_options['thumber']['thumbs'][$ID]) ) {
1016
+ $old_path = $dg_options['thumber']['thumbs'][$ID]['thumb_path'];
1017
+ $uploaded_filename = self::validateUploadedFile();
1018
+ if ($uploaded_filename && DG_Thumber::setThumbnail($ID, $uploaded_filename)) {
1019
+ if ($dg_options['thumber']['thumbs'][$ID]['thumb_path'] !== $old_path) {
1020
+ @unlink($old_path);
1021
+ }
1022
+ $responseArr['result'] = true;
1023
+ $responseArr['url'] = $dg_options['thumber']['thumbs'][$ID]['thumb_url'];
1024
+ }
1025
+ }
1026
+ if (isset($_POST[DG_OPTION_NAME]['ajax'])) {
1027
+ echo DG_Util::jsonEncode($responseArr);
1028
+ wp_die();
1029
+ }
1030
+ }
1031
+
1032
  /**
1033
  * Render the Logging table.
1034
  */
1105
  echo '<div class="noLog">'.__('There are no log entries at this time.', 'document-gallery').'<br />'.__('For Your information:', 'document-gallery').' <strong><i>'.__('Logging', 'document-gallery').'</i></strong> '.(DG_Logger::logEnabled()?'<span class="loggingON">'.__('is turned ON', 'document-gallery').'!</span>':'<span class="loggingOFF">'.__('is turned OFF', 'document-gallery').'!</span>').'</div>';
1106
  }
1107
  }
1108
+
1109
  /**
1110
  * Takes label name and returns SPAN tag.
1111
  * @param string $e label name.
1112
  * @return string SPAN tag
1113
  */
1114
  private static function getLogLabelSpan($e) {
1115
+ return '<span class="logLabel ' . strtolower($e) . '">' . strtoupper($e) . '</span>';
1116
  }
1117
+
1118
  /**
1119
  * Render a checkbox field.
1120
  * @param array $args
1126
  $args['name'],
1127
  $args['label_for'],
1128
  checked($args['value'], 1, false),
1129
+ disabled($args['disabled'], true, false),
1130
  $args['description']);
1131
  }
1132
 
1143
  $args['label_for'],
1144
  $args['description']);
1145
  }
1146
+
1147
  /**
1148
  * Accepts a two-dimensional array where each inner array consists of valid arguments for renderTextField.
1149
  * @param array $args
1174
 
1175
  print '</select> ' . $args['description'];
1176
  }
1177
+
1178
  /**
1179
  * Wraps the PHP exit language construct.
1180
  */
assets/css/admin.css CHANGED
@@ -16,7 +16,7 @@ div.thumbs-list-wrapper>div, div.log-list-wrapper>div{
16
  -moz-border-radius: 10px;
17
  background: #B9C9FE;
18
  color: #039;
19
- width: auto;
20
  margin: 10px auto;
21
  }
22
  #ThumbsTable tbody, #LogTable tbody {
@@ -40,7 +40,7 @@ div.thumbs-list-wrapper>div, div.log-list-wrapper>div{
40
  #LogTable td {
41
  text-align: left;
42
  }
43
- td.title.column-title{
44
  text-align: left !important;
45
  }
46
  #ThumbsTable img{
@@ -58,11 +58,20 @@ tr.selected{
58
  tr.selected:hover{
59
  background: #D8D3E5 !important;
60
  }
61
- .column-date, .column-level, .check-column, .column-icon {
62
  white-space: nowrap;
 
63
  }
64
  .column-entry, .column-title {
65
- width: 100%;
 
 
 
 
 
 
 
 
66
  }
67
  .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before, .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after{
68
  display: inline-block;
@@ -148,7 +157,7 @@ tr.selected:hover{
148
  }
149
  .logLabel.date {
150
  background: #999999;
151
- white-space: nowrap;
152
  font-weight: inherit;
153
  }
154
  .logLabel.date:before{
@@ -282,7 +291,60 @@ th input{
282
  td input{
283
  margin-right: 0 !important;
284
  }
285
-
286
  textarea[readonly], input[readonly], select[readonly] {
287
  background-color: #dcdcdc;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  }
16
  -moz-border-radius: 10px;
17
  background: #B9C9FE;
18
  color: #039;
19
+ width: 100%;
20
  margin: 10px auto;
21
  }
22
  #ThumbsTable tbody, #LogTable tbody {
40
  #LogTable td {
41
  text-align: left;
42
  }
43
+ td.title.column-title, .column-thumbupload {
44
  text-align: left !important;
45
  }
46
  #ThumbsTable img{
58
  tr.selected:hover{
59
  background: #D8D3E5 !important;
60
  }
61
+ .check-column, .column-icon {
62
  white-space: nowrap;
63
+ width: 1%;
64
  }
65
  .column-entry, .column-title {
66
+ /*width: 100%;*/
67
+ }
68
+ .column-thumbupload {
69
+ white-space: nowrap;
70
+ width: 35%;
71
+ }
72
+ #document_gallery_gen_box .column-thumbupload {
73
+ width: auto;
74
+ padding-left: 7em;
75
  }
76
  .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before, .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after{
77
  display: inline-block;
157
  }
158
  .logLabel.date {
159
  background: #999999;
160
+ /*white-space: nowrap;*/
161
  font-weight: inherit;
162
  }
163
  .logLabel.date:before{
291
  td input{
292
  margin-right: 0 !important;
293
  }
 
294
  textarea[readonly], input[readonly], select[readonly] {
295
  background-color: #dcdcdc;
296
+ }
297
+ .nowrap {
298
+ white-space: nowrap;
299
+ }
300
+ .column-level {
301
+ white-space: nowrap;
302
+ width: 6em;
303
+ }
304
+ .column-date {
305
+ width: 16em !important;
306
+ }
307
+ .column-thumbupload {
308
+ -webkit-user-select: none;
309
+ -moz-user-select: none;
310
+ -ms-user-select: none;
311
+ user-select: none;
312
+ cursor: context-menu;
313
+ }
314
+ .column-thumbupload > * {
315
+ visibility: hidden;
316
+ }
317
+ .column-thumbupload .dashicons {
318
+ font-size: x-large;
319
+ padding-top: 2px;
320
+ padding-left: 16px;
321
+ padding-right: 10px;
322
+ vertical-align: text-bottom;
323
+ }
324
+ /*Attempt to add fancy shadows*/
325
+ /*#ThumbsTable tr:hover {
326
+ z-index: 3;
327
+ border-color: #ccc;
328
+ -webkit-box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
329
+ -moz-box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
330
+ -ms-box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
331
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
332
+ }*/
333
+ #ThumbsTable tr:hover .column-thumbupload > *, #document_gallery_gen_box #ThumbsTable tr .column-thumbupload > * {
334
+ visibility: visible;
335
+ }
336
+ .dragover {
337
+ outline: 3px dashed #83b4d8;
338
+ }
339
+ .html5dndmarker span {
340
+ padding: 0px 10px;
341
+ }
342
+ .buttons-area input:first-child {
343
+ display: none !important;
344
+ }
345
+ #document_gallery_gen_box h3 span {
346
+ font-weight: 100;
347
+ }
348
+ #document_gallery_gen_box h3 span i b {
349
+ font-weight: 500;
350
  }
assets/css/style.css CHANGED
@@ -1,8 +1,8 @@
1
  div.document-icon{ text-align: center; }
2
 
3
  div.document-icon img{
4
- max-width: 89px;
5
- max-height: 89px;
6
  border: none;
7
  }
8
 
@@ -18,9 +18,6 @@ div.document-icon{
18
  display: inline-block;
19
  vertical-align: top;
20
  overflow: hidden;
21
- /* percents round up in some browsers, making
22
- only 3 icons fit per line so can't use 25% */
23
- width: 24.5%;
24
  }
25
  div.document-icon-wrapper{
26
  width: 100%;
@@ -43,8 +40,8 @@ div.descriptions.document-icon-wrapper{
43
  }
44
 
45
  div.descriptions.document-icon-wrapper img{
46
- max-width: 65px;
47
- max-height: 65px;
48
  }
49
 
50
  /* clearfix */
1
  div.document-icon{ text-align: center; }
2
 
3
  div.document-icon img{
4
+ width: 89px;
5
+ max-width: 100%;
6
  border: none;
7
  }
8
 
18
  display: inline-block;
19
  vertical-align: top;
20
  overflow: hidden;
 
 
 
21
  }
22
  div.document-icon-wrapper{
23
  width: 100%;
40
  }
41
 
42
  div.descriptions.document-icon-wrapper img{
43
+ width: 65px;
44
+ max-width: 100%;
45
  }
46
 
47
  /* clearfix */
assets/js/admin.js CHANGED
@@ -17,7 +17,7 @@ jQuery(document).ready(function(){
17
  });
18
  jQuery('input.current-page').bind('keypress', {}, function(e) {
19
  var code = (e.keyCode ? e.keyCode : e.which);
20
- if (code == 13) { //Enter keycode
21
  e.preventDefault();
22
  jQuery(location).attr('href','?'+jQuery.param(jQuery.extend(URL_params,{ sheet: this.value })));
23
  }
@@ -26,20 +26,25 @@ jQuery(document).ready(function(){
26
  jQuery(location).attr('href','?'+jQuery.param(jQuery.extend(URL_params,{ limit: this.value })));
27
  });
28
  jQuery('#tab-Thumbnail').submit( function (event) {
29
- event.preventDefault();
30
- if (jQuery('.cb-ids:checked').length > 0) {
31
  var a = jQuery(this).attr('action');
32
- var b = jQuery(this).serialize() + '&document_gallery%5Bajax%5D=true';
 
 
33
  jQuery.post(a, b, function(response) {
34
- if (response.indexOf("\n") == -1) {
35
- var result = eval(response);
36
- for (var index in result) {
37
- jQuery('input[type=checkbox][value='+result[index]+']').closest('tr').fadeOut('slow', 0.00, function() {jQuery(this).slideUp('slow', function() {jQuery(this).remove();});});
38
- }
39
- } else {
40
- console.log('Invalid response from server:');
41
- console.log(response);
42
- }
 
 
 
43
  } ).fail(function() {
44
  console.log( 'Problem in reaching the server' );
45
  });
@@ -63,8 +68,7 @@ jQuery(document).ready(function(){
63
 
64
  jQuery('.expander').click(toggleSpoiler);
65
 
66
- if (jQuery('.spoiler-body').length)
67
- {
68
  jQuery('.expandAll, .collapseAll').addClass('button');
69
  jQuery('.expandAll').click(function(e) {
70
  e.preventDefault();
@@ -90,4 +94,168 @@ jQuery(document).ready(function(){
90
  }
91
  }
92
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  });
17
  });
18
  jQuery('input.current-page').bind('keypress', {}, function(e) {
19
  var code = (e.keyCode ? e.keyCode : e.which);
20
+ if (code == 13) {//Enter keycode
21
  e.preventDefault();
22
  jQuery(location).attr('href','?'+jQuery.param(jQuery.extend(URL_params,{ sheet: this.value })));
23
  }
26
  jQuery(location).attr('href','?'+jQuery.param(jQuery.extend(URL_params,{ limit: this.value })));
27
  });
28
  jQuery('#tab-Thumbnail').submit( function (event) {
29
+ event.preventDefault();
30
+ if (jQuery('.cb-ids:checked').length > 0) {
31
  var a = jQuery(this).attr('action');
32
+ var b = jQuery(this).serialize() +
33
+ '&document_gallery%5Bajax%5D=true' +
34
+ '&document_gallery%5Bcleanup%5D=true';
35
  jQuery.post(a, b, function(response) {
36
+ if (response.indexOf("\n") == -1) {
37
+ eval('var reply = ' + response + ';');
38
+ if (reply.result) {
39
+ var result = reply.deleted;
40
+ for (var index in result) {
41
+ jQuery('input[type=checkbox][value='+result[index]+']').closest('tr').fadeOut('slow', 0.00, function() {jQuery(this).slideUp('slow', function() {jQuery(this).remove();});});
42
+ }
43
+ }
44
+ } else {
45
+ console.log('Invalid response from server:');
46
+ console.log(response);
47
+ }
48
  } ).fail(function() {
49
  console.log( 'Problem in reaching the server' );
50
  });
68
 
69
  jQuery('.expander').click(toggleSpoiler);
70
 
71
+ if (jQuery('.spoiler-body').length) {
 
72
  jQuery('.expandAll, .collapseAll').addClass('button');
73
  jQuery('.expandAll').click(function(e) {
74
  e.preventDefault();
94
  }
95
  }
96
  });
97
+
98
+ function DragDropFilesStop(e) {
99
+ e = e || event;
100
+ if (e.dataTransfer.types) {
101
+ //Testing if dragenter/dragover Event Contains Files - http://css-tricks.com/snippets/javascript/test-if-dragenterdragover-event-contains-files/
102
+ for (var i = 0; i < e.dataTransfer.types.length; i++) {
103
+ if (e.dataTransfer.types[i] == 'Files') {
104
+ //Or maybe it is better just to test e.dataTransfer.files[0].name - http://stackoverflow.com/a/12622904/3951387
105
+ //NO: before drop the list is not accessible
106
+ e.stopPropagation();
107
+ e.preventDefault();
108
+ e.dataTransfer.dropEffect = 'none';
109
+ break;
110
+ }
111
+ }
112
+ //jQuery way - Not working
113
+ /*if (jQuery.inArray('Files', e.dataTransfer.types)) {
114
+ e.stopPropagation();
115
+ e.preventDefault();
116
+ e.dataTransfer.dropEffect = 'none';
117
+ }*/
118
+ }
119
+ }
120
+
121
+ //Preventing browser from acting on drag&dropped files beside the dedicated areas
122
+ window.addEventListener('dragover', DragDropFilesStop, false);
123
+ window.addEventListener('drop', DragDropFilesStop, false);
124
+
125
+ function handleDragOver(e) {
126
+ e = e || event;
127
+ if (e.originalEvent.dataTransfer.types) {
128
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
129
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
130
+ e.stopPropagation();
131
+ e.preventDefault();
132
+ //Have to exploit broker to access standart properties while using jQuery to bind handlers - http://stackoverflow.com/a/14792183/3951387
133
+ e.originalEvent.dataTransfer.dropEffect = 'move';
134
+ return false;
135
+ }
136
+ }
137
+ }
138
+ }
139
+
140
+ //Firing HTML5 DragLeave only when all the DragEnter'ed child elements were DragLeave'd - http://stackoverflow.com/a/21002544
141
+ var counter = {};
142
+ function handleDragEnter(e) {
143
+ // this / e.target is the current hover target.
144
+ e = e || event;
145
+ if (e.originalEvent.dataTransfer.types) {
146
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
147
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
148
+ this.classList.add('dragover');
149
+ counter[jQuery(this).data('entry')]++;//or without jQuery: this.getAttribute('data-entry')
150
+ break;
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ function handleDragLeave(e) {
157
+ e = e || event;
158
+ if (e.originalEvent.dataTransfer.types) {
159
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
160
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
161
+ counter[jQuery(this).data('entry')]--;
162
+ if (counter[jQuery(this).data('entry')] === 0) {
163
+ this.classList.remove('dragover');// this / e.target is previous target element.
164
+ }
165
+ break;
166
+ }
167
+ }
168
+ }
169
+ }
170
+
171
+ function handleDrop(e) {
172
+ e = e || event;
173
+ if (e.originalEvent.dataTransfer.types) {
174
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
175
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
176
+
177
+ e.stopPropagation();// Stops some browsers from redirecting.
178
+ e.preventDefault();
179
+
180
+ processFiles(e.originalEvent.dataTransfer.files,jQuery(this).data('entry'));
181
+ counter[jQuery(this).data('entry')] = 0;
182
+ this.classList.remove('dragover');
183
+ break;
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ function handleBrowseButton(e) {
190
+ e = e || event;
191
+ processFiles(e.target.files,jQuery(this).closest('tr').data('entry'));
192
+ //Was thinking about purging input:file control - http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery
193
+ //Decided just to get rid of name properties thus such controls wouldn't be taken into consideration during form submit or processed with FormData
194
+ }
195
+
196
+ function processFiles(files,entry) {
197
+ for (var i = 0, f; f = files[i]; i++) {
198
+ //Processing only first qualifying file
199
+ if (f.type.indexOf('image/') == 0 && typeof dg_admin_vars.upload_limit != 'undefined' && f.size <= parseInt(dg_admin_vars.upload_limit)) {
200
+ var target;
201
+ var formData= new FormData(jQuery('[data-entry='+entry+']').closest('form')[0]);
202
+ if (typeof ajax_object != 'undefined' && typeof ajax_object.ajax_url != 'undefined') {
203
+ target = ajax_object.ajax_url;
204
+ formData.append('action', 'dg_upload_thumb');
205
+ } else {
206
+ target = jQuery('#tab-Thumbnail').attr('action');
207
+ }
208
+ formData.append('document_gallery[entry]', entry);
209
+ formData.append('document_gallery[ajax]', 'true');
210
+ formData.append('document_gallery[upload]', 'true');
211
+ formData.append('file', f);
212
+ var xhr = new XMLHttpRequest();
213
+ xhr.open('POST', target);
214
+ var theImg = jQuery('[data-entry='+entry+']').find('.column-icon img');
215
+ xhr.onreadystatechange = function() {
216
+ if (xhr.readyState == 4) {
217
+ if (xhr.responseText.indexOf("\n") == -1) {
218
+ eval('var response = ' + xhr.responseText + ';');
219
+ if (response.result) {
220
+ // check if generated thumbnail has the same url
221
+ if (response.url === theImg.attr('src')) {
222
+ theImg.attr('src', theImg.attr('src') + '?' + new Date().getTime());
223
+ } else {
224
+ theImg.attr('src', response.url);
225
+ }
226
+ }
227
+ } else {
228
+ console.log('Invalid response from server:');
229
+ console.log(xhr.responseText);
230
+ }
231
+ }
232
+ }
233
+ xhr.send(formData);
234
+ break;
235
+ }
236
+ }
237
+ }
238
+
239
+ // Prepairing all the drop-zones on page load
240
+ jQuery('#ThumbsTable tbody tr').each(function() {
241
+ jQuery(this)
242
+ .on('dragenter', handleDragEnter)
243
+ .on('dragover', handleDragOver)
244
+ .on('dragleave', handleDragLeave)
245
+ .on('drop', handleDrop);
246
+ counter[jQuery(this).data('entry')] = 0;
247
+ jQuery(this).find('input:button').on('click', function() {
248
+ jQuery(this).prevAll('input:file').click();
249
+ });
250
+ jQuery(this).find('input:file').on('change', handleBrowseButton);
251
+ });
252
+
253
+ //Checking Drag&Drop support
254
+ //Structure is Not supported in Chrome's WebKit
255
+ /*if (!('files' in DataTransfer.prototype)) {//Determine presence of HTML5 drag'n'drop file upload API - http://stackoverflow.com/a/2312859/3951387
256
+ jQuery('.html5dndmarker').hide();
257
+ }*/
258
+ if (!('draggable' in document.createElement('span'))) {
259
+ jQuery('.html5dndmarker').hide();
260
+ }
261
  });
document-gallery.php CHANGED
@@ -5,14 +5,14 @@ defined('WPINC') OR exit;
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__));
@@ -26,6 +26,9 @@ 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
 
@@ -52,17 +55,25 @@ 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
 
@@ -197,7 +208,6 @@ class DocumentGallery {
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
 
@@ -228,13 +238,15 @@ class DocumentGallery {
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
  /**
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: 3.0.0-beta
9
  Author: Dan Rossiter
10
  Author URI: http://danrossiter.org/
11
  License: GPLv2
12
  Text Domain: document-gallery
13
  */
14
 
15
+ define('DG_VERSION', '3.0.0-beta');
16
 
17
  // define helper paths & URLs
18
  define('DG_BASENAME', plugin_basename(__FILE__));
26
  define('DG_OPTION_NAME', 'document_gallery');
27
  $dg_options = get_option(DG_OPTION_NAME, null);
28
 
29
+ // DG general utility functions
30
+ include_once DG_PATH . 'inc/class-util.php';
31
+
32
  // logging functionality
33
  include_once DG_PATH . 'inc/class-logger.php';
34
 
55
  // admin house keeping
56
  include_once DG_PATH . 'admin/class-admin.php';
57
 
58
+ // add links to plugin index
59
  add_filter('plugin_action_links_' . DG_BASENAME, array('DG_Admin', 'addSettingsLink'));
60
+ add_filter('plugin_row_meta', array('DG_Admin', 'addDonateLink'), 10, 2);
61
 
62
  // build options page
63
  add_action('admin_menu', array('DG_Admin', 'addAdminPage'));
64
+
65
+ // add meta box for managing thumbnail generation to attachment Edit Media page
66
+ add_action('add_meta_boxes', array('DG_Admin', 'addMetaBox'));
67
+ add_action('wp_ajax_dg_upload_thumb', array('DG_Admin', 'saveMetaBox'));
68
+
69
  if (DG_Admin::doRegisterSettings()) {
70
  add_action('admin_init', array('DG_Admin', 'registerSettings'));
71
  }
72
  } else {
73
  // styling for gallery
74
+ if (apply_filters('dg_use_default_gallery_style', true )) {
75
+ add_action('wp_enqueue_scripts', array('DocumentGallery', 'enqueueGalleryStyle'));
76
+ }
77
  add_action('wp_print_scripts', array('DocumentGallery', 'printCustomStyle'));
78
  }
79
 
208
  */
209
  private static function isValidOptionsStructure($o, $schema = null) {
210
  if (is_null($schema)) {
 
211
  $schema = DG_Setup::getDefaultOptions(true);
212
  }
213
 
238
  */
239
  public static function localDateTimeFromTimestamp($timestamp) {
240
  static $gmt_offet = null;
241
+ static $wp_date_format = null;
242
+ static $wp_time_format = null;
243
  if (is_null($gmt_offet)) {
244
  $gmt_offet = get_option('gmt_offset');
245
+ $wp_date_format = get_option('date_format');
246
+ $wp_time_format = get_option('time_format');
247
  }
248
 
249
+ return '<span class="nowrap">'.date_i18n($wp_date_format, $timestamp + $gmt_offet * 3600).'</span> <span class="nowrap">'.date_i18n($wp_time_format, $timestamp + $gmt_offet * 3600).'</span>';
250
  }
251
 
252
  /**
inc/class-document.php CHANGED
@@ -29,12 +29,12 @@ class DG_Document {
29
 
30
  // init general document data
31
  $this->gallery = $gallery;
32
- $this->description = $attachment->post_content;
33
  $this->ID = $attachment->ID;
34
  $this->link = $gallery->linkToAttachmentPg()
35
  ? get_attachment_link($attachment->ID)
36
  : wp_get_attachment_url($attachment->ID);
37
- $this->title = get_the_title($attachment->ID);
38
  $this->title_attribute = esc_attr(strip_tags($this->title));
39
  }
40
 
@@ -46,7 +46,6 @@ class DG_Document {
46
  * Returns HTML representing this Document.
47
  * @filter dg_icon_template Filters the DG icon HTML. Passes a single
48
  * bool value indicating whether the gallery is using descriptions or not.
49
- * @filter dg_doc_icon Deprecated. To be removed in a future relesase.
50
  * @return string
51
  */
52
  public function __toString() {
@@ -65,20 +64,20 @@ class DG_Document {
65
  $description = ' <p>%description%</p>';
66
  }
67
 
68
- // allow developers to filter icon output
69
- $doc_icon = apply_filters(
70
- 'dg_icon_template',
71
  ' <div class="document-icon">' . PHP_EOL .
72
  ' <a href="%link%"><img src="%img%" title="%title_attribute%" alt="%title_attribute%" /><br>%title%</a>' . PHP_EOL .
73
  ' </div>' . PHP_EOL .
74
- $description,
 
 
 
 
 
75
  $this->gallery->useDescriptions(),
76
  $this->ID);
77
 
78
- $core = str_replace($find, $repl, $doc_icon);
79
-
80
- // deprecated: users may filter icon here
81
- return apply_filters('dg_doc_icon', $core, $this->ID, $this->gallery->useDescriptions());
82
  }
83
  }
84
 
29
 
30
  // init general document data
31
  $this->gallery = $gallery;
32
+ $this->description = wptexturize($attachment->post_content);
33
  $this->ID = $attachment->ID;
34
  $this->link = $gallery->linkToAttachmentPg()
35
  ? get_attachment_link($attachment->ID)
36
  : wp_get_attachment_url($attachment->ID);
37
+ $this->title = wptexturize($attachment->post_title);
38
  $this->title_attribute = esc_attr(strip_tags($this->title));
39
  }
40
 
46
  * Returns HTML representing this Document.
47
  * @filter dg_icon_template Filters the DG icon HTML. Passes a single
48
  * bool value indicating whether the gallery is using descriptions or not.
 
49
  * @return string
50
  */
51
  public function __toString() {
64
  $description = ' <p>%description%</p>';
65
  }
66
 
67
+ $doc_icon =
 
 
68
  ' <div class="document-icon">' . PHP_EOL .
69
  ' <a href="%link%"><img src="%img%" title="%title_attribute%" alt="%title_attribute%" /><br>%title%</a>' . PHP_EOL .
70
  ' </div>' . PHP_EOL .
71
+ $description;
72
+
73
+ // allow developers to filter icon output
74
+ $doc_icon = apply_filters(
75
+ 'dg_icon_template',
76
+ $doc_icon,
77
  $this->gallery->useDescriptions(),
78
  $this->ID);
79
 
80
+ return str_replace($find, $repl, $doc_icon);
 
 
 
81
  }
82
  }
83
 
inc/class-gallery.php CHANGED
@@ -77,8 +77,7 @@ class DG_Gallery {
77
  * Initializes static values for this class.
78
  */
79
  public static function init() {
80
- if (!isset(self::$comment))
81
- {
82
  self::$comment =
83
  PHP_EOL . '<!-- ' . __('Generated using Document Gallery. Get yours here: ', 'document-gallery') .
84
  'http://wordpress.org/extend/plugins/document-gallery -->' . PHP_EOL;
@@ -93,9 +92,49 @@ class DG_Gallery {
93
  * @param multitype:string $atts Array of attributes used in shortcode.
94
  */
95
  public function __construct($atts) {
 
 
 
 
96
  // empty string is passed when no arguments are given, but constructor expects an array
97
  $atts = empty($atts) ? array() : $atts;
98
- $defaults = self::getOptions();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  // values used to construct tax query (may be empty)
101
  $this->taxa = array_diff_key($atts, $defaults);
@@ -103,35 +142,37 @@ class DG_Gallery {
103
  // all recognized attributes go here
104
  $this->atts = shortcode_atts($defaults, $atts);
105
 
106
- // goes through all values in $this->atts, setting $this->errs as needed
107
- $this->atts = self::sanitizeDefaults($this->atts, $this->errs);
108
 
109
  // query DB for all documents requested
110
- include_once DG_PATH . 'inc/class-document.php';
111
  try {
112
- $docs = $this->getDocuments();
113
-
114
- foreach($docs as $doc) {
115
  $this->docs[] = new DG_Document($doc, $this);
116
  }
117
  } catch(InvalidArgumentException $e) {
118
  // errors will be printed in __toString()
119
  }
120
  }
121
-
122
  /**
123
  * Cleans up user input, making sure we don't pass crap on to WP core.
 
124
  * @param multitype:string $defaults The defaults array to sanitize.
125
  * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
126
- * @param bool $isDefaults Whether we're sanitizing the defaults array (from DG_Admin).
127
  */
128
- public static function sanitizeDefaults($defaults, &$errs, $isDefaults = false) {
129
- $old_defaults = self::getOptions();
 
 
130
 
131
  // remove invalid keys
132
- $sanitized = array_intersect_key($defaults, $old_defaults);
133
-
134
- // add any missing keys & sanitize each value
 
 
135
  foreach ($old_defaults as $k => $v) {
136
  if (!isset($sanitized[$k])) {
137
  if (is_bool($v)) {
@@ -141,29 +182,15 @@ class DG_Gallery {
141
  // missing value
142
  $sanitized[$k] = $v;
143
  }
144
- }
145
-
146
- // sanitize value
147
- $sanitized[$k] = self::sanitizeParameter($k, $sanitized[$k], $errs);
148
- }
149
-
150
- // process mime_types attribute separately since default value varies depending on images attribute
151
- // TODO: Cleaner way to handle this?
152
- if (!$isDefaults) {
153
- if (isset($defaults['mime_types'])) {
154
- $sanitized['mime_types'] = self::sanitizeMimeTypes($defaults['mime_types'], $err);
155
- if (isset($err)) {
156
- $errs['mime_types'] = $err;
157
- unset($err);
158
- }
159
- } else {
160
- $sanitized['mime_types'] = self::getDefaultMimeTypes($sanitized['images']);
161
  }
162
  }
163
 
164
  return $sanitized;
165
  }
166
-
167
  /**
168
  *
169
  * @param string $key The key to reference the current value in the defaults array.
@@ -176,9 +203,10 @@ class DG_Gallery {
176
  $funct = $key;
177
  $funct[0] = strtoupper($funct[0]);
178
  $funct = 'sanitize' . preg_replace_callback('/_([a-z])/', array(__CLASS__, 'secondCharToUpper'), $funct);
179
-
180
  $callable = array(__CLASS__, $funct);
181
-
 
182
  if (DG_Logger::logEnabled() && !method_exists(__CLASS__, $funct)) {
183
  DG_Logger::writeLog(
184
  DG_LogLevel::Error,
@@ -188,15 +216,15 @@ class DG_Gallery {
188
 
189
  // call param-specific sanitization
190
  $ret = call_user_func_array($callable, array($value, &$err));
191
-
192
  // check for error and return default
193
- if (is_null($ret)) {
194
  $defaults = self::getOptions();
195
  $ret = $defaults[$key];
196
-
197
  $errs[$key] = $err;
198
  }
199
-
200
  return $ret;
201
  }
202
 
@@ -216,6 +244,16 @@ class DG_Gallery {
216
  return $ret;
217
  }
218
 
 
 
 
 
 
 
 
 
 
 
219
  /**
220
  * Takes the provided value and returns a sanitized value.
221
  * @param string $value The descriptions value to be sanitized.
@@ -232,6 +270,16 @@ class DG_Gallery {
232
  return $ret;
233
  }
234
 
 
 
 
 
 
 
 
 
 
 
235
  /**
236
  * Takes the provided value and returns a sanitized value.
237
  * @param string $value The fancy value to be sanitized.
@@ -247,27 +295,32 @@ class DG_Gallery {
247
 
248
  return $ret;
249
  }
250
-
251
  /**
252
  * Takes the provided value and returns a sanitized value.
 
 
 
 
 
 
 
 
 
 
 
253
  * @param string $value The ids value to be sanitized.
254
  * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
255
- * @return bool|multitype:int The sanitized ids value.
256
  */
257
- private static function sanitizeIds($value, &$err) {
258
- if(false === self::toBool($value)) {
259
- $ret = false;
260
- } else {
261
- $value = trim($value);
262
- $ret = $value ? explode(',', $value) : array();
263
- $bad = array_filter($ret, array(__CLASS__, 'negativeInt'));
264
-
265
- if(!empty($bad)) {
266
- $err = _n('The following ID is invalid: ',
267
- 'The following IDs are invalid: ',
268
- count($bad), 'document-gallery') . implode(', ', $bad);
269
- $ret = null;
270
- }
271
  }
272
 
273
  return $ret;
@@ -275,18 +328,12 @@ class DG_Gallery {
275
 
276
  /**
277
  * Takes the provided value and returns a sanitized value.
278
- * @param string $value The images value to be sanitized.
279
  * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
280
- * @return bool The sanitized images value.
281
  */
282
- private static function sanitizeImages($value, &$err) {
283
- $ret = self::toBool($value);
284
-
285
- if(is_null($ret)) {
286
- $err = sprintf(self::$binary_err, 'images', 'true', 'false', $value);
287
- }
288
-
289
- return $ret;
290
  }
291
 
292
  /**
@@ -297,27 +344,11 @@ class DG_Gallery {
297
  */
298
  private static function sanitizeLimit($value, &$err) {
299
  $ret = intval($value);
300
-
301
  if (is_null($ret) || $ret < -1) {
302
  $err = sprintf(self::$unary_err, 'limit', '>= -1');
303
  $ret = null;
304
  }
305
-
306
- return $ret;
307
- }
308
-
309
- /**
310
- * Takes the provided value and returns a sanitized value.
311
- * @param string $value The localpost value to be sanitized.
312
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
313
- * @return bool The sanitized localpost value.
314
- */
315
- private static function sanitizeLocalpost($value, &$err) {
316
- $ret = self::toBool($value);
317
-
318
- if(is_null($ret)) {
319
- $err = sprintf(self::$binary_err, 'localpost', 'true', 'false', $value);
320
- }
321
 
322
  return $ret;
323
  }
@@ -332,20 +363,6 @@ class DG_Gallery {
332
  // TODO: do some actual sanitization...
333
  return $value;
334
  }
335
-
336
- /**
337
- * Gets the default mime types if no attribute is set.
338
- * @param bool $ingImages Whether images are included in this gallery.
339
- * @return string The comma-delimited mime types.
340
- */
341
- private static function getDefaultMimeTypes($incImages) {
342
- $mime_types = array('application', 'video', 'text', 'audio');
343
- if ($incImages) {
344
- $mime_types[] = 'image';
345
- }
346
-
347
- return implode(',', $mime_types);
348
- }
349
 
350
  /**
351
  * Takes the provided value and returns a sanitized value.
@@ -355,7 +372,7 @@ class DG_Gallery {
355
  */
356
  private static function sanitizeOrder($value, &$err) {
357
  $ret = strtoupper($value);
358
-
359
  if(!in_array($ret, self::getOrderOptions())) {
360
  $err = sprintf(self::$binary_err, 'order', 'ASC', 'DESC', $value);
361
  $ret = null;
@@ -379,7 +396,7 @@ class DG_Gallery {
379
  */
380
  private static function sanitizeOrderby($value, &$err) {
381
  $ret = ('ID' === strtoupper($value)) ? 'ID' : strtolower($value);
382
-
383
  if (!in_array($ret, self::getOrderbyOptions())) {
384
  $err = sprintf(self::$unary_err, 'orderby', $value);
385
  $ret = null;
@@ -396,7 +413,7 @@ class DG_Gallery {
396
  'menu_order', 'modified', 'name', 'none',
397
  'parent', 'post__in', 'rand', 'title');
398
  }
399
-
400
  /**
401
  * Takes the provided value and returns a sanitized value.
402
  * @param string $value The post_status value to be sanitized.
@@ -404,16 +421,16 @@ class DG_Gallery {
404
  * @return string The sanitized post_status value.
405
  */
406
  private static function sanitizePostStatus($value, &$err) {
407
- $ret = preg_grep('/' . preg_quote($value) .'/i', self::getPostStatuses());
408
  $ret = reset($ret);
409
-
410
  if($ret === false) {
411
  $err = sprintf(self::$unary_err, 'post_status', $value);
412
  }
413
-
414
  return $ret;
415
  }
416
-
417
  /**
418
  * @return multitype:string All registered post statuses.
419
  */
@@ -424,10 +441,10 @@ class DG_Gallery {
424
  $statuses[] = 'any';
425
  asort($statuses);
426
  }
427
-
428
  return $statuses;
429
  }
430
-
431
  /**
432
  * Takes the provided value and returns a sanitized value.
433
  * @param string $value The post_type value to be sanitized.
@@ -435,16 +452,16 @@ class DG_Gallery {
435
  * @return string The sanitized post_type value.
436
  */
437
  private static function sanitizePostType($value, &$err) {
438
- $ret = preg_grep('/' . preg_quote($value) .'/i', self::getPostTypes());
439
  $ret = reset($ret);
440
-
441
  if($ret === false) {
442
  $err = sprintf(self::$unary_err, 'post_type', $value);
443
  }
444
-
445
  return $ret;
446
  }
447
-
448
  /**
449
  * @return multitype:string All registered post types.
450
  */
@@ -455,7 +472,7 @@ class DG_Gallery {
455
  $types[] = 'any';
456
  asort($types);
457
  }
458
-
459
  return $types;
460
  }
461
 
@@ -467,7 +484,7 @@ class DG_Gallery {
467
  */
468
  private static function sanitizeRelation($value, &$err) {
469
  $ret = strtoupper($value);
470
-
471
  if(!in_array($ret, self::getRelationOptions())) {
472
  $err = sprintf(self::$binary_err, 'relation', 'AND', 'OR', $value);
473
  $ret = null;
@@ -490,7 +507,7 @@ class DG_Gallery {
490
  */
491
  private function sanitizeOperator($operator) {
492
  $ret = strtoupper($operator);
493
-
494
  if (!in_array($ret, self::getOperatorOptions())) {
495
  $this->errs[] = sprintf(self::$binary_err, $key, 'IN", "NOT IN", "OR', 'AND', $operator);
496
  $ret = null;
@@ -507,9 +524,10 @@ class DG_Gallery {
507
  public static function getOperatorOptions() {
508
  return array('IN', 'NOT IN', 'AND', 'OR');
509
  }
510
-
511
  /**
512
  * Gets all valid Documents based on the attributes passed by the user.
 
513
  * @return multitype:unknown Contains all documents matching the query.
514
  * @throws InvalidArgumentException Thrown when $this->errs is not empty.
515
  */
@@ -522,19 +540,28 @@ class DG_Gallery {
522
  'post_type' => $this->atts['post_type'],
523
  'post_mime_type' => $this->atts['mime_types']);
524
 
525
- $query['post_parent'] =
526
- $this->atts['localpost']
527
- && ($post = get_post()) ? $post->ID : '';
528
-
529
  $this->setTaxa($query);
530
 
531
  if(!empty($this->errs)) {
532
  throw new InvalidArgumentException();
533
  }
534
 
535
- return (false !== $this->atts['ids'])
536
- ? $this->getAttachmentsByIds()
537
- : get_posts($query);
 
 
 
 
 
 
 
 
 
 
 
 
 
538
  }
539
 
540
  /**
@@ -549,7 +576,7 @@ class DG_Gallery {
549
  $operator = array();
550
  $suffix = array('relation', 'operator');
551
  $pattern = '/(.+)_(?:' . implode('|', $suffix) . ')$/i';
552
-
553
  // find any relations for taxa
554
  $iterable = $this->taxa;
555
  foreach ($iterable as $key => $value) {
@@ -561,7 +588,7 @@ class DG_Gallery {
561
  }
562
  }
563
  }
564
-
565
  // build tax query
566
  foreach ($this->taxa as $taxon => $terms) {
567
  $terms = $this->getTermIdsByNames($taxon, explode(',', $terms));
@@ -619,11 +646,11 @@ class DG_Gallery {
619
  private function getTermXByNames($x, $taxon, $term_names) {
620
  $ret = array();
621
  $valid = true;
622
-
623
  // taxons may optionally be prefixed by 'tax_' --
624
  // this is only useful when avoiding collisions with other attributes
625
  if (!taxonomy_exists($taxon)) {
626
- $tmp = preg_replace('^tax_(.*)', '$1', $taxon, 1, $count);
627
  if ($count > 0 && taxonomy_exists($tmp)) {
628
  $taxon = $tmp;
629
  } else {
@@ -647,22 +674,6 @@ class DG_Gallery {
647
  return $ret;
648
  }
649
 
650
- /**
651
- * Given a list of IDs, all attachments represented by these IDs are returned.
652
- * @return multitype:Post The posts matched.
653
- */
654
- private function getAttachmentsByIds() {
655
- $args = array(
656
- 'post_type' => $this->atts['post_type'],
657
- 'post_status' => $this->atts['post_status'],
658
- 'numberposts' => $this->atts['limit'],
659
- 'post__in' => $this->atts['ids'],
660
- 'orderby' => 'post__in'
661
- );
662
-
663
- return count($args['post__in']) ? get_posts($args) : array();
664
- }
665
-
666
  /**
667
  * @param string $string To take second char from.
668
  * @return char Capitalized second char of given string.
@@ -670,7 +681,7 @@ class DG_Gallery {
670
  private static function secondCharToUpper($string) {
671
  return strtoupper($string[1]);
672
  }
673
-
674
  /**
675
  * Function returns false for positive ints, true otherwise.
676
  * @param string $var could be anything.
@@ -691,16 +702,16 @@ class DG_Gallery {
691
  if (is_null($val)) {
692
  return false;
693
  }
694
-
695
  if (is_bool($val)) {
696
  return $val;
697
  }
698
-
699
  if (is_int($val)) {
700
  if (1 === $val) {
701
  return true;
702
  }
703
-
704
  if (0 === $val) {
705
  return false;
706
  }
@@ -731,22 +742,31 @@ class DG_Gallery {
731
  * @return string HTML representing this Gallery.
732
  */
733
  public function __toString() {
 
 
 
734
  static $find = null;
735
  if (is_null($find)) {
736
  $find = array('%class%', '%icons%');
737
  }
738
-
739
- if(!empty($this->errs)) {
740
  return '<p>' . implode('</p><p>', $this->errs) . '</p>';
741
  }
742
 
743
- if(empty($this->docs)) {
744
  return self::$no_docs;
745
  }
 
 
 
 
 
 
746
 
747
  $icon_wrapper = apply_filters(
748
  'dg_row_template',
749
- '<div class="%class%">'. PHP_EOL . '%icons%' . PHP_EOL . '</div>' . PHP_EOL,
750
  $this->useDescriptions());
751
 
752
  $core = '';
@@ -762,10 +782,20 @@ class DG_Gallery {
762
  $core .= str_replace($find, $repl, $icon_wrapper);
763
  }
764
  } else {
765
- for($i = 0; $i < count($this->docs); $i+=4) {
 
 
 
 
 
 
 
 
 
 
766
  $repl[1] = '';
767
 
768
- $min = min($i+4, count($this->docs));
769
  for($x = $i; $x < $min; $x++) {
770
  $repl[1] .= $this->docs[$x];
771
  }
77
  * Initializes static values for this class.
78
  */
79
  public static function init() {
80
+ if (!isset(self::$comment)) {
 
81
  self::$comment =
82
  PHP_EOL . '<!-- ' . __('Generated using Document Gallery. Get yours here: ', 'document-gallery') .
83
  'http://wordpress.org/extend/plugins/document-gallery -->' . PHP_EOL;
92
  * @param multitype:string $atts Array of attributes used in shortcode.
93
  */
94
  public function __construct($atts) {
95
+ include_once DG_PATH . 'inc/class-document.php';
96
+
97
+ $post = get_post();
98
+
99
  // empty string is passed when no arguments are given, but constructor expects an array
100
  $atts = empty($atts) ? array() : $atts;
101
+
102
+ if (!empty($atts['ids'])) {
103
+ // 'ids' is explicitly ordered, unless you specify otherwise.
104
+ if (empty($atts['orderby'])) {
105
+ $atts['orderby'] = 'post__in';
106
+ }
107
+
108
+ $atts['include'] = $atts['ids'];
109
+ unset($atts['ids']);
110
+ }
111
+
112
+ // allow abbreviated columns attribute
113
+ if (!empty($atts['cols'])) {
114
+ $atts['columns'] = $atts['cols'];
115
+ unset($atts['cols']);
116
+ }
117
+
118
+ if (!empty($atts['images'])) {
119
+ $options = self::getOptions();
120
+ $mimes = trim(isset($atts['mime_types']) ? $atts['mime_types'] : $options['mime_types']);
121
+ if (!preg_match('/[,^]image[,$]/', $mimes)) {
122
+ $atts['mime_types'] = empty($mimes) ? 'image' : ($mimes . ',image');
123
+ }
124
+ }
125
+
126
+ /**
127
+ * @deprecated localpost will be removed at some point.
128
+ */
129
+ if (!empty($atts['localpost'])) {
130
+ $atts['id'] = -1;
131
+ unset($atts['localpost']);
132
+ }
133
+
134
+ // merge options w/ default values not stored in options
135
+ $defaults = array_merge(
136
+ array('id' => $post->ID, 'include' => '', 'exclude' => ''),
137
+ self::getOptions());
138
 
139
  // values used to construct tax query (may be empty)
140
  $this->taxa = array_diff_key($atts, $defaults);
142
  // all recognized attributes go here
143
  $this->atts = shortcode_atts($defaults, $atts);
144
 
145
+ // goes through all values in atts, setting errs as needed
146
+ $this->atts = self::sanitizeDefaults($defaults, $this->atts, $this->errs);
147
 
148
  // query DB for all documents requested
 
149
  try {
150
+ foreach($this->getDocuments() as $doc) {
 
 
151
  $this->docs[] = new DG_Document($doc, $this);
152
  }
153
  } catch(InvalidArgumentException $e) {
154
  // errors will be printed in __toString()
155
  }
156
  }
157
+
158
  /**
159
  * Cleans up user input, making sure we don't pass crap on to WP core.
160
+ * @param multitype:string $old_defaults The previous set of defaults.
161
  * @param multitype:string $defaults The defaults array to sanitize.
162
  * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
163
+ * @param bool $isDefaults Whether we're sanitizing the defaults array from DG_Admin.
164
  */
165
+ public static function sanitizeDefaults($old_defaults, $defaults, &$errs) {
166
+ if (is_null($old_defaults)) {
167
+ $old_defaults = self::getOptions();
168
+ }
169
 
170
  // remove invalid keys
171
+ $sanitized = is_array($defaults)
172
+ ? array_intersect_key($defaults, $old_defaults)
173
+ : array();
174
+
175
+ // add any missing keys & sanitize each new value
176
  foreach ($old_defaults as $k => $v) {
177
  if (!isset($sanitized[$k])) {
178
  if (is_bool($v)) {
182
  // missing value
183
  $sanitized[$k] = $v;
184
  }
185
+ } else if ($sanitized[$k] != $v) {
186
+ // sanitize value if different from old value
187
+ $sanitized[$k] = self::sanitizeParameter($k, $sanitized[$k], $errs);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
188
  }
189
  }
190
 
191
  return $sanitized;
192
  }
193
+
194
  /**
195
  *
196
  * @param string $key The key to reference the current value in the defaults array.
203
  $funct = $key;
204
  $funct[0] = strtoupper($funct[0]);
205
  $funct = 'sanitize' . preg_replace_callback('/_([a-z])/', array(__CLASS__, 'secondCharToUpper'), $funct);
206
+
207
  $callable = array(__CLASS__, $funct);
208
+
209
+ // avoid looking for method beforehand unless we're running in debug mode -- expensive call
210
  if (DG_Logger::logEnabled() && !method_exists(__CLASS__, $funct)) {
211
  DG_Logger::writeLog(
212
  DG_LogLevel::Error,
216
 
217
  // call param-specific sanitization
218
  $ret = call_user_func_array($callable, array($value, &$err));
219
+
220
  // check for error and return default
221
+ if (isset($err)) {
222
  $defaults = self::getOptions();
223
  $ret = $defaults[$key];
224
+
225
  $errs[$key] = $err;
226
  }
227
+
228
  return $ret;
229
  }
230
 
244
  return $ret;
245
  }
246
 
247
+ /**
248
+ * Takes the provided value and returns a sanitized value.
249
+ * @param string $value The columns value to be sanitized.
250
+ * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
251
+ * @return int The sanitized columns value.
252
+ */
253
+ public static function sanitizeColumns($value, &$err) {
254
+ return $value != -1 ? absint($value) : null;
255
+ }
256
+
257
  /**
258
  * Takes the provided value and returns a sanitized value.
259
  * @param string $value The descriptions value to be sanitized.
270
  return $ret;
271
  }
272
 
273
+ /**
274
+ * Takes the provided value and returns a sanitized value.
275
+ * @param string $value The exclude value to be sanitized.
276
+ * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
277
+ * @return bool The sanitized exclude value.
278
+ */
279
+ private static function sanitizeExclude($value, &$err) {
280
+ return self::sanitizeIdList('Exclude', $value, $err);
281
+ }
282
+
283
  /**
284
  * Takes the provided value and returns a sanitized value.
285
  * @param string $value The fancy value to be sanitized.
295
 
296
  return $ret;
297
  }
298
+
299
  /**
300
  * Takes the provided value and returns a sanitized value.
301
+ * @param string $value The id value to be sanitized.
302
+ * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
303
+ * @return int The sanitized id value.
304
+ */
305
+ private static function sanitizeId($value, &$err) {
306
+ return $value != -1 ? absint($value) : null;
307
+ }
308
+
309
+ /**
310
+ * Takes the provided comma-delimited list of IDs and returns null if it is invalid.
311
+ * @param string $name Name of the value being sanitized. Used in error string when needed.
312
  * @param string $value The ids value to be sanitized.
313
  * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
314
+ * @return bool|multitype:int The sanitized comma-delimited list of IDs value.
315
  */
316
+ private static function sanitizeIdList($name, $value, &$err) {
317
+ static $regex = '/(?:|\d+(?:,\d+)*)/';
318
+
319
+ $ret = $value;
320
+
321
+ if (!preg_match($regex, $value)) {
322
+ $err = sprintf(__('%s may only be a comma-delimited list of integers.', 'document-gallery'), name);
323
+ $ret = null;
 
 
 
 
 
 
324
  }
325
 
326
  return $ret;
328
 
329
  /**
330
  * Takes the provided value and returns a sanitized value.
331
+ * @param string $value The ids value to be sanitized.
332
  * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
333
+ * @return bool|multitype:int The sanitized ids value.
334
  */
335
+ private static function sanitizeInclude($value, &$err) {
336
+ return self::sanitizeIdList('Include', $value, $err);
 
 
 
 
 
 
337
  }
338
 
339
  /**
344
  */
345
  private static function sanitizeLimit($value, &$err) {
346
  $ret = intval($value);
347
+
348
  if (is_null($ret) || $ret < -1) {
349
  $err = sprintf(self::$unary_err, 'limit', '>= -1');
350
  $ret = null;
351
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
 
353
  return $ret;
354
  }
363
  // TODO: do some actual sanitization...
364
  return $value;
365
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
366
 
367
  /**
368
  * Takes the provided value and returns a sanitized value.
372
  */
373
  private static function sanitizeOrder($value, &$err) {
374
  $ret = strtoupper($value);
375
+
376
  if(!in_array($ret, self::getOrderOptions())) {
377
  $err = sprintf(self::$binary_err, 'order', 'ASC', 'DESC', $value);
378
  $ret = null;
396
  */
397
  private static function sanitizeOrderby($value, &$err) {
398
  $ret = ('ID' === strtoupper($value)) ? 'ID' : strtolower($value);
399
+
400
  if (!in_array($ret, self::getOrderbyOptions())) {
401
  $err = sprintf(self::$unary_err, 'orderby', $value);
402
  $ret = null;
413
  'menu_order', 'modified', 'name', 'none',
414
  'parent', 'post__in', 'rand', 'title');
415
  }
416
+
417
  /**
418
  * Takes the provided value and returns a sanitized value.
419
  * @param string $value The post_status value to be sanitized.
421
  * @return string The sanitized post_status value.
422
  */
423
  private static function sanitizePostStatus($value, &$err) {
424
+ $ret = preg_grep('/^' . preg_quote($value) .'$/i', self::getPostStatuses());
425
  $ret = reset($ret);
426
+
427
  if($ret === false) {
428
  $err = sprintf(self::$unary_err, 'post_status', $value);
429
  }
430
+
431
  return $ret;
432
  }
433
+
434
  /**
435
  * @return multitype:string All registered post statuses.
436
  */
441
  $statuses[] = 'any';
442
  asort($statuses);
443
  }
444
+
445
  return $statuses;
446
  }
447
+
448
  /**
449
  * Takes the provided value and returns a sanitized value.
450
  * @param string $value The post_type value to be sanitized.
452
  * @return string The sanitized post_type value.
453
  */
454
  private static function sanitizePostType($value, &$err) {
455
+ $ret = preg_grep('/^' . preg_quote($value) .'$/i', self::getPostTypes());
456
  $ret = reset($ret);
457
+
458
  if($ret === false) {
459
  $err = sprintf(self::$unary_err, 'post_type', $value);
460
  }
461
+
462
  return $ret;
463
  }
464
+
465
  /**
466
  * @return multitype:string All registered post types.
467
  */
472
  $types[] = 'any';
473
  asort($types);
474
  }
475
+
476
  return $types;
477
  }
478
 
484
  */
485
  private static function sanitizeRelation($value, &$err) {
486
  $ret = strtoupper($value);
487
+
488
  if(!in_array($ret, self::getRelationOptions())) {
489
  $err = sprintf(self::$binary_err, 'relation', 'AND', 'OR', $value);
490
  $ret = null;
507
  */
508
  private function sanitizeOperator($operator) {
509
  $ret = strtoupper($operator);
510
+
511
  if (!in_array($ret, self::getOperatorOptions())) {
512
  $this->errs[] = sprintf(self::$binary_err, $key, 'IN", "NOT IN", "OR', 'AND', $operator);
513
  $ret = null;
524
  public static function getOperatorOptions() {
525
  return array('IN', 'NOT IN', 'AND', 'OR');
526
  }
527
+
528
  /**
529
  * Gets all valid Documents based on the attributes passed by the user.
530
+ * NOTE: Keys in returned array are arbitrary and will vary. They should be ignored.
531
  * @return multitype:unknown Contains all documents matching the query.
532
  * @throws InvalidArgumentException Thrown when $this->errs is not empty.
533
  */
540
  'post_type' => $this->atts['post_type'],
541
  'post_mime_type' => $this->atts['mime_types']);
542
 
 
 
 
 
543
  $this->setTaxa($query);
544
 
545
  if(!empty($this->errs)) {
546
  throw new InvalidArgumentException();
547
  }
548
 
549
+ // NOTE: Derived from gallery shortcode
550
+ if (!empty($this->atts['include'])) {
551
+ $query['include'] = $this->atts['include'];
552
+ $attachments = get_posts($query);
553
+ } else {
554
+ // id == 0 => all attachments w/o a parent
555
+ // id == null => all matched attachments
556
+ $query['post_parent'] = $this->atts['id'];
557
+ if (!empty($exclude)) {
558
+ $query['exclude'] = $this->atts['exclude'];
559
+ }
560
+
561
+ $attachments = get_children($query);
562
+ }
563
+
564
+ return $attachments;
565
  }
566
 
567
  /**
576
  $operator = array();
577
  $suffix = array('relation', 'operator');
578
  $pattern = '/(.+)_(?:' . implode('|', $suffix) . ')$/i';
579
+
580
  // find any relations for taxa
581
  $iterable = $this->taxa;
582
  foreach ($iterable as $key => $value) {
588
  }
589
  }
590
  }
591
+
592
  // build tax query
593
  foreach ($this->taxa as $taxon => $terms) {
594
  $terms = $this->getTermIdsByNames($taxon, explode(',', $terms));
646
  private function getTermXByNames($x, $taxon, $term_names) {
647
  $ret = array();
648
  $valid = true;
649
+
650
  // taxons may optionally be prefixed by 'tax_' --
651
  // this is only useful when avoiding collisions with other attributes
652
  if (!taxonomy_exists($taxon)) {
653
+ $tmp = preg_replace('/^tax_(.*)/', '$1', $taxon, 1, $count);
654
  if ($count > 0 && taxonomy_exists($tmp)) {
655
  $taxon = $tmp;
656
  } else {
674
  return $ret;
675
  }
676
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
677
  /**
678
  * @param string $string To take second char from.
679
  * @return char Capitalized second char of given string.
681
  private static function secondCharToUpper($string) {
682
  return strtoupper($string[1]);
683
  }
684
+
685
  /**
686
  * Function returns false for positive ints, true otherwise.
687
  * @param string $var could be anything.
702
  if (is_null($val)) {
703
  return false;
704
  }
705
+
706
  if (is_bool($val)) {
707
  return $val;
708
  }
709
+
710
  if (is_int($val)) {
711
  if (1 === $val) {
712
  return true;
713
  }
714
+
715
  if (0 === $val) {
716
  return false;
717
  }
742
  * @return string HTML representing this Gallery.
743
  */
744
  public function __toString() {
745
+ static $instance = 0;
746
+ $instance++;
747
+
748
  static $find = null;
749
  if (is_null($find)) {
750
  $find = array('%class%', '%icons%');
751
  }
752
+
753
+ if (!empty($this->errs)) {
754
  return '<p>' . implode('</p><p>', $this->errs) . '</p>';
755
  }
756
 
757
+ if (empty($this->docs)) {
758
  return self::$no_docs;
759
  }
760
+
761
+ $selector = "document-gallery-$instance";
762
+ $template =
763
+ "<div id='$selector' class='%class%'>". PHP_EOL .
764
+ '%icons%' . PHP_EOL .
765
+ '</div>' . PHP_EOL;
766
 
767
  $icon_wrapper = apply_filters(
768
  'dg_row_template',
769
+ $template,
770
  $this->useDescriptions());
771
 
772
  $core = '';
782
  $core .= str_replace($find, $repl, $icon_wrapper);
783
  }
784
  } else {
785
+ global $dg_gallery_style;
786
+
787
+ $count = count($this->docs);
788
+ $cols = !is_null($this->atts['columns']) ? $this->atts['columns'] : $count;
789
+
790
+ if (apply_filters('dg_use_default_gallery_style', true )) {
791
+ $itemwidth = $cols > 0 ? (floor(100/$cols) - 1) : 100;
792
+ $core .= "<style type='text/css'>#$selector .document-icon{width:$itemwidth%}</style>";
793
+ }
794
+
795
+ for($i = 0; $i < $count; $i += $cols) {
796
  $repl[1] = '';
797
 
798
+ $min = min($i + $cols, $count);
799
  for($x = $i; $x < $min; $x++) {
800
  $repl[1] .= $this->docs[$x];
801
  }
inc/class-setup.php CHANGED
@@ -16,9 +16,10 @@ class DG_Setup {
16
  public static function getDefaultOptions($skeleton = false) {
17
  include_once DG_PATH . 'inc/class-thumber.php';
18
 
19
- $gs = null;
20
  if (!$skeleton) {
21
  $gs = DG_Thumber::getGhostscriptExecutable();
 
22
  }
23
 
24
  return array(
@@ -51,14 +52,8 @@ class DG_Setup {
51
  // include thumbnail of actual document in gallery display
52
  'fancy' => true,
53
 
54
- // comma-separated list of attachment ids
55
- 'ids' => false,
56
-
57
- // if true, all images attached to current page will be included also
58
- 'images' => false,
59
-
60
- // include just attached to the post using shortcode
61
- 'localpost' => true,
62
 
63
  // ascending/descending order for included documents
64
  'order' => 'ASC',
@@ -77,6 +72,9 @@ class DG_Setup {
77
 
78
  // the max number of thumbnails to return
79
  'limit' => -1,
 
 
 
80
  ),
81
  'css' => array(
82
  // plain text of CSS to be edited by user
@@ -85,9 +83,14 @@ class DG_Setup {
85
  // "minified" text to be rendered on pages
86
  'minified' => ''
87
  ),
88
-
89
- // current DG version
90
- 'version' => DG_VERSION,
 
 
 
 
 
91
 
92
  // whether to validate DG option structure on save
93
  'validation' => false,
@@ -97,6 +100,13 @@ class DG_Setup {
97
  );
98
  }
99
 
 
 
 
 
 
 
 
100
  /**
101
  * Runs every page load, updates as needed.
102
  */
@@ -104,7 +114,7 @@ class DG_Setup {
104
  global $dg_options;
105
 
106
  // do update
107
- if (!is_null($dg_options) && DG_VERSION !== $dg_options['version']) {
108
  $blogs = array(null);
109
 
110
  if (is_multisite()) {
@@ -130,9 +140,11 @@ class DG_Setup {
130
  // version-specific updates
131
  self::twoPointTwo($options);
132
  self::twoPointThree($options);
 
133
 
134
- // update plugin version
135
- $options['version'] = DG_VERSION;
 
136
 
137
  // remove previously-failed thumbs
138
  $thumbs = $options['thumber']['thumbs'];
@@ -155,7 +167,7 @@ class DG_Setup {
155
  * @param array $options The options to be modified.
156
  */
157
  private static function twoPointTwo(&$options) {
158
- if (version_compare($options['version'], '2.2', '<')) {
159
  $thumbs = array();
160
 
161
  // "created_timestamp" moving to just "timestamp"
@@ -192,7 +204,7 @@ class DG_Setup {
192
  * @param array $options The options to be modified.
193
  */
194
  private static function twoPointThree(&$options) {
195
- if (version_compare($options['version'], '2.3', '<')) {
196
  include_once DG_PATH . 'inc/class-thumber.php';
197
 
198
  unset($options['css']['last-modified']);
@@ -214,6 +226,42 @@ class DG_Setup {
214
  }
215
  }
216
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
217
  /**
218
  * Sets up Document Gallery on all blog(s) activated.
219
  * @param bool $networkwide Whether this is a network-wide update (multisite only).
@@ -292,6 +340,16 @@ class DG_Setup {
292
 
293
  DocumentGallery::deleteOptions($blog);
294
  }
 
 
 
 
 
 
 
 
 
 
295
 
296
  /**
297
  * Blocks instantiation. All functions are static.
16
  public static function getDefaultOptions($skeleton = false) {
17
  include_once DG_PATH . 'inc/class-thumber.php';
18
 
19
+ $gs = $donate_link = null;
20
  if (!$skeleton) {
21
  $gs = DG_Thumber::getGhostscriptExecutable();
22
+ $donate_link = self::getDonateLink();
23
  }
24
 
25
  return array(
52
  // include thumbnail of actual document in gallery display
53
  'fancy' => true,
54
 
55
+ // comma-delimited list of all mime types to be included
56
+ 'mime_types' => implode(',', self::getDefaultMimeTypes()),
 
 
 
 
 
 
57
 
58
  // ascending/descending order for included documents
59
  'order' => 'ASC',
72
 
73
  // the max number of thumbnails to return
74
  'limit' => -1,
75
+
76
+ // # of columns to be used in gallery
77
+ 'columns' => 4
78
  ),
79
  'css' => array(
80
  // plain text of CSS to be edited by user
83
  // "minified" text to be rendered on pages
84
  'minified' => ''
85
  ),
86
+
87
+ 'meta' => array(
88
+ // current DG version
89
+ 'version' => DG_VERSION,
90
+
91
+ // URL to donate to plugin development
92
+ 'donate_link' => $donate_link
93
+ ),
94
 
95
  // whether to validate DG option structure on save
96
  'validation' => false,
100
  );
101
  }
102
 
103
+ /**
104
+ * @return multitype:string The default MIME types to include in gallery.
105
+ */
106
+ public static function getDefaultMimeTypes() {
107
+ return array('application', 'video', 'text', 'audio');
108
+ }
109
+
110
  /**
111
  * Runs every page load, updates as needed.
112
  */
114
  global $dg_options;
115
 
116
  // do update
117
+ if (!is_null($dg_options) && (isset($options['version']) || DG_VERSION !== $dg_options['meta']['version'])) {
118
  $blogs = array(null);
119
 
120
  if (is_multisite()) {
140
  // version-specific updates
141
  self::twoPointTwo($options);
142
  self::twoPointThree($options);
143
+ self::threePointZeroBeta($options);
144
 
145
+ // update plugin meta data
146
+ $options['meta']['version'] = DG_VERSION;
147
+ $options['meta']['donate_link'] = self::getDonateLink();
148
 
149
  // remove previously-failed thumbs
150
  $thumbs = $options['thumber']['thumbs'];
167
  * @param array $options The options to be modified.
168
  */
169
  private static function twoPointTwo(&$options) {
170
+ if (isset($options['version']) && version_compare($options['version'], '2.2', '<')) {
171
  $thumbs = array();
172
 
173
  // "created_timestamp" moving to just "timestamp"
204
  * @param array $options The options to be modified.
205
  */
206
  private static function twoPointThree(&$options) {
207
+ if (isset($options['version']) && version_compare($options['version'], '2.3', '<')) {
208
  include_once DG_PATH . 'inc/class-thumber.php';
209
 
210
  unset($options['css']['last-modified']);
226
  }
227
  }
228
 
229
+ /**
230
+ * Creating new meta branch in options to store plugin meta information.
231
+ *
232
+ * "Localpost" no longer supported. Replaced by "id" attribute.
233
+ * "Images" no longer supported. Replaced by "mime_types" attribute.
234
+ * "Ids" still supported, but not stored in DB.
235
+ *
236
+ * Google thumber no longer supported.
237
+ *
238
+ * Added "columns" attribute.
239
+ * Added "mime_types" attribute.
240
+ *
241
+ * @param array $options The options to be modified.
242
+ */
243
+ private static function threePointZeroBeta(&$options) {
244
+ if (isset($options['version']) /*&& version_compare($options['version'], '3.0.0-beta', '<')*/) {
245
+ $options['meta'] = array('version' => $options['version']);
246
+ unset($options['version']);
247
+
248
+ $images = $options['gallery']['images'];
249
+
250
+ unset($options['gallery']['localpost']);
251
+ unset($options['gallery']['ids']);
252
+ unset($options['gallery']['images']);
253
+
254
+ unset($options['thumber']['active']['google']);
255
+
256
+ $defaults = self::getDefaultOptions();
257
+ $options['gallery']['columns'] = $defaults['gallery']['columns'];
258
+ $options['gallery']['mime_types'] = $defaults['gallery']['mime_types'];
259
+ if ($images) {
260
+ $options['gallery']['mime_types'] .= ',image';
261
+ }
262
+ }
263
+ }
264
+
265
  /**
266
  * Sets up Document Gallery on all blog(s) activated.
267
  * @param bool $networkwide Whether this is a network-wide update (multisite only).
340
 
341
  DocumentGallery::deleteOptions($blog);
342
  }
343
+
344
+ /**
345
+ * NOTE: This is expensive as is involves file I/O reading the README. Only use when
346
+ * the equivilent value in options array is not viable.
347
+ * @return string URL where users can donate to plugin.
348
+ */
349
+ private static function getDonateLink() {
350
+ $data = get_file_data(DG_PATH . 'README.txt', array('donate' => 'Donate link'));
351
+ return $data['donate'];
352
+ }
353
 
354
  /**
355
  * Blocks instantiation. All functions are static.
inc/class-thumber.php CHANGED
@@ -23,7 +23,29 @@ class DG_Thumber {
23
  }
24
 
25
  return array('av' => true, 'gs' => $gs_active,
26
- 'imagick' => $imagick_active, 'google' => false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  }
28
 
29
  /**
@@ -38,7 +60,7 @@ class DG_Thumber {
38
  if (is_null($start)) {
39
  $start = time();
40
  }
41
-
42
  $options = self::getOptions();
43
 
44
  // if we haven't saved a thumb, generate one
@@ -60,15 +82,10 @@ class DG_Thumber {
60
  'document-gallery'), $ID, is_array($thumber) ? implode('::',$thumber) : print_r($thumber, true));
61
  DG_Logger::writeLog(DG_LogLevel::Detail, $toLog);
62
  }
63
-
64
- if ($thumb = self::getThumbnailTemplate($thumber, $ID, $pg)) {
65
- $options['thumbs'][$ID] = array(
66
- 'timestamp' => time(),
67
- 'thumb_url' => $thumb['url'],
68
- 'thumb_path' => $thumb['path'],
69
- 'thumber' => $thumber
70
- );
71
- self::setOptions($options);
72
  break;
73
  }
74
  }
@@ -78,8 +95,7 @@ class DG_Thumber {
78
  $new = empty($options['thumbs'][$ID]);
79
  if ($new || empty($options['thumbs'][$ID]['thumber'])) {
80
  if ($new) {
81
- $options['thumbs'][$ID] = array('timestamp' => time());
82
- self::setOptions($options);
83
  }
84
 
85
  // fallback to default thumb for attachment type
@@ -229,10 +245,10 @@ class DG_Thumber {
229
  if (is_null($gs)) {
230
  $options = self::getOptions();
231
  $gs = $options['gs'];
232
-
233
  if (false !== $gs) {
234
- $gs = escapeshellarg($gs) . ' -sDEVICE=png16m -dFirstPage=%d'
235
- . ' -dLastPage=%d -dBATCH -dNOPAUSE -dPDFFitPage -sOutputFile=%s %s 2>&1';
236
  }
237
  }
238
 
@@ -243,7 +259,7 @@ class DG_Thumber {
243
  $doc_path = get_attached_file($ID);
244
  $temp_path = self::getTempFile();
245
 
246
- exec(sprintf($gs, $pg, $pg, $temp_path, $doc_path), $out, $ret);
247
 
248
  if ($ret != 0) {
249
  DG_Logger::writeLog(DG_LogLevel::Error, __('Ghostscript failed: ', 'document-gallery') . print_r($out));
@@ -311,140 +327,31 @@ class DG_Thumber {
311
  if (!empty($executable)) {
312
  return $executable;
313
  }
314
-
315
  // GoDaddy and others aren't setup in such a way that
316
  // the above works so we need to fallback to a direct
317
  // filesystem check in most common location
318
  exec('test -e /usr/bin/gs', $dummy, $ret);
319
  $executable = ($ret === 0) ? '/usr/bin/gs' : false;
320
-
321
  return $executable;
322
  }
323
 
324
  return $executable;
325
  }
326
-
327
  /**
328
  * @return bool Whether we can use the GS executable.
329
  */
330
  public static function isGhostscriptAvailable() {
331
  static $ret = null;
332
-
333
  if (is_null($ret)) {
334
  $options = self::getOptions();
335
  $ret = $options['gs'] && self::isExecAvailable();
336
  }
337
-
338
- return $ret;
339
- }
340
-
341
- /*==========================================================================
342
- * GOOGLE DRIVE VIEWER THUMBNAILS
343
- *=========================================================================*/
344
-
345
- /**
346
- * Get thumbnail for document with given ID from Google Drive Viewer.
347
- *
348
- * NOTE: Caller must verify that extension is supported.
349
- *
350
- * @param str $ID The attachment ID to retrieve thumbnail for.
351
- * @param int $pg The page number to make thumbnail of -- index starts at 1.
352
- * @return bool|str False on failure, URL to thumb on success.
353
- */
354
- public static function getGoogleDriveThumbnail($ID, $pg = 1) {
355
- // User agent for Lynx 2.8.7rel.2 -- Why? Because I can.
356
- static $user_agent = 'Lynx/2.8.7rel.2 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/1.0.0a';
357
- static $timeout = 60;
358
-
359
- $google_viewer = 'https://docs.google.com/viewer?url=%s&a=bi&pagenumber=%d&w=%d';
360
- $doc_url = wp_get_attachment_url($ID);
361
- if (!$doc_url) {
362
- return false;
363
- }
364
-
365
- $temp_file = self::getTempFile();
366
-
367
- // args for use in HTTP request
368
- $args = array(
369
- 'timeout' => $timeout, // these requests can take a LONG time
370
- 'redirection' => 5,
371
- 'httpversion' => '1.0',
372
- 'user-agent' => $user_agent,
373
- 'blocking' => true,
374
- 'headers' => array(),
375
- 'cookies' => array(),
376
- 'body' => null,
377
- 'compress' => false,
378
- 'decompress' => true,
379
- 'sslverify' => true,
380
- 'stream' => true,
381
- 'filename' => $temp_file
382
- );
383
-
384
- // prevent PHP timeout before HTTP completes
385
- @set_time_limit($timeout);
386
-
387
- $options = self::getOptions();
388
- $google_viewer = sprintf($google_viewer, urlencode($doc_url), (int)$pg, $options['width']);
389
-
390
- // get thumbnail from Google Drive Viewer & check for error on return
391
- $response = wp_remote_get($google_viewer, $args);
392
-
393
- if (is_wp_error($response) || !preg_match('/[23][0-9]{2}/', $response['response']['code'])) {
394
- DG_Logger::writeLog(DG_LogLevel::Warning, __('Failed to retrieve thumbnail from Google: ', 'document-gallery') .
395
- (is_wp_error($response)
396
- ? $response->get_error_message()
397
- : $response['response']['message']));
398
-
399
- @unlink($temp_file);
400
- return false;
401
- }
402
-
403
- return $temp_file;
404
- }
405
-
406
- /**
407
- * @return array All extensions supported by Google Drive Viewer.
408
- */
409
- private static function getGoogleDriveExts() {
410
- return array(
411
- 'tiff', 'bmp', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx',
412
- 'pdf', 'pages', 'ai', 'psd', 'dxf', 'svg', 'eps', 'ps', 'ttf'
413
- );
414
- }
415
 
416
- /**
417
- * @return bool Whether Google Drive can access files on this system.
418
- */
419
- public static function isGoogleDriveAvailable() {
420
- static $available = null;
421
-
422
- if (is_null($available)) {
423
- // to check if we're visible externally, retrieve image for file we know exists.
424
- $user_agent = 'Lynx/2.8.7rel.2 libwww-FM/2.14 SSL-MM/1.4.1 OpenSSL/1.0.0a';
425
- $google_viewer = 'https://docs.google.com/viewer?url=%s&a=bi&pagenumber=1&w=1';
426
- $google_viewer = sprintf($google_viewer, urlencode(DG_URL . 'LICENSE.txt'));
427
-
428
- // args for use in HTTP request
429
- $args = array(
430
- 'redirection' => 5,
431
- 'httpversion' => '1.0',
432
- 'user-agent' => $user_agent,
433
- 'blocking' => true,
434
- 'headers' => array(),
435
- 'cookies' => array(),
436
- 'body' => null,
437
- 'compress' => false,
438
- 'decompress' => true,
439
- 'sslverify' => true
440
- );
441
-
442
- $response = wp_remote_get($google_viewer, $args);
443
-
444
- $available = (!is_wp_error($response) && $response['response']['code'] != 404);
445
- }
446
-
447
- return $available;
448
  }
449
 
450
  /*==========================================================================
@@ -646,25 +553,19 @@ class DG_Thumber {
646
  }
647
  }
648
 
649
- // Google Drive Viewer
650
- if ($active['google']) {
651
- $exts = implode('|', self::getGoogleDriveExts());
652
- $thumbers[$exts] = array(__CLASS__, 'getGoogleDriveThumbnail');
653
- }
654
-
655
  // allow users to filter thumbers used
656
  $thumbers = apply_filters('dg_thumbers', $thumbers);
657
-
658
  // strip out anything that can't be called
659
  $thumbers = array_filter($thumbers, 'is_callable');
660
-
661
  // log which thumbers are being used
662
  if (DG_Logger::logEnabled()) {
663
  if (count($thumbers) > 0) {
664
  $entry = __('Thumbnail Generators: ', 'document-gallery');
665
  foreach ($thumbers as $k => $v) {
666
  $thumber = is_array($v) ? implode('::', $v) : print_r($v, true);
667
-
668
  // TODO: The following works for all internal regexes, but may have unpredictable
669
  // results if developer adds additional thumbnail generators using different regexes
670
  $filetypes = str_replace('|', ', ', $k);
@@ -683,15 +584,22 @@ class DG_Thumber {
683
 
684
  /**
685
  * Template that handles generating a thumbnail.
 
 
 
686
  *
687
  * @param callable $generator Takes ID and pg and returns path to temp file or false.
688
  * @param int $ID ID for the attachment that we need a thumbnail for.
689
- * @param int $pg Page number of the attachment to get a thumbnail for.
690
- * @return bool|array Array containing 'url' and 'path' values or false.
691
  */
692
- public static function getThumbnailTemplate($generator, $ID, $pg = 1) {
 
 
 
 
693
  // delegate thumbnail generation to $generator
694
- if (false === ($temp_path = call_user_func($generator, $ID, $pg))) {
695
  return false;
696
  }
697
 
@@ -736,9 +644,16 @@ class DG_Thumber {
736
  @unlink($temp_path);
737
  self::deleteThumbMeta($ID);
738
 
739
- return array(
740
- 'path' => $thumb_path,
741
- 'url' => preg_replace('#'.preg_quote($basename).'$#', $thumb_name, $doc_url));
 
 
 
 
 
 
 
742
  }
743
 
744
  /**
@@ -755,7 +670,7 @@ class DG_Thumber {
755
  $tmp = untrailingslashit(get_temp_dir());
756
  }
757
 
758
- return $tmp . DIRECTORY_SEPARATOR . wp_unique_filename($tmp, "$base.$ext");
759
  }
760
 
761
  /**
@@ -791,7 +706,7 @@ class DG_Thumber {
791
  $modified = true;
792
  }
793
  }
794
-
795
  if ($modified) { self::setOptions($options); }
796
  }
797
 
@@ -829,13 +744,18 @@ class DG_Thumber {
829
  * @param str $filename Name of the file to get extension from.
830
  * @return str|bool Returns the file extension on success, false on failure.
831
  */
832
- private static function getExt($filename) {
833
- foreach (array_keys(wp_get_mime_types()) as $ext_preg) {
834
- $ext_preg = '!\.(' . $ext_preg . ')$!i';
835
- if (preg_match($ext_preg, $filename, $ext_matches)) {
836
- return $ext_matches[1];
 
 
837
  }
838
  }
 
 
 
839
 
840
  return false;
841
  }
23
  }
24
 
25
  return array('av' => true, 'gs' => $gs_active,
26
+ 'imagick' => $imagick_active);
27
+ }
28
+
29
+ /**
30
+ * Sets the thumbnail for the given attachment ID.
31
+ *
32
+ * @param int $ID Document ID.
33
+ * @param string $path System path to thumbnail.
34
+ * @return bool Whether set was successful.
35
+ */
36
+ public static function setThumbnail($ID, $path, $generator = 'unknown') {
37
+ return self::thumbnailGenerationHarness($generator, $ID, $path);
38
+ }
39
+
40
+ /**
41
+ * Sets the thumbnail for the given attachment ID to a failed state.
42
+ *
43
+ * @param int $ID Document ID.
44
+ */
45
+ public static function setThumbnailFailed($ID) {
46
+ $options = self::getOptions();
47
+ $options['thumbs'][$ID] = array('timestamp' => time());
48
+ self::setOptions($options);
49
  }
50
 
51
  /**
60
  if (is_null($start)) {
61
  $start = time();
62
  }
63
+
64
  $options = self::getOptions();
65
 
66
  // if we haven't saved a thumb, generate one
82
  'document-gallery'), $ID, is_array($thumber) ? implode('::',$thumber) : print_r($thumber, true));
83
  DG_Logger::writeLog(DG_LogLevel::Detail, $toLog);
84
  }
85
+
86
+ if (self::thumbnailGenerationHarness($thumber, $ID, $pg)) {
87
+ // harness updates options so we need a new copy
88
+ $options = self::getOptions();
 
 
 
 
 
89
  break;
90
  }
91
  }
95
  $new = empty($options['thumbs'][$ID]);
96
  if ($new || empty($options['thumbs'][$ID]['thumber'])) {
97
  if ($new) {
98
+ self::setThumbnailFailed($ID);
 
99
  }
100
 
101
  // fallback to default thumb for attachment type
245
  if (is_null($gs)) {
246
  $options = self::getOptions();
247
  $gs = $options['gs'];
248
+
249
  if (false !== $gs) {
250
+ $gs = escapeshellarg($gs) . ' -sDEVICE=png16m -dFirstPage=%1$d'
251
+ . ' -dLastPage=%1$d -dBATCH -dNOPAUSE -dPDFFitPage -sOutputFile=%2$s %3$s 2>&1';
252
  }
253
  }
254
 
259
  $doc_path = get_attached_file($ID);
260
  $temp_path = self::getTempFile();
261
 
262
+ exec(sprintf($gs, $pg, $temp_path, $doc_path), $out, $ret);
263
 
264
  if ($ret != 0) {
265
  DG_Logger::writeLog(DG_LogLevel::Error, __('Ghostscript failed: ', 'document-gallery') . print_r($out));
327
  if (!empty($executable)) {
328
  return $executable;
329
  }
330
+
331
  // GoDaddy and others aren't setup in such a way that
332
  // the above works so we need to fallback to a direct
333
  // filesystem check in most common location
334
  exec('test -e /usr/bin/gs', $dummy, $ret);
335
  $executable = ($ret === 0) ? '/usr/bin/gs' : false;
336
+
337
  return $executable;
338
  }
339
 
340
  return $executable;
341
  }
342
+
343
  /**
344
  * @return bool Whether we can use the GS executable.
345
  */
346
  public static function isGhostscriptAvailable() {
347
  static $ret = null;
348
+
349
  if (is_null($ret)) {
350
  $options = self::getOptions();
351
  $ret = $options['gs'] && self::isExecAvailable();
352
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
+ return $ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
  }
356
 
357
  /*==========================================================================
553
  }
554
  }
555
 
 
 
 
 
 
 
556
  // allow users to filter thumbers used
557
  $thumbers = apply_filters('dg_thumbers', $thumbers);
558
+
559
  // strip out anything that can't be called
560
  $thumbers = array_filter($thumbers, 'is_callable');
561
+
562
  // log which thumbers are being used
563
  if (DG_Logger::logEnabled()) {
564
  if (count($thumbers) > 0) {
565
  $entry = __('Thumbnail Generators: ', 'document-gallery');
566
  foreach ($thumbers as $k => $v) {
567
  $thumber = is_array($v) ? implode('::', $v) : print_r($v, true);
568
+
569
  // TODO: The following works for all internal regexes, but may have unpredictable
570
  // results if developer adds additional thumbnail generators using different regexes
571
  $filetypes = str_replace('|', ', ', $k);
584
 
585
  /**
586
  * Template that handles generating a thumbnail.
587
+ *
588
+ * If image has already been generated through other means, $pg may be set to the system path where the
589
+ * thumbnail is located. In this case, $generator will not be invoked, but *will* be kept for historical purposes.
590
  *
591
  * @param callable $generator Takes ID and pg and returns path to temp file or false.
592
  * @param int $ID ID for the attachment that we need a thumbnail for.
593
+ * @param int|str $pg Page number of the attachment to get a thumbnail for or the system path to the image to be used.
594
+ * @return bool Whether generation was successful.
595
  */
596
+ private static function thumbnailGenerationHarness($generator, $ID, $pg = 1) {
597
+ // handle system page in $pg variable
598
+ if (is_string($pg) && !is_numeric($pg)) {
599
+ $temp_path = $pg;
600
+ }
601
  // delegate thumbnail generation to $generator
602
+ elseif (false === ($temp_path = call_user_func($generator, $ID, $pg))) {
603
  return false;
604
  }
605
 
644
  @unlink($temp_path);
645
  self::deleteThumbMeta($ID);
646
 
647
+ // store new thumbnail in DG options
648
+ $options['thumbs'][$ID] = array(
649
+ 'timestamp' => time(),
650
+ 'thumb_url' => preg_replace('#'.preg_quote($basename).'$#', $thumb_name, $doc_url),
651
+ 'thumb_path' => $thumb_path,
652
+ 'thumber' => $generator
653
+ );
654
+ self::setOptions($options);
655
+
656
+ return true;
657
  }
658
 
659
  /**
670
  $tmp = untrailingslashit(get_temp_dir());
671
  }
672
 
673
+ return $tmp . DIRECTORY_SEPARATOR . wp_unique_filename($tmp, $base.'.'.$ext);
674
  }
675
 
676
  /**
706
  $modified = true;
707
  }
708
  }
709
+
710
  if ($modified) { self::setOptions($options); }
711
  }
712
 
744
  * @param str $filename Name of the file to get extension from.
745
  * @return str|bool Returns the file extension on success, false on failure.
746
  */
747
+ private static function getExt($filename, $img = false) {
748
+ $options = $img ? array('jpg', 'jpeg', 'gif', 'png', 'bmp') : array_keys(wp_get_mime_types());
749
+ if ($ext = pathinfo($filename, PATHINFO_EXTENSION)) {
750
+ $res = preg_grep('/^(?:.*\|)?' . $ext . '(?:\|.*)?$/i', $options);
751
+ $res = reset($res);
752
+ if ($res!== false) {
753
+ return $ext;
754
  }
755
  }
756
+ elseif ( ($info = getimagesize($filename)) && ($ext = image_type_to_extension($info[2], false)) ) {
757
+ return $ext;
758
+ }
759
 
760
  return false;
761
  }
inc/class-util.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined('WPINC') OR exit;
3
+
4
+ /**
5
+ * General utility function for Document Gallery.
6
+ *
7
+ * @author drossiter
8
+ */
9
+ class DG_Util {
10
+ /**
11
+ * @var callable Either native JSON encode or custom JSON encode if needed.
12
+ */
13
+ private static $nativeJsonEncode;
14
+
15
+ /**
16
+ * Wraps JSON encoding functionality, utilizing native functions if available.
17
+ *
18
+ * @param unknown $decoded Value to be encoded.
19
+ * @return string The JSON string.
20
+ */
21
+ public static function jsonEncode($decoded) {
22
+ if (!isset(self::$nativeJsonEncode)) {
23
+ self::$nativeJsonEncode = function_exists('json_encode');
24
+ }
25
+
26
+ // do encoding
27
+ return self::$nativeJsonEncode ? json_encode($decoded) : self::_jsonEncode($decoded);
28
+ }
29
+
30
+ /**
31
+ * Home-made JSON encode to replace mssing json_encode when needed.
32
+ *
33
+ * @param unknown $decoded Value to be encoded.
34
+ * @return string The JSON string.
35
+ */
36
+ private static function _jsonEncode($decoded) {
37
+ if (self::isJsonObj($decoded)) {
38
+ $ret = '';
39
+ $first = true;
40
+ foreach ($decoded as $k => $v) {
41
+ if (!$first) $ret .= ',';
42
+ $ret .= "\"$k\":" . self::_jsonEncode($v);
43
+ $first = false;
44
+ }
45
+
46
+ return "\{$ret\}";
47
+ } elseif (is_array($decoded)) {
48
+ return '[' . implode(',', array_map(array(__CLASS__, __FUNCTION__), $decoded)) . ']';
49
+ } elseif (is_bool($decoded)) {
50
+ static $boolMap = array('false', 'true');
51
+ return $boolMap[(int)$decoded];
52
+ } elseif (is_string($decoded)) {
53
+ return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $decoded) . '"';
54
+ }
55
+
56
+ return (string)$decoded;
57
+ }
58
+
59
+ /**
60
+ * Returns true for PHP objects and associative arrays.
61
+ *
62
+ * @param unknown $decoded Value to be checked.
63
+ * @return bool Whether passed value should be encoded as a JSON object.
64
+ */
65
+ private static function isJsonObj($decoded) {
66
+ $ret = is_object($decoded);
67
+
68
+ if (!$ret && is_array($decoded)) {
69
+ $next = 0;
70
+ foreach (array_keys($decoded) as $k) {
71
+ if ($next++ !== $k) {
72
+ $ret = true;
73
+ break;
74
+ }
75
+ }
76
+ }
77
+
78
+ return $ret;
79
+ }
80
+ }
log/index.php ADDED
@@ -0,0 +1,2 @@
 
 
1
+ <?php // silence is golden
2
+ die();