Document Gallery - Version 4.0

Version Description

  • Enhancement: The WordPress visual editor now displays a full gallery preview.
  • Enhancement: You can now paginate your galleries. This is especially useful in large multi-hundred item galleries. To enable pagination in your galleries, simply use limit=##.
  • Enhancement: All CSS & JavaScript is now served minified to ensure the fastest possible load time for your site.
  • Enhancement: When using taxonomies to generate your galleries (eg: media categories) you can now use term slug instead of the name. Thanks andremalenfant for suggesting this!
  • Enhancement: The structure of the gallery output has been cleaned up, making it easier to style if you chose to use custom CSS. NOTE: This modified structure may break existing custom CSS or PHP filtering, so be sure to check this if you're using either of those features.
  • Bug Fix: The storage of the DG thumbnail cache was very broken. Due to how the cache was originally designed, it ran into issues at large scale and on busy sites, which resulted in difficult to track bugs. The entire storage mechanism for the cache has been rewritten from the ground up to address this issue, which will result in faster gallery generation and more reliable performance.
  • Bug Fix: In the thumbnail management tab of the DG settings, sorting by title was broken. This has been fixed.
  • Bug Fix: Limit was not working in cases where the ids or include attribute were present. This has been fixed.
  • Tested Up To: Document Gallery has been tested in WP 4.4 beta.
Download this release

Release Info

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

Code changes from version 3.5.4 to 4.0

README.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: dan.rossiter, demur
3
  Tags: attachments, thumbnail, documents, gallery, MS office, pdf
4
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=EE5LWRLG933EN&lc=US&item_name=Document%20Gallery%20Plugin&item_number=document%2dgallery&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted
5
  Requires at least: 4.1
6
- Tested up to: 4.3
7
- Stable tag: 3.5.4
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -186,8 +186,8 @@ documents are displayed in ascending or descending order.
186
  The relation option should only be used when also using the *category or custom
187
  taxonomy* option (see above).
188
 
189
- When using multiple taxa this option allows you to decide whether the attachments
190
- returned must match all of the different taxa specified (AND) or a minimum of one
191
  taxa match (OR).
192
 
193
  *NOTE: This has no bearing on the relationship between different terms for a single
@@ -239,10 +239,6 @@ of a gallery: `dg_gallery_template`, `dg_row_template`, and `dg_icon_template`.
239
  These filtered templates are used when dynamically generating output for each
240
  gallery.
241
 
242
- *NOTE: The `dg_doc_icon` has been deprecated with the release and is
243
- scheduled to be removed in a future release. If you are using this
244
- filter, you are encouraged to replace its usages with `dg_icon_template`.*
245
-
246
  Each of the following filters provides an bool argument which indicates
247
  whither the gallery being generated will display descriptions, which
248
  allows you to handle galleries with and without descriptions differently.
@@ -253,10 +249,12 @@ content prior to or following your document galleries. The filter
253
  exposes 2 special tags which are replaced during gallery generation
254
  with data specific to that gallery. The tag is described below:
255
 
256
- * **%id%**: This tag is replaced by the document gallery HTML id attribute.
 
 
257
  * **%rows%**: This tag is replaced by all of the document gallery rows.
258
- Everything before this string will be rendered before the gallery and
259
- everything after this string will be rendered following the gallery.
260
 
261
 
262
  If you wish to modify how gallery rows are generated, `dg_row_template`,
@@ -299,7 +297,7 @@ for a given attachment.
299
 
300
  The value being filtered is an associative array with keys equal to a regular
301
  expression matching all file extensions supported by the generator and values
302
- equal to [callables](http://www.php.net/manual/en/language.types.callable.php)
303
  which take an **attachment ID** and a **file page number** as arguments.
304
 
305
  The callable given should return false if thumbnail generation fails or
@@ -378,6 +376,12 @@ CSS being loaded by returning false in `dg_use_default_gallery_style` filter, li
378
  == Frequently Asked Questions ==
379
 
380
 
 
 
 
 
 
 
381
  = Q: Ghostscript is installed on my server, but it's not working! =
382
 
383
  A: Document Gallery does a pretty good job of detecting where Ghostscript is installed,
@@ -439,6 +443,24 @@ To see a list of features planned for the future as well as to propose your own
439
  ideas for future Document Gallery development, take a look at our
440
  [issue tracker](https://github.com/thenadz/document-gallery/issues).
441
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
442
  = 3.5.4 =
443
  * **Bug Fix:** There were issues in the structure of HTML generated for galleries. This resulted in issues
444
  with icon generation.
3
  Tags: attachments, thumbnail, documents, gallery, MS office, pdf
4
  Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=EE5LWRLG933EN&lc=US&item_name=Document%20Gallery%20Plugin&item_number=document%2dgallery&currency_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted
5
  Requires at least: 4.1
6
+ Tested up to: 4.4
7
+ Stable tag: 4.0
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
186
  The relation option should only be used when also using the *category or custom
187
  taxonomy* option (see above).
188
 
189
+ When using multiple taxa this option allows you to decide whether the attachments
190
+ returned must match all of the different taxa specified (AND) or a minimum of one
191
  taxa match (OR).
192
 
193
  *NOTE: This has no bearing on the relationship between different terms for a single
239
  These filtered templates are used when dynamically generating output for each
240
  gallery.
241
 
 
 
 
 
242
  Each of the following filters provides an bool argument which indicates
243
  whither the gallery being generated will display descriptions, which
244
  allows you to handle galleries with and without descriptions differently.
249
  exposes 2 special tags which are replaced during gallery generation
250
  with data specific to that gallery. The tag is described below:
251
 
252
+ * **%id%**: The id attribute value for this gallery.
253
+ * **%class%**: The class attribute value for this gallery.
254
+ * **%data%**: The one ore more data-* attributes for the gallery, which are necessary for client-side operations.
255
  * **%rows%**: This tag is replaced by all of the document gallery rows.
256
+ Everything before this string will be rendered before the gallery and
257
+ everything after this string will be rendered following the gallery.
258
 
259
 
260
  If you wish to modify how gallery rows are generated, `dg_row_template`,
297
 
298
  The value being filtered is an associative array with keys equal to a regular
299
  expression matching all file extensions supported by the generator and values
300
+ equal to [callables](http://www.php.net/manual/en/language.types.callable.php)
301
  which take an **attachment ID** and a **file page number** as arguments.
302
 
303
  The callable given should return false if thumbnail generation fails or
376
  == Frequently Asked Questions ==
377
 
378
 
379
+ = Q: I'm using taxonomies, but nothing is showing up in my gallery =
380
+
381
+ A: Remember that Document Gallery defaults to retrieving just attachments for the current post/page.
382
+ If you want a broader scope of attachments, you'll also need tell Document Gallery to search everywhere
383
+ like so: `[dg id=-1 category="My Awesome Category"]`.
384
+
385
  = Q: Ghostscript is installed on my server, but it's not working! =
386
 
387
  A: Document Gallery does a pretty good job of detecting where Ghostscript is installed,
443
  ideas for future Document Gallery development, take a look at our
444
  [issue tracker](https://github.com/thenadz/document-gallery/issues).
445
 
446
+ = 4.0 =
447
+ * **Enhancement:** The WordPress visual editor now displays a full gallery preview.
448
+ * **Enhancement:** You can now paginate your galleries. This is especially useful in large multi-hundred item galleries.
449
+ To enable pagination in your galleries, simply use `limit=##`.
450
+ * **Enhancement:** All CSS & JavaScript is now served minified to ensure the fastest possible load time for your site.
451
+ * **Enhancement:** When using taxonomies to generate your galleries (eg: media categories) you can now use term slug
452
+ instead of the name. *Thanks andremalenfant for suggesting this!*
453
+ * **Enhancement:** The structure of the gallery output has been cleaned up, making it easier to style if you chose to
454
+ use custom CSS. *NOTE: This modified structure may break existing custom CSS or PHP filtering, so be sure to check
455
+ this if you're using either of those features.*
456
+ * **Bug Fix:** The storage of the DG thumbnail cache was very broken. Due to how the cache was originally designed, it
457
+ ran into issues at large scale and on busy sites, which resulted in difficult to track bugs. The entire storage
458
+ mechanism for the cache has been rewritten from the ground up to address this issue, which will result in faster
459
+ gallery generation and more reliable performance.
460
+ * **Bug Fix:** In the thumbnail management tab of the DG settings, sorting by title was broken. This has been fixed.
461
+ * **Bug Fix:** `Limit` was not working in cases where the `ids` or `include` attribute were present. This has been fixed.
462
+ * **Tested Up To:** Document Gallery has been tested in WP 4.4 beta.
463
+
464
  = 3.5.4 =
465
  * **Bug Fix:** There were issues in the structure of HTML generated for galleries. This resulted in issues
466
  with icon generation.
admin/class-admin.php CHANGED
@@ -72,6 +72,8 @@ class DG_Admin {
72
 
73
  /**
74
  * Adds settings link to main plugin view.
 
 
75
  */
76
  public static function addSettingsLink( $links ) {
77
  $settings = '<a href="options-general.php?page=' . DG_OPTION_NAME . '">' .
@@ -83,6 +85,9 @@ class DG_Admin {
83
 
84
  /**
85
  * Adds donate link to main plugin view.
 
 
 
86
  */
87
  public static function addDonateLink( $links, $file ) {
88
  if ( $file === DG_BASENAME ) {
@@ -110,23 +115,13 @@ class DG_Admin {
110
 
111
  /**
112
  * Enqueues styles and scripts for the admin settings page.
 
113
  */
114
  public static function enqueueScriptsAndStyles( $hook ) {
115
  if ( in_array( $hook, array( DG_Admin::$hook, 'post.php', 'post-new.php' ), true ) ) {
116
  // Settings Page
117
  DG_Util::enqueueAsset( 'document-gallery-admin', 'assets/css/admin.css' );
118
 
119
- // gracefully degrade for older WP versions
120
- if ( version_compare( get_bloginfo( 'version' ), '3.8', '<' ) ) { ?>
121
- <style type="text/css">
122
- .dashicons, .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before,
123
- .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after,
124
- #ThumbsTable .title a:after, #LogTable>tbody a:after {
125
- display: none !important;
126
- }
127
- </style>
128
- <?php }
129
-
130
  DG_Util::enqueueAsset( 'document-gallery-admin', 'assets/js/admin.js', array( 'jquery' ) );
131
  wp_localize_script( 'document-gallery-admin', 'dg_admin_vars', array( 'upload_limit' => wp_max_upload_size() ) );
132
  if ( $hook !== self::$hook ) { //if $hook is 'post.php' or 'post-new.php'
@@ -134,22 +129,58 @@ class DG_Admin {
134
 
135
  // Media Manager
136
  global $dg_options;
137
- DG_Util::enqueueAsset( 'document-gallery-media-manager', 'assets/js/media_manager.js', array( 'media-views' ) );
138
- wp_localize_script( 'document-gallery-media-manager', 'DGl10n', array(
139
- 'documentGalleryMenuTitle' => __( 'Create Document Gallery', 'document-gallery' ),
140
- 'documentGalleryButton' => __( 'Create a new Document Gallery', 'document-gallery' ),
141
- 'cancelDocumentGalleryTitle' => '&#8592; ' . __( 'Cancel Document Gallery', 'document-gallery' ),
142
- 'updateDocumentGallery' => __( 'Update Document Gallery', 'document-gallery' ),
143
- 'insertDocumentGallery' => __( 'Insert Document Gallery', 'document-gallery' ),
144
- 'addToDocumentGallery' => __( 'Add to Document Gallery', 'document-gallery' ),
145
- 'addToDocumentGalleryTitle' => __( 'Add to Document Gallery', 'document-gallery' ),
146
- 'editDocumentGalleryTitle' => __( 'Edit Document Gallery', 'document-gallery' )
 
 
 
147
  ) );
148
- wp_localize_script( 'document-gallery-media-manager', 'documentGalleryDefaults', $dg_options['gallery'] );
 
 
 
 
149
  }
150
  }
151
  }
152
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
153
  /**
154
  * Load Document Gallery Custom templates.
155
  */
@@ -181,6 +212,7 @@ class DG_Admin {
181
  global $dg_options;
182
 
183
  include_once DG_PATH . 'inc/class-gallery.php';
 
184
  include_once DG_PATH . 'inc/class-thumber.php';
185
 
186
  $defaults = $dg_options['gallery'];
@@ -207,7 +239,7 @@ class DG_Admin {
207
  'name' => 'gallery_defaults][attachment_pg',
208
  'value' => esc_attr( $defaults['attachment_pg'] ),
209
  'option_name' => DG_OPTION_NAME,
210
- 'description' => __( 'Link to attachment page rather than to file', 'document-gallery' )
211
  ) );
212
 
213
  add_settings_field(
@@ -232,7 +264,7 @@ class DG_Admin {
232
  'name' => 'gallery_defaults][descriptions',
233
  'value' => esc_attr( $defaults['descriptions'] ),
234
  'option_name' => DG_OPTION_NAME,
235
- 'description' => __( 'Include document descriptions', 'document-gallery' )
236
  ) );
237
 
238
  add_settings_field(
@@ -244,7 +276,7 @@ class DG_Admin {
244
  'name' => 'gallery_defaults][fancy',
245
  'value' => esc_attr( $defaults['fancy'] ),
246
  'option_name' => DG_OPTION_NAME,
247
- 'description' => __( 'Use auto-generated document thumbnails', 'document-gallery' )
248
  ) );
249
 
250
  add_settings_field(
@@ -255,9 +287,9 @@ class DG_Admin {
255
  'label_for' => 'label_gallery_defaults_order',
256
  'name' => 'gallery_defaults][order',
257
  'value' => esc_attr( $defaults['order'] ),
258
- 'options' => DG_Gallery::getOrderOptions(),
259
  'option_name' => DG_OPTION_NAME,
260
- 'description' => __( 'Ascending or descending sorting of documents', 'document-gallery' )
261
  ) );
262
 
263
  add_settings_field(
@@ -268,9 +300,9 @@ class DG_Admin {
268
  'label_for' => 'label_gallery_defaults_orderby',
269
  'name' => 'gallery_defaults][orderby',
270
  'value' => esc_attr( $defaults['orderby'] ),
271
- 'options' => DG_Gallery::getOrderbyOptions(),
272
  'option_name' => DG_OPTION_NAME,
273
- 'description' => __( 'Which field to order documents by', 'document-gallery' )
274
  ) );
275
 
276
  add_settings_field(
@@ -281,9 +313,9 @@ class DG_Admin {
281
  'label_for' => 'label_gallery_defaults_relation',
282
  'name' => 'gallery_defaults][relation',
283
  'value' => esc_attr( $defaults['relation'] ),
284
- 'options' => DG_Gallery::getRelationOptions(),
285
  'option_name' => DG_OPTION_NAME,
286
- 'description' => __( 'Whether matched documents must have all taxa_names (AND) or at least one (OR)', 'document-gallery' )
287
  ) );
288
 
289
  add_settings_field(
@@ -321,7 +353,19 @@ class DG_Admin {
321
  'name' => 'gallery_defaults][new_window',
322
  'value' => esc_attr( $defaults['new_window'] ),
323
  'option_name' => DG_OPTION_NAME,
324
- 'description' => __( 'Open thumbnail links in new window', 'document-gallery' ) . '.'
 
 
 
 
 
 
 
 
 
 
 
 
325
  ) );
326
 
327
  add_settings_field(
@@ -332,7 +376,7 @@ class DG_Admin {
332
  'label_for' => 'label_gallery_defaults_post_status',
333
  'name' => 'gallery_defaults][post_status',
334
  'value' => esc_attr( $defaults['post_status'] ),
335
- 'options' => DG_Gallery::getPostStatuses(),
336
  'option_name' => DG_OPTION_NAME,
337
  'description' => __( 'Which post status to look for when querying documents.', 'document-gallery' )
338
  ) );
@@ -345,7 +389,7 @@ class DG_Admin {
345
  'label_for' => 'label_gallery_defaults_post_type',
346
  'name' => 'gallery_defaults][post_type',
347
  'value' => esc_attr( $defaults['post_type'] ),
348
- 'options' => DG_Gallery::getPostTypes(),
349
  'option_name' => DG_OPTION_NAME,
350
  'description' => __( 'Which post type to look for when querying documents.', 'document-gallery' )
351
  ) );
@@ -506,7 +550,7 @@ class DG_Admin {
506
  global $dg_options;
507
  return $dg_options;
508
  } else {
509
- if ( array_key_exists( 'ajax', $values ) ) {
510
  unset( $values['ajax'] );
511
  define( 'DOING_AJAX', true );
512
  }
@@ -571,16 +615,8 @@ class DG_Admin {
571
 
572
  // delete thumb cache to force regeneration if max dimensions changed
573
  if ( $ret['thumber']['width'] !== $dg_options['thumber']['width'] ||
574
- $ret['thumber']['height'] !== $dg_options['thumber']['height']
575
- ) {
576
- foreach ( $ret['thumber']['thumbs'] as $v ) {
577
- if ( isset( $v['thumber'] ) ) {
578
- @unlink( $v['thumb_path'] );
579
- }
580
- }
581
-
582
- $ret['thumber']['thumbs'] = array();
583
- $thumbs_cleared = true;
584
  }
585
 
586
  // handle setting the active thumbers
@@ -590,16 +626,7 @@ class DG_Admin {
590
 
591
  // if new thumbers available, clear failed thumbnails for retry
592
  if ( ! $thumbs_cleared ) {
593
- foreach ( $dg_options['thumber']['active'] as $k => $v ) {
594
- if ( ! $v && $ret['thumber']['active'][ $k ] ) {
595
- foreach ( $dg_options['thumber']['thumbs'] as $k2 => $v2 ) {
596
- if ( empty( $v['thumber'] ) ) {
597
- unset( $ret['thumber']['thumbs'][ $k2 ] );
598
- }
599
- }
600
- break;
601
- }
602
- }
603
  }
604
 
605
  // handle modified CSS
@@ -631,16 +658,8 @@ class DG_Admin {
631
  // Thumbnail(s) cleanup;
632
  // cleanup value is a marker
633
  if ( isset( $values['cleanup'] ) && isset( $values['ids'] ) ) {
634
- $deleted = array_values( array_intersect( array_keys( $dg_options['thumber']['thumbs'] ), $values['ids'] ) );
635
-
636
- foreach ( $deleted as $k ) {
637
- if ( isset( $ret['thumber']['thumbs'][ $k ]['thumber'] ) ) {
638
- @unlink( $ret['thumber']['thumbs'][ $k ]['thumb_path'] );
639
- }
640
-
641
- unset( $ret['thumber']['thumbs'][ $k ] );
642
- }
643
-
644
  $responseArr['result'] = true;
645
  $responseArr['deleted'] = $deleted;
646
  }
@@ -671,17 +690,12 @@ class DG_Admin {
671
 
672
  // Thumbnail file manual refresh (one at a time)
673
  // upload value is a marker
674
- elseif ( isset( $values['upload'] ) && isset( $_FILES['file'] ) && isset( $ret['thumber']['thumbs'][ $ID ] ) ) {
675
- $old_path = DG_Util::hasThumb( $ID ) ? $ret['thumber']['thumbs'][ $ID ]['thumb_path'] : null;
676
  $uploaded_filename = self::validateUploadedFile();
677
- if ( $uploaded_filename && DG_Thumber::setThumbnail( $ID, $uploaded_filename ) ) {
678
- if ( ! is_null( $old_path ) && $dg_options['thumber']['thumbs'][ $ID ]['thumb_path'] !== $old_path ) {
679
- @unlink( $old_path );
680
- }
681
- $responseArr['result'] = true;
682
- $responseArr['url'] = $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'];
683
- $ret['thumber']['thumbs'][ $ID ] = $dg_options['thumber']['thumbs'][ $ID ];
684
- }
685
  }
686
 
687
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
@@ -904,13 +918,13 @@ class DG_Admin {
904
  self::$URL_params = array( 'page' => DG_OPTION_NAME, 'tab' => 'Thumbnail' );
905
  $orderby = self::$URL_params['orderby'] = self::getOrderbyParam($orderby_options);
906
  $order = self::$URL_params['order'] = self::getOrderParam($order_options);
907
- $limit = self::$URL_params['limit'] = self::getLimitParam($limit_options);
908
 
909
- $thumbs = $options['thumbs'];
910
  uasort( $thumbs, array( __CLASS__, 'cmpThumb' ) );
911
- $thumbs_number = count( $options['thumbs'] );
912
  $lastsheet = ceil( $thumbs_number / $limit );
913
- $sheet = array_key_exists( 'sheet', $_REQUEST ) ? absint( $_REQUEST['sheet'] ) : 1;
914
  if ( $sheet === 0 || $sheet > $lastsheet ) {
915
  $sheet = 1;
916
  }
@@ -934,13 +948,14 @@ class DG_Admin {
934
  foreach ( $posts as $post ) {
935
  $path_parts = pathinfo( $post->guid );
936
 
937
- $t = &$thumbs[$post->ID];
938
- $t['title'] = !empty( $post->post_title ) ? $post->post_title : $path_parts['filename'];
939
- $t['ext'] = array_key_exists( 'extension', $path_parts ) ? $path_parts['extension'] : '';
 
 
 
940
  $t['description'] = $post->post_content;
941
- $t['icon'] = array_key_exists( 'thumb_url', $t )
942
- ? $t['thumb_url']
943
- : DG_Thumber::getDefaultThumbnail( $post->ID );
944
  }
945
  unset( $posts );
946
 
@@ -969,7 +984,7 @@ class DG_Admin {
969
 
970
  $pagination = '<div class="alignleft bulkactions"><button class="button action deleteSelected">' . __( 'Delete Selected', 'document-gallery' ) . '</button></div><div class="tablenav-pages">' .
971
  '<span class="displaying-num">' .
972
- $thumbs_number . ' ' . _n( 'item', 'items', $thumbs_number ) .
973
  '</span>' . ( $lastsheet > 1 ?
974
  '<span class="pagination-links">' .
975
  '<a class="first-page' . ( $sheet == 1 ? ' disabled' : '' ) . '" title="' . __( 'Go to the first page', 'document-gallery' ) . '"' . ( $sheet == 1 ? '' : ' href="?' . http_build_query( self::$URL_params ) . '"' ) . '>«</a>' .
@@ -1065,12 +1080,17 @@ class DG_Admin {
1065
  <?php }
1066
 
1067
  /**
1068
- * @param $limit_options array The possible options for limit. If no limit was provided then this is used to find a default limit.
1069
- *
1070
  * @return int The limit, which may or may not be a member of $limit_options.
1071
  */
1072
- private static function getLimitParam($limit_options) {
1073
- return array_key_exists( 'limit', $_REQUEST ) ? DG_Util::posint( $_REQUEST['limit'] ) : $limit_options[0];
 
 
 
 
 
 
 
1074
  }
1075
 
1076
  /**
@@ -1079,7 +1099,7 @@ class DG_Admin {
1079
  * @return string The order value.
1080
  */
1081
  private static function getOrderParam($order_options) {
1082
- $ret = array_key_exists( 'order', $_REQUEST ) ? strtolower( $_REQUEST['order'] ) : '';
1083
  return in_array($ret, $order_options) ? $ret : $order_options[0];
1084
  }
1085
 
@@ -1089,8 +1109,8 @@ class DG_Admin {
1089
  * @return string The orderby value.
1090
  */
1091
  private static function getOrderbyParam($orderby_options) {
1092
- $ret = array_key_exists( 'orderby', $_REQUEST ) ? strtolower( $_REQUEST['orderby'] ) : '';
1093
- return in_array($ret, $orderby_options) ? $ret : $orderby_options[0];
1094
  }
1095
 
1096
  /**
@@ -1112,12 +1132,16 @@ class DG_Admin {
1112
 
1113
  /**
1114
  * Render a Meta Box.
 
1115
  */
1116
  public static function renderMetaBox( $post ) {
1117
- global $dg_options;
1118
  wp_nonce_field( DG_OPTION_NAME . '_meta_box', DG_OPTION_NAME . '_meta_box_nonce' );
1119
- $ID = $post->ID;
1120
- $icon = isset( $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'] ) ? $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'] : DG_Thumber::getDefaultThumbnail( $ID );
 
 
 
 
1121
 
1122
  echo '<table id="ThumbsTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">' .
1123
  '<tbody><tr data-entry="' . $ID . '"><td class="column-icon media-icon"><img src="' .
@@ -1131,7 +1155,7 @@ class DG_Admin {
1131
  '</span>' .
1132
  '</span>' .
1133
  '</td></tr></tbody></table>' .
1134
- ( 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;
1135
  }
1136
 
1137
  /**
@@ -1145,7 +1169,6 @@ class DG_Admin {
1145
  return;
1146
  }
1147
 
1148
- global $dg_options;
1149
  $responseArr = array( 'result' => false );
1150
  if ( isset( $_POST[ DG_OPTION_NAME ]['entry'] ) ) {
1151
  $ID = intval( $_POST[ DG_OPTION_NAME ]['entry'] );
@@ -1153,15 +1176,12 @@ class DG_Admin {
1153
  $ID = - 1;
1154
  }
1155
 
1156
- if ( isset( $_POST[ DG_OPTION_NAME ]['upload'] ) && isset( $_FILES['file'] ) && isset( $dg_options['thumber']['thumbs'][ $ID ] ) ) {
1157
- $old_path = DG_Util::hasThumb($ID) ? $dg_options['thumber']['thumbs'][ $ID ]['thumb_path'] : null;
1158
  $uploaded_filename = self::validateUploadedFile();
1159
- if ( $uploaded_filename && DG_Thumber::setThumbnail( $ID, $uploaded_filename ) ) {
1160
- if ( ! is_null($old_path) && $dg_options['thumber']['thumbs'][ $ID ]['thumb_path'] !== $old_path ) {
1161
- @unlink( $old_path );
1162
- }
1163
  $responseArr['result'] = true;
1164
- $responseArr['url'] = $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'];
1165
  }
1166
  }
1167
 
@@ -1229,7 +1249,7 @@ class DG_Admin {
1229
  <?php echo $thead; ?>
1230
  </tfoot>
1231
  <tbody><?php
1232
- for ( $i = count( $log_list ); $i > 0; $i -- ) {
1233
  $log_entry = $log_list[ $i - 1 ];
1234
  $date = DocumentGallery::localDateTimeFromTimestamp( $log_entry[0] );
1235
 
@@ -1340,26 +1360,38 @@ class DG_Admin {
1340
  }
1341
 
1342
  /**
1343
- * @param $t1 array Thumbnail array #1.
1344
- * @param $t2 array Thumbnail array #2
1345
  *
1346
- * @return int The result of comparing the two thumbnail arrays using arguments in $URL_params.
1347
  */
1348
  public static function cmpThumb($t1, $t2) {
1349
  $ret = 0;
1350
  switch (self::$URL_params['orderby']) {
1351
  case 'date':
1352
- $ret = $t1['timestamp'] - $t2['timestamp'];
1353
  break;
1354
 
1355
  case 'title':
1356
- $ret = strcmp( basename( $t1['thumb_path'] ), basename( $t2['thumb_path'] ) );
1357
  break;
1358
  }
1359
 
1360
  return 'asc' === self::$URL_params['order'] ? $ret : -$ret;
1361
  }
1362
 
 
 
 
 
 
 
 
 
 
 
 
 
1363
  /**
1364
  * Wraps the PHP exit language construct.
1365
  */
72
 
73
  /**
74
  * Adds settings link to main plugin view.
75
+ * @param $links array The links being prepended.
76
+ * @return array The given array with settings link prepended.
77
  */
78
  public static function addSettingsLink( $links ) {
79
  $settings = '<a href="options-general.php?page=' . DG_OPTION_NAME . '">' .
85
 
86
  /**
87
  * Adds donate link to main plugin view.
88
+ * @param $links array The links.
89
+ * @param $file string The file.
90
+ * @return array The given array with donate link appended.
91
  */
92
  public static function addDonateLink( $links, $file ) {
93
  if ( $file === DG_BASENAME ) {
115
 
116
  /**
117
  * Enqueues styles and scripts for the admin settings page.
118
+ * @param $hook string The hook.
119
  */
120
  public static function enqueueScriptsAndStyles( $hook ) {
121
  if ( in_array( $hook, array( DG_Admin::$hook, 'post.php', 'post-new.php' ), true ) ) {
122
  // Settings Page
123
  DG_Util::enqueueAsset( 'document-gallery-admin', 'assets/css/admin.css' );
124
 
 
 
 
 
 
 
 
 
 
 
 
125
  DG_Util::enqueueAsset( 'document-gallery-admin', 'assets/js/admin.js', array( 'jquery' ) );
126
  wp_localize_script( 'document-gallery-admin', 'dg_admin_vars', array( 'upload_limit' => wp_max_upload_size() ) );
127
  if ( $hook !== self::$hook ) { //if $hook is 'post.php' or 'post-new.php'
129
 
130
  // Media Manager
131
  global $dg_options;
132
+ DG_Util::enqueueAsset( 'dg-media-manager', 'assets/js/media_manager.js', array( 'media-views' ) );
133
+ wp_localize_script( 'dg-media-manager', 'DGl10n', array(
134
+ 'dgMenuTitle' => __( 'Create Document Gallery', 'document-gallery' ),
135
+ 'dgButton' => __( 'Create a new Document Gallery', 'document-gallery' ),
136
+ 'canceldgTitle' => '&#8592; ' . __( 'Cancel Document Gallery', 'document-gallery' ),
137
+ 'updatedg' => __( 'Update Document Gallery', 'document-gallery' ),
138
+ 'insertdg' => __( 'Insert Document Gallery', 'document-gallery' ),
139
+ 'addTodg' => __( 'Add to Document Gallery', 'document-gallery' ),
140
+ 'addTodgTitle' => __( 'Add to Document Gallery', 'document-gallery' ),
141
+ 'editdgTitle' => __( 'Edit Document Gallery', 'document-gallery' ),
142
+ 'unfitSCalert' => __( 'This DG shortcode is an advanced one. '.
143
+ 'Sorry there is no way to use standard edit dialog for it. '.
144
+ 'You should switch to text mode to edit shortcode itself.', 'document-gallery' ),
145
  ) );
146
+ wp_localize_script( 'dg-media-manager', 'dgDefaults', $dg_options['gallery'] );
147
+
148
+ // TinyMCE visual editor
149
+ add_filter( 'mce_external_plugins', array( __CLASS__, 'mce_external_plugins' ) );
150
+ add_filter( 'mce_css', array( __CLASS__, 'dg_plugin_mce_css' ) );
151
  }
152
  }
153
  }
154
 
155
+ /**
156
+ * Adds assets/js/gallery.js as registered TinyMCE plugin
157
+ *
158
+ * @param array $plugin_array Previously registered plugins
159
+ *
160
+ * @return array Total set of plugins
161
+ */
162
+ public static function mce_external_plugins( $plugin_array ) {
163
+ $plugin_array['dg'] = DG_Util::getAssetPath( 'assets/js/gallery.js' );
164
+
165
+ return $plugin_array;
166
+ }
167
+
168
+ /**
169
+ * Adds assets/css/style.css as registered TinyMCE CSS
170
+ *
171
+ * @param array $mce_css Previously registered CSS
172
+ *
173
+ * @return array Total set of CSS
174
+ */
175
+ public static function dg_plugin_mce_css( $mce_css ) {
176
+ if ( ! empty( $mce_css ) ) {
177
+ $mce_css .= ',';
178
+ }
179
+ $mce_css .= str_replace( ',', '%2C', DG_Util::getAssetPath( 'assets/css/style.css' ) );
180
+
181
+ return $mce_css;
182
+ }
183
+
184
  /**
185
  * Load Document Gallery Custom templates.
186
  */
212
  global $dg_options;
213
 
214
  include_once DG_PATH . 'inc/class-gallery.php';
215
+ include_once DG_PATH . 'inc/class-gallery-sanitization.php';
216
  include_once DG_PATH . 'inc/class-thumber.php';
217
 
218
  $defaults = $dg_options['gallery'];
239
  'name' => 'gallery_defaults][attachment_pg',
240
  'value' => esc_attr( $defaults['attachment_pg'] ),
241
  'option_name' => DG_OPTION_NAME,
242
+ 'description' => __( 'Link to attachment page rather than to file.', 'document-gallery' )
243
  ) );
244
 
245
  add_settings_field(
264
  'name' => 'gallery_defaults][descriptions',
265
  'value' => esc_attr( $defaults['descriptions'] ),
266
  'option_name' => DG_OPTION_NAME,
267
+ 'description' => __( 'Include document descriptions.', 'document-gallery' )
268
  ) );
269
 
270
  add_settings_field(
276
  'name' => 'gallery_defaults][fancy',
277
  'value' => esc_attr( $defaults['fancy'] ),
278
  'option_name' => DG_OPTION_NAME,
279
+ 'description' => __( 'Use auto-generated document thumbnails.', 'document-gallery' )
280
  ) );
281
 
282
  add_settings_field(
287
  'label_for' => 'label_gallery_defaults_order',
288
  'name' => 'gallery_defaults][order',
289
  'value' => esc_attr( $defaults['order'] ),
290
+ 'options' => DG_GallerySanitization::getOrderOptions(),
291
  'option_name' => DG_OPTION_NAME,
292
+ 'description' => __( 'Ascending or descending sorting of documents.', 'document-gallery' )
293
  ) );
294
 
295
  add_settings_field(
300
  'label_for' => 'label_gallery_defaults_orderby',
301
  'name' => 'gallery_defaults][orderby',
302
  'value' => esc_attr( $defaults['orderby'] ),
303
+ 'options' => DG_GallerySanitization::getOrderbyOptions(),
304
  'option_name' => DG_OPTION_NAME,
305
+ 'description' => __( 'Which field to order documents by.', 'document-gallery' )
306
  ) );
307
 
308
  add_settings_field(
313
  'label_for' => 'label_gallery_defaults_relation',
314
  'name' => 'gallery_defaults][relation',
315
  'value' => esc_attr( $defaults['relation'] ),
316
+ 'options' => DG_GallerySanitization::getRelationOptions(),
317
  'option_name' => DG_OPTION_NAME,
318
+ 'description' => __( 'Whether matched documents must have all taxa_names (AND) or at least one (OR).', 'document-gallery' )
319
  ) );
320
 
321
  add_settings_field(
353
  'name' => 'gallery_defaults][new_window',
354
  'value' => esc_attr( $defaults['new_window'] ),
355
  'option_name' => DG_OPTION_NAME,
356
+ 'description' => __( 'Open thumbnail links in new window.', 'document-gallery' )
357
+ ) );
358
+
359
+ add_settings_field(
360
+ 'gallery_defaults_paginate', 'paginate',
361
+ array( __CLASS__, 'renderCheckboxField' ),
362
+ DG_OPTION_NAME, 'gallery_defaults',
363
+ array(
364
+ 'label_for' => 'label_gallery_defaults_paginate',
365
+ 'name' => 'gallery_defaults][paginate',
366
+ 'value' => esc_attr( $defaults['paginate'] ),
367
+ 'option_name' => DG_OPTION_NAME,
368
+ 'description' => __( 'When a limit exists, paginate rather than truncating gallery.', 'document-gallery' )
369
  ) );
370
 
371
  add_settings_field(
376
  'label_for' => 'label_gallery_defaults_post_status',
377
  'name' => 'gallery_defaults][post_status',
378
  'value' => esc_attr( $defaults['post_status'] ),
379
+ 'options' => DG_GallerySanitization::getPostStatuses(),
380
  'option_name' => DG_OPTION_NAME,
381
  'description' => __( 'Which post status to look for when querying documents.', 'document-gallery' )
382
  ) );
389
  'label_for' => 'label_gallery_defaults_post_type',
390
  'name' => 'gallery_defaults][post_type',
391
  'value' => esc_attr( $defaults['post_type'] ),
392
+ 'options' => DG_GallerySanitization::getPostTypes(),
393
  'option_name' => DG_OPTION_NAME,
394
  'description' => __( 'Which post type to look for when querying documents.', 'document-gallery' )
395
  ) );
550
  global $dg_options;
551
  return $dg_options;
552
  } else {
553
+ if ( isset( $values['ajax'] ) ) {
554
  unset( $values['ajax'] );
555
  define( 'DOING_AJAX', true );
556
  }
615
 
616
  // delete thumb cache to force regeneration if max dimensions changed
617
  if ( $ret['thumber']['width'] !== $dg_options['thumber']['width'] ||
618
+ $ret['thumber']['height'] !== $dg_options['thumber']['height'] ) {
619
+ DG_Thumb::purgeThumbs();
 
 
 
 
 
 
 
 
620
  }
621
 
622
  // handle setting the active thumbers
626
 
627
  // if new thumbers available, clear failed thumbnails for retry
628
  if ( ! $thumbs_cleared ) {
629
+ DG_Thumb::purgeFailedThumbs();
 
 
 
 
 
 
 
 
 
630
  }
631
 
632
  // handle modified CSS
658
  // Thumbnail(s) cleanup;
659
  // cleanup value is a marker
660
  if ( isset( $values['cleanup'] ) && isset( $values['ids'] ) ) {
661
+ $deleted = array_values( array_intersect( array_keys( DG_Thumb::getThumbs() ), $values['ids'] ) );
662
+ DG_Thumb::purgeThumbs( $deleted );
 
 
 
 
 
 
 
 
663
  $responseArr['result'] = true;
664
  $responseArr['deleted'] = $deleted;
665
  }
690
 
691
  // Thumbnail file manual refresh (one at a time)
692
  // upload value is a marker
693
+ elseif ( isset( $values['upload'] ) && isset( $_FILES['file'] ) && array_key_exists( $ID, DG_Thumb::getThumbs() ) ) {
 
694
  $uploaded_filename = self::validateUploadedFile();
695
+ if ( $uploaded_filename && ( $thumb = DG_Thumber::setThumbnail( $ID, $uploaded_filename ) ) ) {
696
+ $responseArr['result'] = true;
697
+ $responseArr['url'] = $thumb->getUrl();
698
+ }
 
 
 
 
699
  }
700
 
701
  if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
918
  self::$URL_params = array( 'page' => DG_OPTION_NAME, 'tab' => 'Thumbnail' );
919
  $orderby = self::$URL_params['orderby'] = self::getOrderbyParam($orderby_options);
920
  $order = self::$URL_params['order'] = self::getOrderParam($order_options);
921
+ $limit = self::$URL_params['limit'] = self::getLimitParam();
922
 
923
+ $thumbs = DG_Thumb::getThumbs( $options['width'] . 'x' . $options['height'] );
924
  uasort( $thumbs, array( __CLASS__, 'cmpThumb' ) );
925
+ $thumbs_number = count( $thumbs );
926
  $lastsheet = ceil( $thumbs_number / $limit );
927
+ $sheet = isset( $_REQUEST['sheet'] ) ? absint( $_REQUEST['sheet'] ) : 1;
928
  if ( $sheet === 0 || $sheet > $lastsheet ) {
929
  $sheet = 1;
930
  }
948
  foreach ( $posts as $post ) {
949
  $path_parts = pathinfo( $post->guid );
950
 
951
+ $thumb = $thumbs[$post->ID];
952
+ $thumbs[$post->ID] = array();
953
+ $t = &$thumbs[$post->ID];
954
+ $t['timestamp'] = $thumb->getTimestamp();
955
+ $t['title'] = self::getTitle( $post );
956
+ $t['ext'] = isset( $path_parts['extension'] ) ? $path_parts['extension'] : '';
957
  $t['description'] = $post->post_content;
958
+ $t['icon'] = $thumb->isSuccess() ? $thumb->getUrl() : DG_Thumber::getDefaultThumbnail( $post->ID );
 
 
959
  }
960
  unset( $posts );
961
 
984
 
985
  $pagination = '<div class="alignleft bulkactions"><button class="button action deleteSelected">' . __( 'Delete Selected', 'document-gallery' ) . '</button></div><div class="tablenav-pages">' .
986
  '<span class="displaying-num">' .
987
+ $thumbs_number . ' ' . _n( 'item', 'items', $thumbs_number, 'document-gallery' ) .
988
  '</span>' . ( $lastsheet > 1 ?
989
  '<span class="pagination-links">' .
990
  '<a class="first-page' . ( $sheet == 1 ? ' disabled' : '' ) . '" title="' . __( 'Go to the first page', 'document-gallery' ) . '"' . ( $sheet == 1 ? '' : ' href="?' . http_build_query( self::$URL_params ) . '"' ) . '>«</a>' .
1080
  <?php }
1081
 
1082
  /**
 
 
1083
  * @return int The limit, which may or may not be a member of $limit_options.
1084
  */
1085
+ private static function getLimitParam() {
1086
+ global $dg_options;
1087
+ $limit = isset( $_REQUEST['limit'] ) ? DG_Util::posint( $_REQUEST['limit'] ) : $dg_options['meta']['items_per_page'];
1088
+ if ( $limit !== $dg_options['meta']['items_per_page'] ) {
1089
+ $dg_options['meta']['items_per_page'] = $limit;
1090
+ DocumentGallery::setOptions( $dg_options );
1091
+ }
1092
+
1093
+ return $limit;
1094
  }
1095
 
1096
  /**
1099
  * @return string The order value.
1100
  */
1101
  private static function getOrderParam($order_options) {
1102
+ $ret = isset( $_REQUEST['order'] ) ? strtolower( $_REQUEST['order'] ) : '';
1103
  return in_array($ret, $order_options) ? $ret : $order_options[0];
1104
  }
1105
 
1109
  * @return string The orderby value.
1110
  */
1111
  private static function getOrderbyParam($orderby_options) {
1112
+ $ret = isset( $_REQUEST['orderby'] ) ? strtolower( $_REQUEST['orderby'] ) : '';
1113
+ return in_array( $ret, $orderby_options ) ? $ret : $orderby_options[0];
1114
  }
1115
 
1116
  /**
1132
 
1133
  /**
1134
  * Render a Meta Box.
1135
+ * @param $post WP_Post The post.
1136
  */
1137
  public static function renderMetaBox( $post ) {
 
1138
  wp_nonce_field( DG_OPTION_NAME . '_meta_box', DG_OPTION_NAME . '_meta_box_nonce' );
1139
+ $ID = $post->ID;
1140
+ $options = DG_Thumber::getOptions();
1141
+ $thumb = DG_Thumb::getThumb( $ID, $options['width'] . 'x' . $options['height'] );
1142
+ $icon = ! is_null( $thumb ) && $thumb->isSuccess()
1143
+ ? $thumb->getUrl()
1144
+ : DG_Thumber::getDefaultThumbnail( $ID );
1145
 
1146
  echo '<table id="ThumbsTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">' .
1147
  '<tbody><tr data-entry="' . $ID . '"><td class="column-icon media-icon"><img src="' .
1155
  '</span>' .
1156
  '</span>' .
1157
  '</td></tr></tbody></table>' .
1158
+ ( is_null( $thumb ) ? '<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;
1159
  }
1160
 
1161
  /**
1169
  return;
1170
  }
1171
 
 
1172
  $responseArr = array( 'result' => false );
1173
  if ( isset( $_POST[ DG_OPTION_NAME ]['entry'] ) ) {
1174
  $ID = intval( $_POST[ DG_OPTION_NAME ]['entry'] );
1176
  $ID = - 1;
1177
  }
1178
 
1179
+ $thumbs = DG_Thumb::getThumbs();
1180
+ if ( isset( $_POST[DG_OPTION_NAME]['upload'] ) && isset( $_FILES['file'] ) && isset( $thumbs[$ID] ) ) {
1181
  $uploaded_filename = self::validateUploadedFile();
1182
+ if ( $uploaded_filename && ( $thumb = DG_Thumber::setThumbnail( $ID, $uploaded_filename ) ) ) {
 
 
 
1183
  $responseArr['result'] = true;
1184
+ $responseArr['url'] = $thumb->getUrl();
1185
  }
1186
  }
1187
 
1249
  <?php echo $thead; ?>
1250
  </tfoot>
1251
  <tbody><?php
1252
+ for ( $i = count( $log_list ); $i > 0; $i-- ) {
1253
  $log_entry = $log_list[ $i - 1 ];
1254
  $date = DocumentGallery::localDateTimeFromTimestamp( $log_entry[0] );
1255
 
1360
  }
1361
 
1362
  /**
1363
+ * @param $t1 DG_Thumb Thumbnail #1.
1364
+ * @param $t2 DG_Thumb Thumbnail #2
1365
  *
1366
+ * @return int The result of comparing the two thumbs using arguments in $URL_params.
1367
  */
1368
  public static function cmpThumb($t1, $t2) {
1369
  $ret = 0;
1370
  switch (self::$URL_params['orderby']) {
1371
  case 'date':
1372
+ $ret = $t1->getTimestamp() - $t2->getTimestamp();
1373
  break;
1374
 
1375
  case 'title':
1376
+ $ret = strcmp( self::getTitle( $t1->getPostId() ), self::getTitle( $t2->getPostId() ) );
1377
  break;
1378
  }
1379
 
1380
  return 'asc' === self::$URL_params['order'] ? $ret : -$ret;
1381
  }
1382
 
1383
+ /**
1384
+ * @param $post int|WP_Post The post to get title of.
1385
+ * @return string The title.
1386
+ */
1387
+ private static function getTitle( $post ) {
1388
+ if ( is_numeric( $post ) ) {
1389
+ $post = get_post( $post );
1390
+ }
1391
+
1392
+ return ! empty( $post->post_title ) ? $post->post_title : pathinfo( $post->guid, PATHINFO_FILENAME );
1393
+ }
1394
+
1395
  /**
1396
  * Wraps the PHP exit language construct.
1397
  */
admin/class-ajax-handler.php ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'WPINC' ) OR exit;
3
+
4
+ // register for all handled actions
5
+ add_action( 'wp_ajax_dg_generate_icons', array( 'DG_AjaxHandler', 'generateIcons' ) );
6
+ add_action( 'wp_ajax_nopriv_dg_generate_icons', array( 'DG_AjaxHandler', 'generateIcons' ) );
7
+
8
+ add_action( 'wp_ajax_dg_generate_gallery', array( 'DG_AjaxHandler', 'generateGallery' ) );
9
+ add_action( 'wp_ajax_nopriv_dg_generate_gallery', array( 'DG_AjaxHandler', 'generateGallery' ) );
10
+
11
+ /**
12
+ * Handler to isolate AJAX request handling.
13
+ *
14
+ * @author drossiter
15
+ */
16
+ class DG_AjaxHandler {
17
+
18
+ /**
19
+ * Accepts AJAX request containing list of IDs to be generated and returned.
20
+ * Returns associative array mapping ID to thumbnail URL for all icons that were generated,
21
+ * skipping any that could not be processed.
22
+ */
23
+ public static function generateIcons() {
24
+ $ret = array();
25
+
26
+ if ( isset( $_REQUEST['ids'] ) ) {
27
+ foreach ( $_REQUEST['ids'] as $id ) {
28
+ // only return URL if different from default -- default image is already displayed on the client side
29
+ $url = DG_Thumber::getThumbnail( $id, 1, true, $is_default );
30
+ if ( ! $is_default ) {
31
+ $ret[$id] = $url;
32
+ }
33
+ }
34
+ }
35
+
36
+ wp_send_json($ret);
37
+ }
38
+
39
+ /**
40
+ * Accepts AJAX request containing an array matching any allowable params for the [dg] shortcode.
41
+ * Returns the resultant gallery HTML.
42
+ */
43
+ public static function generateGallery() {
44
+ if ( isset( $_REQUEST['atts'] ) ) {
45
+ @header( 'Content-Type: text/html; charset=' . get_option( 'blog_charset' ) );
46
+ echo DocumentGallery::doShortcode( $_REQUEST['atts'] );
47
+ }
48
+
49
+ wp_die();
50
+ }
51
+ }
admin/media-manager-template.php CHANGED
@@ -1,19 +1,23 @@
1
  <?php /* Custom templates into the DOM */
2
  include_once DG_PATH . 'inc/class-gallery.php';
3
  ?>
4
- <script type="text/html" id="tmpl-document-gallery-settings">
5
  <h3><?php _e('Document Gallery Settings', 'document-gallery'); ?></h3>
6
 
7
  <label class="setting">
8
  <table><tr>
9
  <td><span><?php _e('Link To'); ?></span></td>
10
  <td><select class="link-to" data-setting="attachment_pg">
11
- <option value="false" <# if ( !documentGalleryDefaults.attachment_pg ) { #>selected="selected"<# } #>>
12
- <?php esc_attr_e('Media File'); ?>
13
- </option>
14
- <option value="true" <# if ( documentGalleryDefaults.attachment_pg ) { #>selected="selected"<# } #>>
15
- <?php esc_attr_e('Attachment Page'); ?>
16
- </option>
 
 
 
 
17
  </select></td>
18
  </tr></table>
19
  </label>
@@ -23,16 +27,16 @@
23
  <td><span><?php _e('Columns'); ?></span></td>
24
  <td><select class="columns" name="columns" data-setting="columns">
25
  <option value="-1" <#
26
- if ( '-1' == documentGalleryDefaults.columns ) { #>selected="selected"<# }
27
- #>>
28
- &infin;
29
- </option>
30
  <?php for ( $i = 1; $i <= 9; $i++ ) : ?>
31
  <option value="<?php echo esc_attr( $i ); ?>" <#
32
- if ( <?php echo $i ?> == documentGalleryDefaults.columns ) { #>selected="selected"<# }
33
- #>>
34
- <?php echo esc_html( $i ); ?>
35
- </option>
36
  <?php endfor; ?>
37
  </select></td>
38
  </tr></table>
@@ -41,34 +45,46 @@
41
  <label class="setting">
42
  <table><tr>
43
  <td><span><?php _e('Open thumbnail links in new window', 'document-gallery'); ?></span></td>
44
- <td><input type="checkbox" data-setting="new_window" <# if ( documentGalleryDefaults.new_window ) { #>checked="checked"<# } #>/></td>
 
 
 
 
45
  </tr></table>
46
  </label>
47
 
48
  <label class="setting">
49
  <table><tr>
50
  <td><span><?php _e('Include document descriptions', 'document-gallery'); ?></span></td>
51
- <td><input type="checkbox" data-setting="descriptions" <# if ( documentGalleryDefaults.descriptions ) { #>checked="checked"<# } #>/></td>
 
 
 
 
52
  </tr></table>
53
  </label>
54
 
55
  <label class="setting">
56
  <table><tr>
57
  <td><span><?php _e('Use auto-generated document thumbnails', 'document-gallery'); ?></span></td>
58
- <td><input type="checkbox" data-setting="fancy" <# if ( documentGalleryDefaults.fancy ) { #>checked="checked"<# } #>/></td>
 
 
 
 
59
  </tr></table>
60
  </label>
61
 
62
  <label class="setting">
63
  <table><tr>
64
  <td><span><?php _e('Which field to order documents by', 'document-gallery'); ?></span></td>
65
- <td><select class="DGorderby" name="DGorderby" data-setting="DGorderby">
66
- <?php foreach ( DG_Gallery::getOrderbyOptions() as $i ) : ?>
67
  <option value="<?php echo esc_attr( $i ); ?>" <#
68
- if ( '<?php echo $i ?>' == documentGalleryDefaults.orderby ) { #>selected="selected"<# }
69
- #>>
70
- <?php echo esc_html( $i ); ?>
71
- </option>
72
  <?php endforeach; ?>
73
  </select></td>
74
  </tr></table>
@@ -77,15 +93,42 @@
77
  <label class="setting">
78
  <table><tr>
79
  <td><span><?php _e('Ascending or descending sorting of documents', 'document-gallery'); ?></span></td>
80
- <td><select class="order" name="order" data-setting="order">
81
- <?php foreach ( DG_Gallery::getOrderOptions() as $i ) : ?>
82
  <option value="<?php echo esc_attr( $i ); ?>" <#
83
- if ( '<?php echo $i ?>' == documentGalleryDefaults.order ) { #>selected="selected"<# }
84
- #>>
85
- <?php echo esc_html( $i ); ?>
86
- </option>
87
  <?php endforeach; ?>
88
  </select></td>
89
  </tr></table>
90
  </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  </script>
1
  <?php /* Custom templates into the DOM */
2
  include_once DG_PATH . 'inc/class-gallery.php';
3
  ?>
4
+ <script type="text/html" id="tmpl-dg-settings">
5
  <h3><?php _e('Document Gallery Settings', 'document-gallery'); ?></h3>
6
 
7
  <label class="setting">
8
  <table><tr>
9
  <td><span><?php _e('Link To'); ?></span></td>
10
  <td><select class="link-to" data-setting="attachment_pg">
11
+ <option value="false" <#
12
+ if ( !wp.media.dgDefaults.attachment_pg ) {
13
+ #>selected="selected"<#
14
+ }
15
+ #>><?php esc_attr_e('Media File'); ?></option>
16
+ <option value="true" <#
17
+ if ( wp.media.dgDefaults.attachment_pg ) {
18
+ #>selected="selected"<#
19
+ }
20
+ #>><?php esc_attr_e('Attachment Page'); ?></option>
21
  </select></td>
22
  </tr></table>
23
  </label>
27
  <td><span><?php _e('Columns'); ?></span></td>
28
  <td><select class="columns" name="columns" data-setting="columns">
29
  <option value="-1" <#
30
+ if ( '-1' == wp.media.dgDefaults.columns ) {
31
+ #>selected="selected"<#
32
+ }
33
+ #>>&infin;</option>
34
  <?php for ( $i = 1; $i <= 9; $i++ ) : ?>
35
  <option value="<?php echo esc_attr( $i ); ?>" <#
36
+ if ( <?php echo $i ?> == wp.media.dgDefaults.columns ) {
37
+ #>selected="selected"<#
38
+ }
39
+ #>><?php echo esc_html( $i ); ?></option>
40
  <?php endfor; ?>
41
  </select></td>
42
  </tr></table>
45
  <label class="setting">
46
  <table><tr>
47
  <td><span><?php _e('Open thumbnail links in new window', 'document-gallery'); ?></span></td>
48
+ <td><input type="checkbox" data-setting="new_window" <#
49
+ if ( wp.media.dgDefaults.new_window ) {
50
+ #>checked="checked"<#
51
+ } #> />
52
+ </td>
53
  </tr></table>
54
  </label>
55
 
56
  <label class="setting">
57
  <table><tr>
58
  <td><span><?php _e('Include document descriptions', 'document-gallery'); ?></span></td>
59
+ <td><input type="checkbox" data-setting="descriptions" <#
60
+ if ( wp.media.dgDefaults.descriptions ) {
61
+ #>checked="checked"<#
62
+ } #> />
63
+ </td>
64
  </tr></table>
65
  </label>
66
 
67
  <label class="setting">
68
  <table><tr>
69
  <td><span><?php _e('Use auto-generated document thumbnails', 'document-gallery'); ?></span></td>
70
+ <td><input type="checkbox" data-setting="fancy" <#
71
+ if ( wp.media.dgDefaults.fancy ) {
72
+ #>checked="checked"<#
73
+ } #> />
74
+ </td>
75
  </tr></table>
76
  </label>
77
 
78
  <label class="setting">
79
  <table><tr>
80
  <td><span><?php _e('Which field to order documents by', 'document-gallery'); ?></span></td>
81
+ <td><select data-setting="dgorderby">
82
+ <?php foreach ( DG_GallerySanitization::getOrderbyOptions() as $i ) : ?>
83
  <option value="<?php echo esc_attr( $i ); ?>" <#
84
+ if ( '<?php echo $i ?>' == wp.media.dgDefaults.dgorderby ) {
85
+ #>selected="selected"<#
86
+ }
87
+ #>><?php echo esc_html( $i ); ?></option>
88
  <?php endforeach; ?>
89
  </select></td>
90
  </tr></table>
93
  <label class="setting">
94
  <table><tr>
95
  <td><span><?php _e('Ascending or descending sorting of documents', 'document-gallery'); ?></span></td>
96
+ <td><select data-setting="dgorder">
97
+ <?php foreach ( DG_GallerySanitization::getOrderOptions() as $i ) : ?>
98
  <option value="<?php echo esc_attr( $i ); ?>" <#
99
+ if ( '<?php echo $i ?>' == wp.media.dgDefaults.dgorder ) {
100
+ #>selected="selected"<#
101
+ }
102
+ #>><?php echo esc_html( $i ); ?></option>
103
  <?php endforeach; ?>
104
  </select></td>
105
  </tr></table>
106
  </label>
107
+
108
+ <label class="setting">
109
+ <table><tr>
110
+ <td><span><?php _e('Paginate', 'document-gallery'); ?></span></td>
111
+ <td><select data-setting="paginate">
112
+ <option value="false" <#
113
+ if ( !wp.media.dgDefaults.paginate ) {
114
+ #>selected="selected"<#
115
+ }
116
+ #>><?php _e('No', 'document-gallery'); ?></option>
117
+ <option value="true" <#
118
+ if ( wp.media.dgDefaults.paginate ) {
119
+ #>selected="selected"<#
120
+ }
121
+ #>><?php _e('Yes', 'document-gallery'); ?></option>
122
+ </select></td>
123
+ </tr></table>
124
+ </label>
125
+
126
+ <label class="setting">
127
+ <table><tr>
128
+ <td><span><?php _e('Limit', 'document-gallery'); ?></span></td>
129
+ <td><input data-setting="limit" type="number" min="-1" step="1" value="{{ wp.media.dgDefaults.limit }}" /></td>
130
+ </tr></table>
131
+ </label>
132
+ </script>
133
+ <script type="text/html" id="tmpl-editor-dg">
134
  </script>
assets/css/admin.css CHANGED
@@ -627,26 +627,26 @@ td.column-title input {
627
  -webkit-border-radius: 5px;
628
  }
629
 
630
- .document-gallery-settings label.setting {
631
  margin: 2px 0;
632
  }
633
 
634
- .document-gallery-settings label.setting table {
635
  width: 100%;
636
  padding-right: 8px;
637
  }
638
 
639
- .document-gallery-settings label.setting table tr td:last-of-type, .document-gallery-settings label.setting table tr td:last-of-type * {
640
  text-align: right !important;
641
  float: none !important;
642
  margin: auto 0 !important;
643
  }
644
 
645
- .document-gallery-settings label.setting table tr td:last-of-type select {
646
  max-width: none !important;
647
  }
648
 
649
- .document-gallery-settings label.setting table tr td span {
650
  text-align: left !important;
651
  float: none !important;
652
  }
627
  -webkit-border-radius: 5px;
628
  }
629
 
630
+ .dg-settings label.setting {
631
  margin: 2px 0;
632
  }
633
 
634
+ .dg-settings label.setting table {
635
  width: 100%;
636
  padding-right: 8px;
637
  }
638
 
639
+ .dg-settings label.setting table tr td:last-of-type, .dg-settings label.setting table tr td:last-of-type * {
640
  text-align: right !important;
641
  float: none !important;
642
  margin: auto 0 !important;
643
  }
644
 
645
+ .dg-settings label.setting table tr td:last-of-type select {
646
  max-width: none !important;
647
  }
648
 
649
+ .dg-settings label.setting table tr td span {
650
  text-align: left !important;
651
  float: none !important;
652
  }
assets/css/admin.min.css CHANGED
@@ -1 +1 @@
1
- @media screen and (max-width:782px){.column-title{width:150px}.column-date{display:table-cell !important;width:auto !important}.nav-tab-wrapper{padding-left:5px !important;padding-right:5px !important}.top .tablenav-pages{display:none}.bottom .tablenav-pages{width:auto !important;margin-top:0 !important}.bottom .displaying-num{position:inherit !important}}@media screen and (max-width:979px){.column-thumbupload{display:none}.thumbs-list-wrapper{margin-top:0 !important}}div.thumbs-list-wrapper,div.log-list-wrapper{text-align:center;margin-top:1.5em}div.thumbs-list-wrapper>div,div.log-list-wrapper>div{margin:0 auto;display:inline-block}#ThumbsTable,#LogTable{border:0;box-shadow:none;-webkit-box-shadow:none;-moz-box-shadow:none;border-radius:10px;-webkit-border-radius:10px;-moz-border-radius:10px;background:#b9c9fe;color:#039;width:100%;margin:10px auto}#ThumbsTable tbody,#LogTable tbody{background:#e8edff;color:#669}#ThumbsTable>tbody>tr:hover,#LogTable>tbody>tr:hover{background:#d0dafd}#ThumbsTable>tbody>tr:not(:last-child) td,#LogTable>tbody>tr:not(:last-child) td{border-bottom:1px solid #b9c9fe}#ThumbsTable td,#ThumbsTable th,#LogTable td,#LogTable th{text-align:center;vertical-align:middle;margin:0;padding:4px}#LogTable td{text-align:left}td.title.column-title,.column-thumbupload{text-align:left !important}td.column-icon.media-icon{height:70px}td.media-icon img{width:auto;height:auto;max-width:80px;max-height:60px;border:0}#ThumbsTable img{display:block;margin:5px auto}#LogTable td>pre,.spoiler-body>pre,.expander pre,.collapser pre{margin:0;display:inline-block;white-space:pre-line}tr.selected{background:#b6adce}tr.selected:hover{background:#d8d3e5 !important}.check-column,.column-icon{white-space:nowrap;width:80px}.column-thumbupload{white-space:nowrap;width:35%}#document_gallery_gen_box .column-thumbupload{width:auto;padding-left:7em}.nav-tab:before,.deleteSelected:before,.clearLog:before,.expandAll:before,.collapseAll:before,.logLabel.date:before,.collapser:after,.expander:after{display:inline-block;-webkit-font-smoothing:antialiased;font:normal 20px/1 'dashicons';vertical-align:text-bottom;padding-right:5px}.General-tab:before{content:'\f108'}.Thumbnail-tab:before{content:'\f233'}.Logging-tab:before{content:'\f163'}.Advanced-tab:before{content:'\f332'}.deleteSelected:before,.clearLog:before{content:'\f182'}.expandAll:before{content:'\f211'}.collapseAll:before{content:'\f506'}.expandAll,.collapseAll{display:none}#ThumbsTable .title a:after,#LogTable>tbody a:after{content:'\f504';display:inline-block;-webkit-font-smoothing:antialiased;font-family:'dashicons';font-size:inherit;font-style:normal;font-variant:normal;font-weight:normal;line-height:1;vertical-align:inherit;padding-left:5px}#LogTable>tbody a{-webkit-transition:none;transition:none;text-decoration:none;outline:0}#LogTable>tbody pre strong{font-weight:bolder}#LogTable>tbody a:active,#LogTable>tbody a:hover{color:#2ea2cc}.levelSelector{float:right}.logLabel{padding:0 10px;color:#fff !important;text-decoration:none;font-weight:bolder;border:none !important;float:left;margin-left:1px;margin-top:1px;-webkit-border-radius:100px;-moz-border-radius:100px;border-radius:100px;cursor:context-menu}.logLabel.warning{background:#f89406}.logLabel.detail{background:#3a87ad}.logLabel.error{background:#c00}.logLabel.date{background:#999;font-weight:inherit}.logLabel.date:before{font-size:inherit;vertical-align:middle;padding-bottom:.2em;content:'\f469'}.spoiler-body{padding:1px 6px 2px;display:none;border-top:1px solid #c3cbd1;background:#f5f5f5}.column-entry{text-align:left !important}.expander pre,.collapser pre{vertical-align:middle;float:left;white-space:pre-wrap}.expander,.collapser{display:table;vertical-align:middle;width:100%;cursor:pointer}.expander>div,.collapser>div{display:table-cell;vertical-align:middle;height:100%}.dashicons.dashicons-arrow-down-alt2,.dashicons.dashicons-arrow-up-alt2{float:right;padding-right:15px}.levelSelector>*,.dashicons{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.levelSelector>input[type=checkbox]{display:none}.levelSelector>input[type=checkbox]+label{color:#6b6b6b;font-weight:bolder;margin:4px 0;overflow:auto;text-align:center;padding:3px 8px;display:table-cell;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#FFF,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#FFF),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#FFF,#e6e6e6);background-image:-o-linear-gradient(top,#FFF,#e6e6e6);background-image:linear-gradient(to bottom,#FFF,#e6e6e6);background-repeat:repeat-x;border:1px solid #CCC;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF',endColorstr='#FFE6E6E6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.levelSelector>input[type=checkbox]+label:first-of-type{border-top-left-radius:4px;border-bottom-left-radius:4px}.levelSelector>input[type=checkbox]+label:last-of-type{border-top-right-radius:4px;border-bottom-right-radius:4px}.levelSelector>input[type=checkbox]:checked+label{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.levelSelector>input[type=checkbox]+label.errorLevel{color:#c00}.levelSelector>input[type=checkbox]:checked+label.errorLevel{background-color:#c00;color:#FFF}.levelSelector>input[type=checkbox]+label.warningLevel{color:#f89406}.levelSelector>input[type=checkbox]:checked+label.warningLevel{background-color:#f89406;color:#FFF}.levelSelector>input[type=checkbox]+label.detailLevel{color:#3a87ad}.levelSelector>input[type=checkbox]:checked+label.detailLevel{background-color:#3a87ad;color:#FFF}.noLog{font-size:x-large;display:block;width:100%;text-align:center;margin-top:20ex;line-height:150%}.loggingON,.loggingOFF{font-weight:bolder}.loggingON{color:green}.loggingOFF{color:red}th input{margin-left:0 !important;margin-top:1px !important}td input{margin-right:0 !important}textarea[readonly],input[readonly],select[readonly]{background-color:#dcdcdc}.nowrap{white-space:nowrap}.column-level{white-space:nowrap;width:6em}.column-date{width:16em !important}.column-thumbupload{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.column-thumbupload>*{visibility:hidden}.column-thumbupload .dashicons{font-size:x-large;padding-top:2px;padding-left:16px;padding-right:10px;vertical-align:text-bottom}#ThumbsTable tr:hover .column-thumbupload>*,#document_gallery_gen_box #ThumbsTable tr .column-thumbupload>*{visibility:visible}.dragover{outline:3px dashed #83b4d8}.html5dndmarker span{padding:0 10px}.buttons-area input:first-child,.invis{display:none !important}#document_gallery_gen_box h3 span{font-weight:100}#document_gallery_gen_box h3 span i b{font-weight:500}th.column-description{text-align:left !important}.editable-description{max-height:70px;overflow-y:auto;text-align:justify}.column-description textarea{height:65px;width:100%}td.column-title,td.column-description{position:relative}td.column-title.trans,td.column-description.trans{background-color:inherit;transition:background-color 1s linear;-o-transition:background-color 1s linear;-moz-transition:background-color 1s linear;-webkit-transition:background-color 1s linear}td .dashicons-edit,td .edit-controls{display:none;position:absolute;top:5px;right:12px}td .dashicons-edit,.edit-controls .dashicons-yes,.edit-controls .dashicons-no,.edit-controls .dashicons-update{z-index:100;font-size:x-large;cursor:pointer;width:auto;height:auto}td .dashicons-edit,.edit-controls .dashicons-update{color:#0074a2}.edit-controls:hover,.edit-controls.waiting{opacity:1}.edit-controls{opacity:.1}.edit-controls .dashicons-yes{color:green}.edit-controls .dashicons-no{color:red}.edit-controls.waiting .dashicons-yes,.edit-controls.waiting .dashicons-no{display:none}.edit-controls .dashicons-update{display:none;cursor:default}.edit-controls.waiting .dashicons-update,.deleteSelected.waiting:before{display:block;-webkit-animation:spin 1s linear infinite;-moz-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.deleteSelected.waiting:before{content:'\f463';width:auto;height:auto;display:inline-block;padding:0}@-moz-keyframes spin{100%{-moz-transform:rotate(360deg)}}@-webkit-keyframes spin{100%{-webkit-transform:rotate(360deg)}}@keyframes spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.responseSuccess{background-color:greenyellow !important}.responseFail{background-color:crimson !important}td.column-title .dashicons-edit,td.column-description .dashicons-edit{height:0;opacity:0;display:block;overflow:hidden;transition:opacity 1s ease-out;-o-transition:opacity 1s ease-out;-moz-transition:opacity 1s ease-out;-webkit-transition:opacity 1s ease-out}td.column-title:hover .dashicons-edit,td.column-description:hover .dashicons-edit{opacity:1;height:auto}td.column-title input{width:75%}.manual-download{display:block;text-align:center}.editable-description::-webkit-scrollbar,.column-description textarea::-webkit-scrollbar{width:5px;height:5px}.editable-description::-webkit-scrollbar-track-piece,.column-description textarea::-webkit-scrollbar-track-piece{background-color:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.editable-description::-webkit-scrollbar-thumb:vertical,.column-description textarea::-webkit-scrollbar-thumb:vertical{height:5px;background-color:#9b9b9b;-webkit-border-radius:5px}.document-gallery-settings label.setting{margin:2px 0}.document-gallery-settings label.setting table{width:100%;padding-right:8px}.document-gallery-settings label.setting table tr td:last-of-type,.document-gallery-settings label.setting table tr td:last-of-type *{text-align:right !important;float:none !important;margin:auto 0 !important}.document-gallery-settings label.setting table tr td:last-of-type select{max-width:none !important}.document-gallery-settings label.setting table tr td span{text-align:left !important;float:none !important}.progress{border:0;width:100%;height:18px;position:relative;background-color:#f1f1f1;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;-webkit-box-shadow:inset 0 -1px 1px rgba(255,255,255,0.3);-moz-box-shadow:inset 0 -1px 1px rgba(255,255,255,0.3);box-shadow:inset 0 -1px 1px rgba(255,255,255,0.3)}.column-thumbupload .progress{visibility:visible !important}.progress>span{border:0;height:100%;display:block;overflow:hidden;position:relative;-webkit-transition:width .5s linear;-moz-transition:width .5s linear;-o-transition:width .5s linear;transition:width .5s linear;background-color:#7ad03a;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;background-image:linear-gradient(90deg,#2bc253 37%,#54f054 69%);background-image:-moz-linear-gradient(90deg,#2bc253 37%,#54f054 69%);background-image:-o-linear-gradient(90deg,#2bc253 37%,#54f054 69%);background-image:-webkit-gradient(linear,left bottom,left top,color-stop(0,#2bc253),color-stop(1,#54f054));-webkit-box-shadow:inset 0 2px 9px rgba(255,255,255,0.3),inset 0 -2px 6px rgba(0,0,0,0.4);-moz-box-shadow:inset 0 2px 9px rgba(255,255,255,0.3),inset 0 -2px 6px rgba(0,0,0,0.4);box-shadow:inset 0 2px 9px rgba(255,255,255,0.3),inset 0 -2px 6px rgba(0,0,0,0.4)}.progress>span:after,.progress.animate>span>span{top:0;left:0;right:0;bottom:0;z-index:1;content:'';overflow:hidden;position:absolute;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;background-image:-webkit-gradient(linear,0 0,100% 100%,color-stop(.25,rgba(255,255,255,.2)),color-stop(.25,transparent),color-stop(.5,transparent),color-stop(.5,rgba(255,255,255,.2)),color-stop(.75,rgba(255,255,255,.2)),color-stop(.75,transparent),to(transparent));background-image:-moz-linear-gradient(315deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(315deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);background-image:linear-gradient(315deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);-webkit-background-size:50px 50px;-moz-background-size:50px 50px;background-size:50px 50px;-webkit-animation:move 2s linear infinite;-moz-animation:move 2s linear infinite;-ms-animation:move 2s linear infinite;animation:move 2s linear infinite}@-webkit-keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}@-moz-keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}@-ms-keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}@keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}.progress.animate>span:after{display:none}.progress.success>span{background-color:green;width:100% !important;background-image:none !important}.progress.fail>span{background-color:red;background-image:none !important}.progress.nostripes>span>span,.progress.nostripes>span:after,.progress.success>span>span,.progress.success>span:after,.progress.fail>span>span,.progress.fail>span:after{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none;background-image:none}
1
+ @media screen and (max-width:782px){.column-title{width:150px}.column-date{display:table-cell !important;width:auto !important}.nav-tab-wrapper{padding-left:5px !important;padding-right:5px !important}.top .tablenav-pages{display:none}.bottom .tablenav-pages{width:auto !important;margin-top:0 !important}.bottom .displaying-num{position:inherit !important}}@media screen and (max-width:979px){.column-thumbupload{display:none}.thumbs-list-wrapper{margin-top:0 !important}}div.thumbs-list-wrapper,div.log-list-wrapper{text-align:center;margin-top:1.5em}div.thumbs-list-wrapper>div,div.log-list-wrapper>div{margin:0 auto;display:inline-block}#ThumbsTable,#LogTable{border:0;box-shadow:none;-webkit-box-shadow:none;-moz-box-shadow:none;border-radius:10px;-webkit-border-radius:10px;-moz-border-radius:10px;background:#b9c9fe;color:#039;width:100%;margin:10px auto}#ThumbsTable tbody,#LogTable tbody{background:#e8edff;color:#669}#ThumbsTable>tbody>tr:hover,#LogTable>tbody>tr:hover{background:#d0dafd}#ThumbsTable>tbody>tr:not(:last-child) td,#LogTable>tbody>tr:not(:last-child) td{border-bottom:1px solid #b9c9fe}#ThumbsTable td,#ThumbsTable th,#LogTable td,#LogTable th{text-align:center;vertical-align:middle;margin:0;padding:4px}#LogTable td{text-align:left}td.title.column-title,.column-thumbupload{text-align:left !important}td.column-icon.media-icon{height:70px}td.media-icon img{width:auto;height:auto;max-width:80px;max-height:60px;border:0}#ThumbsTable img{display:block;margin:5px auto}#LogTable td>pre,.spoiler-body>pre,.expander pre,.collapser pre{margin:0;display:inline-block;white-space:pre-line}tr.selected{background:#b6adce}tr.selected:hover{background:#d8d3e5 !important}.check-column,.column-icon{white-space:nowrap;width:80px}.column-thumbupload{white-space:nowrap;width:35%}#document_gallery_gen_box .column-thumbupload{width:auto;padding-left:7em}.nav-tab:before,.deleteSelected:before,.clearLog:before,.expandAll:before,.collapseAll:before,.logLabel.date:before,.collapser:after,.expander:after{display:inline-block;-webkit-font-smoothing:antialiased;font:normal 20px/1 'dashicons';vertical-align:text-bottom;padding-right:5px}.General-tab:before{content:'\f108'}.Thumbnail-tab:before{content:'\f233'}.Logging-tab:before{content:'\f163'}.Advanced-tab:before{content:'\f332'}.deleteSelected:before,.clearLog:before{content:'\f182'}.expandAll:before{content:'\f211'}.collapseAll:before{content:'\f506'}.expandAll,.collapseAll{display:none}#ThumbsTable .title a:after,#LogTable>tbody a:after{content:'\f504';display:inline-block;-webkit-font-smoothing:antialiased;font-family:'dashicons';font-size:inherit;font-style:normal;font-variant:normal;font-weight:normal;line-height:1;vertical-align:inherit;padding-left:5px}#LogTable>tbody a{-webkit-transition:none;transition:none;text-decoration:none;outline:0}#LogTable>tbody pre strong{font-weight:bolder}#LogTable>tbody a:active,#LogTable>tbody a:hover{color:#2ea2cc}.levelSelector{float:right}.logLabel{padding:0 10px;color:#fff !important;text-decoration:none;font-weight:bolder;border:none !important;float:left;margin-left:1px;margin-top:1px;-webkit-border-radius:100px;-moz-border-radius:100px;border-radius:100px;cursor:context-menu}.logLabel.warning{background:#f89406}.logLabel.detail{background:#3a87ad}.logLabel.error{background:#c00}.logLabel.date{background:#999;font-weight:inherit}.logLabel.date:before{font-size:inherit;vertical-align:middle;padding-bottom:.2em;content:'\f469'}.spoiler-body{padding:1px 6px 2px;display:none;border-top:1px solid #c3cbd1;background:#f5f5f5}.column-entry{text-align:left !important}.expander pre,.collapser pre{vertical-align:middle;float:left;white-space:pre-wrap}.expander,.collapser{display:table;vertical-align:middle;width:100%;cursor:pointer}.expander>div,.collapser>div{display:table-cell;vertical-align:middle;height:100%}.dashicons.dashicons-arrow-down-alt2,.dashicons.dashicons-arrow-up-alt2{float:right;padding-right:15px}.levelSelector>*,.dashicons{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.levelSelector>input[type=checkbox]{display:none}.levelSelector>input[type=checkbox]+label{color:#6b6b6b;font-weight:bolder;margin:4px 0;overflow:auto;text-align:center;padding:3px 8px;display:table-cell;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#FFF,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#FFF),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#FFF,#e6e6e6);background-image:-o-linear-gradient(top,#FFF,#e6e6e6);background-image:linear-gradient(to bottom,#FFF,#e6e6e6);background-repeat:repeat-x;border:1px solid #CCC;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);border-bottom-color:#b3b3b3;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF',endColorstr='#FFE6E6E6',GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05)}.levelSelector>input[type=checkbox]+label:first-of-type{border-top-left-radius:4px;border-bottom-left-radius:4px}.levelSelector>input[type=checkbox]+label:last-of-type{border-top-right-radius:4px;border-bottom-right-radius:4px}.levelSelector>input[type=checkbox]:checked+label{background-image:none;outline:0;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);-moz-box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05)}.levelSelector>input[type=checkbox]+label.errorLevel{color:#c00}.levelSelector>input[type=checkbox]:checked+label.errorLevel{background-color:#c00;color:#FFF}.levelSelector>input[type=checkbox]+label.warningLevel{color:#f89406}.levelSelector>input[type=checkbox]:checked+label.warningLevel{background-color:#f89406;color:#FFF}.levelSelector>input[type=checkbox]+label.detailLevel{color:#3a87ad}.levelSelector>input[type=checkbox]:checked+label.detailLevel{background-color:#3a87ad;color:#FFF}.noLog{font-size:x-large;display:block;width:100%;text-align:center;margin-top:20ex;line-height:150%}.loggingON,.loggingOFF{font-weight:bolder}.loggingON{color:green}.loggingOFF{color:red}th input{margin-left:0 !important;margin-top:1px !important}td input{margin-right:0 !important}textarea[readonly],input[readonly],select[readonly]{background-color:#dcdcdc}.nowrap{white-space:nowrap}.column-level{white-space:nowrap;width:6em}.column-date{width:16em !important}.column-thumbupload{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:default}.column-thumbupload>*{visibility:hidden}.column-thumbupload .dashicons{font-size:x-large;padding-top:2px;padding-left:16px;padding-right:10px;vertical-align:text-bottom}#ThumbsTable tr:hover .column-thumbupload>*,#document_gallery_gen_box #ThumbsTable tr .column-thumbupload>*{visibility:visible}.dragover{outline:3px dashed #83b4d8}.html5dndmarker span{padding:0 10px}.buttons-area input:first-child,.invis{display:none !important}#document_gallery_gen_box h3 span{font-weight:100}#document_gallery_gen_box h3 span i b{font-weight:500}th.column-description{text-align:left !important}.editable-description{max-height:70px;overflow-y:auto;text-align:justify}.column-description textarea{height:65px;width:100%}td.column-title,td.column-description{position:relative}td.column-title.trans,td.column-description.trans{background-color:inherit;transition:background-color 1s linear;-o-transition:background-color 1s linear;-moz-transition:background-color 1s linear;-webkit-transition:background-color 1s linear}td .dashicons-edit,td .edit-controls{display:none;position:absolute;top:5px;right:12px}td .dashicons-edit,.edit-controls .dashicons-yes,.edit-controls .dashicons-no,.edit-controls .dashicons-update{z-index:100;font-size:x-large;cursor:pointer;width:auto;height:auto}td .dashicons-edit,.edit-controls .dashicons-update{color:#0074a2}.edit-controls:hover,.edit-controls.waiting{opacity:1}.edit-controls{opacity:.1}.edit-controls .dashicons-yes{color:green}.edit-controls .dashicons-no{color:red}.edit-controls.waiting .dashicons-yes,.edit-controls.waiting .dashicons-no{display:none}.edit-controls .dashicons-update{display:none;cursor:default}.edit-controls.waiting .dashicons-update,.deleteSelected.waiting:before{display:block;-webkit-animation:spin 1s linear infinite;-moz-animation:spin 1s linear infinite;animation:spin 1s linear infinite}.deleteSelected.waiting:before{content:'\f463';width:auto;height:auto;display:inline-block;padding:0}@-moz-keyframes spin{100%{-moz-transform:rotate(360deg)}}@-webkit-keyframes spin{100%{-webkit-transform:rotate(360deg)}}@keyframes spin{100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.responseSuccess{background-color:greenyellow !important}.responseFail{background-color:crimson !important}td.column-title .dashicons-edit,td.column-description .dashicons-edit{height:0;opacity:0;display:block;overflow:hidden;transition:opacity 1s ease-out;-o-transition:opacity 1s ease-out;-moz-transition:opacity 1s ease-out;-webkit-transition:opacity 1s ease-out}td.column-title:hover .dashicons-edit,td.column-description:hover .dashicons-edit{opacity:1;height:auto}td.column-title input{width:75%}.manual-download{display:block;text-align:center}.editable-description::-webkit-scrollbar,.column-description textarea::-webkit-scrollbar{width:5px;height:5px}.editable-description::-webkit-scrollbar-track-piece,.column-description textarea::-webkit-scrollbar-track-piece{background-color:#fff;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px}.editable-description::-webkit-scrollbar-thumb:vertical,.column-description textarea::-webkit-scrollbar-thumb:vertical{height:5px;background-color:#9b9b9b;-webkit-border-radius:5px}.dg-settings label.setting{margin:2px 0}.dg-settings label.setting table{width:100%;padding-right:8px}.dg-settings label.setting table tr td:last-of-type,.dg-settings label.setting table tr td:last-of-type *{text-align:right !important;float:none !important;margin:auto 0 !important}.dg-settings label.setting table tr td:last-of-type select{max-width:none !important}.dg-settings label.setting table tr td span{text-align:left !important;float:none !important}.progress{border:0;width:100%;height:18px;position:relative;background-color:#f1f1f1;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;-webkit-box-shadow:inset 0 -1px 1px rgba(255,255,255,0.3);-moz-box-shadow:inset 0 -1px 1px rgba(255,255,255,0.3);box-shadow:inset 0 -1px 1px rgba(255,255,255,0.3)}.column-thumbupload .progress{visibility:visible !important}.progress>span{border:0;height:100%;display:block;overflow:hidden;position:relative;-webkit-transition:width .5s linear;-moz-transition:width .5s linear;-o-transition:width .5s linear;transition:width .5s linear;background-color:#7ad03a;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;background-image:linear-gradient(90deg,#2bc253 37%,#54f054 69%);background-image:-moz-linear-gradient(90deg,#2bc253 37%,#54f054 69%);background-image:-o-linear-gradient(90deg,#2bc253 37%,#54f054 69%);background-image:-webkit-gradient(linear,left bottom,left top,color-stop(0,#2bc253),color-stop(1,#54f054));-webkit-box-shadow:inset 0 2px 9px rgba(255,255,255,0.3),inset 0 -2px 6px rgba(0,0,0,0.4);-moz-box-shadow:inset 0 2px 9px rgba(255,255,255,0.3),inset 0 -2px 6px rgba(0,0,0,0.4);box-shadow:inset 0 2px 9px rgba(255,255,255,0.3),inset 0 -2px 6px rgba(0,0,0,0.4)}.progress>span:after,.progress.animate>span>span{top:0;left:0;right:0;bottom:0;z-index:1;content:'';overflow:hidden;position:absolute;-webkit-border-radius:9px;-moz-border-radius:9px;border-radius:9px;background-image:-webkit-gradient(linear,0 0,100% 100%,color-stop(.25,rgba(255,255,255,.2)),color-stop(.25,transparent),color-stop(.5,transparent),color-stop(.5,rgba(255,255,255,.2)),color-stop(.75,rgba(255,255,255,.2)),color-stop(.75,transparent),to(transparent));background-image:-moz-linear-gradient(315deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(315deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);background-image:linear-gradient(315deg,rgba(255,255,255,.2) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.2) 50%,rgba(255,255,255,.2) 75%,transparent 75%,transparent);-webkit-background-size:50px 50px;-moz-background-size:50px 50px;background-size:50px 50px;-webkit-animation:move 2s linear infinite;-moz-animation:move 2s linear infinite;-ms-animation:move 2s linear infinite;animation:move 2s linear infinite}@-webkit-keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}@-moz-keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}@-ms-keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}@keyframes move{0{background-position:0 0}100%{background-position:50px 50px}}.progress.animate>span:after{display:none}.progress.success>span{background-color:green;width:100% !important;background-image:none !important}.progress.fail>span{background-color:red;background-image:none !important}.progress.nostripes>span>span,.progress.nostripes>span:after,.progress.success>span>span,.progress.success>span:after,.progress.fail>span>span,.progress.fail>span:after{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none;background-image:none}
assets/css/style.css CHANGED
@@ -1,30 +1,36 @@
1
- div.document-icon {
2
  text-align: center;
3
  }
4
 
5
- div.document-icon img {
6
  width: 89px;
7
  max-width: 100%;
8
  border: none;
 
9
  }
10
 
11
- div.document-icon a {
12
  font-size: 10px;
13
  line-height: 12px;
14
  }
15
 
16
- div.document-icon {
17
  margin: 5px 0 0;
18
  }
19
 
 
 
 
 
 
20
  /* WITHOUT DESCRIPTION */
21
- div.document-icon {
22
  display: inline-block;
23
  vertical-align: top;
24
  overflow: hidden;
25
  }
26
 
27
- div.document-icon-wrapper {
28
  width: 100%;
29
  padding: 0;
30
  text-align: left;
@@ -33,18 +39,18 @@ div.document-icon-wrapper {
33
  /* END WITHOUT DESCRIPTION */
34
 
35
  /* WITH DESCRIPTION */
36
- div.descriptions.document-icon-wrapper div.document-icon {
37
  max-width: 115px;
38
  padding: 0 3px 0 0;
39
  float: left;
40
  }
41
 
42
- div.descriptions.document-icon-wrapper {
43
  vertical-align: middle;
44
  text-align: inherit;
45
  }
46
 
47
- div.descriptions.document-icon-wrapper img {
48
  width: 65px;
49
  max-width: 100%;
50
  }
@@ -52,17 +58,17 @@ div.descriptions.document-icon-wrapper img {
52
  /* clearfix */
53
  /* can't depend on theme having a clearfix class,
54
  so build it into dg css */
55
- div.descriptions.document-icon-wrapper:before,
56
- div.descriptions.document-icon-wrapper:after {
57
  content: "";
58
  display: table;
59
  }
60
 
61
- div.descriptions.document-icon-wrapper:after {
62
  clear: both;
63
  }
64
 
65
- div.descriptions.document-icon-wrapper {
66
  zoom: 1; /* For IE 6/7 (trigger hasLayout) */
67
  }
68
 
1
+ .document-gallery .document-icon {
2
  text-align: center;
3
  }
4
 
5
+ .document-gallery .document-icon img {
6
  width: 89px;
7
  max-width: 100%;
8
  border: none;
9
+ margin: 0 auto;
10
  }
11
 
12
+ .document-gallery .document-icon a {
13
  font-size: 10px;
14
  line-height: 12px;
15
  }
16
 
17
+ .document-gallery .document-icon {
18
  margin: 5px 0 0;
19
  }
20
 
21
+ .document-gallery .document-icon .title {
22
+ display: block;
23
+ text-align: center;
24
+ }
25
+
26
  /* WITHOUT DESCRIPTION */
27
+ .document-gallery .document-icon {
28
  display: inline-block;
29
  vertical-align: top;
30
  overflow: hidden;
31
  }
32
 
33
+ .document-gallery .document-icon-row {
34
  width: 100%;
35
  padding: 0;
36
  text-align: left;
39
  /* END WITHOUT DESCRIPTION */
40
 
41
  /* WITH DESCRIPTION */
42
+ .document-gallery .descriptions.document-icon-row .document-icon {
43
  max-width: 115px;
44
  padding: 0 3px 0 0;
45
  float: left;
46
  }
47
 
48
+ .document-gallery .descriptions.document-icon-row {
49
  vertical-align: middle;
50
  text-align: inherit;
51
  }
52
 
53
+ .document-gallery .descriptions.document-icon-row img {
54
  width: 65px;
55
  max-width: 100%;
56
  }
58
  /* clearfix */
59
  /* can't depend on theme having a clearfix class,
60
  so build it into dg css */
61
+ .document-gallery .descriptions.document-icon-row:before,
62
+ .document-gallery .descriptions.document-icon-row:after {
63
  content: "";
64
  display: table;
65
  }
66
 
67
+ .document-gallery .descriptions.document-icon-row:after {
68
  clear: both;
69
  }
70
 
71
+ .document-gallery .descriptions.document-icon-row {
72
  zoom: 1; /* For IE 6/7 (trigger hasLayout) */
73
  }
74
 
assets/css/style.min.css CHANGED
@@ -1 +1 @@
1
- div.document-icon{text-align:center}div.document-icon img{width:89px;max-width:100%;border:0}div.document-icon a{font-size:10px;line-height:12px}div.document-icon{margin:5px 0 0}div.document-icon{display:inline-block;vertical-align:top;overflow:hidden}div.document-icon-wrapper{width:100%;padding:0;text-align:left}div.descriptions.document-icon-wrapper div.document-icon{max-width:115px;padding:0 3px 0 0;float:left}div.descriptions.document-icon-wrapper{vertical-align:middle;text-align:inherit}div.descriptions.document-icon-wrapper img{width:65px;max-width:100%}div.descriptions.document-icon-wrapper:before,div.descriptions.document-icon-wrapper:after{content:"";display:table}div.descriptions.document-icon-wrapper:after{clear:both}div.descriptions.document-icon-wrapper{zoom:1}
1
+ .document-gallery .document-icon{text-align:center}.document-gallery .document-icon img{width:89px;max-width:100%;border:0;margin:0 auto}.document-gallery .document-icon a{font-size:10px;line-height:12px}.document-gallery .document-icon{margin:5px 0 0}.document-gallery .document-icon .title{display:block;text-align:center}.document-gallery .document-icon{display:inline-block;vertical-align:top;overflow:hidden}.document-gallery .document-icon-row{width:100%;padding:0;text-align:left}.document-gallery .descriptions.document-icon-row .document-icon{max-width:115px;padding:0 3px 0 0;float:left}.document-gallery .descriptions.document-icon-row{vertical-align:middle;text-align:inherit}.document-gallery .descriptions.document-icon-row img{width:65px;max-width:100%}.document-gallery .descriptions.document-icon-row:before,.document-gallery .descriptions.document-icon-row:after{content:"";display:table}.document-gallery .descriptions.document-icon-row:after{clear:both}.document-gallery .descriptions.document-icon-row{zoom:1}
assets/js/gallery.js CHANGED
@@ -1,24 +1,98 @@
1
- (function() {
2
- // distinct list of all attachment IDs populated
3
- var ids = [];
4
 
5
- // current index in pendingIcons
6
- var i = 0;
7
 
8
  // find all document-icons without icons generated and start processing
9
- jQuery(document).ready(function() {
10
- jQuery('.document-icon[data-dg-id]').each(function() {
11
- var id = jQuery(this).data('dg-id');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  // if we have multiple galleries, we could have multiple elements
14
- // needing the same icon amd no need to request multiple times
15
- if (-1 === jQuery.inArray(id, ids)) {
16
  ids.push(id);
17
  }
18
  });
19
 
20
  retrieveNextIcons();
21
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
  /**
24
  * Sends AJAX request to server requesting some of the not-yet-generated icons (if any).
@@ -40,7 +114,7 @@
40
 
41
  if (idBatch.length != 0) {
42
  // request the next batch of icons
43
- jQuery.post(ajaxurl, { action: 'dg_generate_icons', ids: idBatch }, processRetrievedThumbnails);
44
  }
45
  }
46
 
@@ -51,12 +125,15 @@
51
  function processRetrievedThumbnails(response) {
52
  for (var id in response) {
53
  if (response.hasOwnProperty(id)) {
54
- var target = jQuery('.document-icon[data-dg-id="' + id + '"] img');
 
 
55
  (function(id, target) {
56
- target.fadeOut('fast', function () {
57
- jQuery(this).attr('src', response[id]);
58
- jQuery(this).fadeIn('fast');
59
- })
 
60
  })(id, target);
61
  }
62
  }
@@ -64,4 +141,4 @@
64
  // start next batch once this response is processed
65
  retrieveNextIcons();
66
  }
67
- })();
1
+ (function($) {
2
+ // distinct list of all document IDs to be fetched via AJAX requests
3
+ var ids;
4
 
5
+ // current index in ids array
6
+ var i;
7
 
8
  // find all document-icons without icons generated and start processing
9
+ $(document).ready(function() {
10
+ sizeGalleryIcons();
11
+ resetPendingIcons();
12
+ handleVisualEditor();
13
+ registerPaginationHandler();
14
+ });
15
+
16
+ /**
17
+ * Sets the size of the gallery icons based on the column count.
18
+ * @param gallery If given, the target gallery. Otherwise all galleries on page.
19
+ */
20
+ function sizeGalleryIcons(gallery) {
21
+ (gallery || $('.document-gallery[data-icon-width]')).each(function() {
22
+ var icon_width = $(this).data('icon-width') + '%';
23
+ $(this).find('.document-icon').width(icon_width);
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Handles necessary logic for when we're rendering gallery preview within visual editor.
29
+ */
30
+ function handleVisualEditor() {
31
+ if (typeof tinymce !== 'undefined') {
32
+ tinymce.PluginManager.add('dg', function (editor, url) {
33
+ editor.on('LoadContent dgUpdate undo', function (e) {
34
+ $(e.target.contentDocument).find('.wpview-type-dg > [data-shortcode]').each(function () {
35
+ retrieveGallery($.parseJSON(decodeURIComponent($(this).data('shortcode'))), $(this));
36
+ });
37
+ });
38
+ });
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Listen for all pagination clicks in current DOM and any future DOM elements.
44
+ */
45
+ function registerPaginationHandler() {
46
+ $('body').delegate('.dg-paginate-wrapper a.paginate', 'click', function (e) {
47
+ var target = $(this).closest('.dg-paginate-wrapper');
48
+ var atts = target.data('shortcode');
49
+
50
+ if ($(this).hasClass('left')) {
51
+ atts['skip'] -= atts['limit'];
52
+ } else {
53
+ atts['skip'] += atts['limit'];
54
+ }
55
+
56
+ retrieveGallery(atts, target);
57
+ e.preventDefault();
58
+ });
59
+ }
60
+
61
+ /**
62
+ * Collects all of the DG icons that need to be generated and starts requesting them via AJAX.
63
+ */
64
+ function resetPendingIcons() {
65
+ ids = [];
66
+ i = 0;
67
+
68
+ $('.document-gallery img[data-id]').each(function() {
69
+ var id = $(this).data('id');
70
 
71
  // if we have multiple galleries, we could have multiple elements
72
+ // needing the same icon and no need to request multiple times
73
+ if (-1 === $.inArray(id, ids)) {
74
  ids.push(id);
75
  }
76
  });
77
 
78
  retrieveNextIcons();
79
+ }
80
+
81
+ /**
82
+ * Requests a gallery generated with the given attributes to populate the given target element.
83
+ * @param atts array The gallery shortcode attributes.
84
+ * @param target element The element to be updated with the AJAX HTML response.
85
+ */
86
+ function retrieveGallery(atts, target) {
87
+ // TODO: Cache already-retrieved gallery pages. Need to be careful not to keep too many at a time
88
+ // (could consume a lot of memory) & handle caching pages for multiple galleries on a single pages.
89
+ $.post(ajaxurl, { action: 'dg_generate_gallery', atts: atts }, function(html) {
90
+ var jobj = $(html);
91
+ target.replaceWith(jobj);
92
+ sizeGalleryIcons(jobj);
93
+ resetPendingIcons();
94
+ });
95
+ }
96
 
97
  /**
98
  * Sends AJAX request to server requesting some of the not-yet-generated icons (if any).
114
 
115
  if (idBatch.length != 0) {
116
  // request the next batch of icons
117
+ $.post(ajaxurl, { action: 'dg_generate_icons', ids: idBatch }, processRetrievedThumbnails);
118
  }
119
  }
120
 
125
  function processRetrievedThumbnails(response) {
126
  for (var id in response) {
127
  if (response.hasOwnProperty(id)) {
128
+ var target = $('.document-gallery img[data-id="' + id + '"]');
129
+ target.removeAttr('data-id');
130
+
131
  (function(id, target) {
132
+ var speed = 'fast';
133
+ target.fadeOut(speed, function () {
134
+ $(this).attr('src', response[id]);
135
+ $(this).fadeIn(speed);
136
+ });
137
  })(id, target);
138
  }
139
  }
141
  // start next batch once this response is processed
142
  retrieveNextIcons();
143
  }
144
+ })(jQuery);
assets/js/gallery.min.js CHANGED
@@ -1,2 +1,3 @@
1
- (function(){function d(){for(var a=[];c<b.length&&2!==a.length;c++)a.push(b[c]);0!=a.length&&jQuery.post(ajaxurl,{action:"dg_generate_icons",ids:a},e)}function e(a){for(var b in a)if(a.hasOwnProperty(b)){var c=jQuery('.document-icon[data-dg-id="'+b+'"] img');(function(b,c){c.fadeOut("fast",function(){jQuery(this).attr("src",a[b]);jQuery(this).fadeIn("fast")})})(b,c)}d()}var b=[],c=0;jQuery(document).ready(function(){jQuery(".document-icon[data-dg-id]").each(function(){var a=jQuery(this).data("dg-id");
2
- -1===jQuery.inArray(a,b)&&b.push(a)});d()})})();
 
1
+ (function(b){function g(a){(a||b(".document-gallery[data-icon-width]")).each(function(){var a=b(this).data("icon-width")+"%";b(this).find(".document-icon").width(a)})}function m(){"undefined"!==typeof tinymce&&tinymce.PluginManager.add("dg",function(a,d){a.on("LoadContent dgUpdate undo",function(a){b(a.target.contentDocument).find(".wpview-type-dg > [data-shortcode]").each(function(){h(b.parseJSON(decodeURIComponent(b(this).data("shortcode"))),b(this))})})})}function n(){b("body").delegate(".dg-paginate-wrapper a.paginate",
2
+ "click",function(a){var d=b(this).closest(".dg-paginate-wrapper"),c=d.data("shortcode");b(this).hasClass("left")?c.skip-=c.limit:c.skip+=c.limit;h(c,d);a.preventDefault()})}function k(){e=[];f=0;b(".document-gallery img[data-id]").each(function(){var a=b(this).data("id");-1===b.inArray(a,e)&&e.push(a)});l()}function h(a,d){b.post(ajaxurl,{action:"dg_generate_gallery",atts:a},function(a){a=b(a);d.replaceWith(a);g(a);k()})}function l(){for(var a=[];f<e.length&&2!==a.length;f++)a.push(e[f]);0!=a.length&&
3
+ b.post(ajaxurl,{action:"dg_generate_icons",ids:a},p)}function p(a){for(var d in a)if(a.hasOwnProperty(d)){var c=b('.document-gallery img[data-id="'+d+'"]');c.removeAttr("data-id");(function(c,d){d.fadeOut("fast",function(){b(this).attr("src",a[c]);b(this).fadeIn("fast")})})(d,c)}l()}var e,f;b(document).ready(function(){g();k();m();n()})})(jQuery);
assets/js/media_manager.js CHANGED
@@ -1,14 +1,24 @@
 
 
 
 
 
 
 
 
1
  ( function ($, _) {
2
- var media = wp.media;
 
3
 
4
  // Link any localized strings.
5
  l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;
6
  jQuery.extend(l10n, DGl10n);
7
 
 
8
  /**
9
- * wp.media.controller.DocumentGalleryEdit
10
  *
11
- * A state for editing a Document Gallery's images and settings.
12
  *
13
  * @class
14
  * @augments wp.media.controller.Library
@@ -16,36 +26,36 @@
16
  * @augments Backbone.Model
17
  *
18
  * @param {object} [attributes] The attributes hash passed to the state.
19
- * @param {string} [attributes.id=gallery-edit] Unique identifier.
20
- * @param {string} [attributes.title=Edit Gallery] Title for the state. Displays in the frame's title region.
21
- * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the gallery.
22
  * If one is not supplied, an empty media.model.Selection collection is created.
23
  * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
24
  * @param {boolean} [attributes.searchable=false] Whether the library is searchable.
25
  * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
26
  * @param {string|false} [attributes.content=browse] Initial mode for the content region.
27
  * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
28
- * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a gallery.
29
  * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface.
30
  * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
31
  * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
32
- * @param {boolean} [attributes.editing=false] Whether the gallery is being created, or editing an existing instance.
33
  * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
34
  * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
35
  * Defaults to false for this state, because the library passed in *is* the selection.
36
  * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
37
  * If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
38
  */
39
- media.controller.DocumentGalleryEdit = media.controller.Library.extend({
40
  defaults: {
41
- id: 'document-gallery-edit',
42
- title: l10n.editDocumentGalleryTitle,
43
  multiple: false,
44
  searchable: false,
45
  sortable: true,
46
  display: false,
47
  content: 'browse',
48
- toolbar: 'document-gallery-edit',
49
  describe: true,
50
  displaySettings: true,
51
  dragInfo: true,
@@ -76,12 +86,12 @@
76
  var library = this.get('library');
77
 
78
  // Limit the library to images only.
79
- //library.props.set( 'type', '' );
80
 
81
  // Watch for uploaded attachments.
82
  this.get('library').observe(wp.Uploader.queue);
83
 
84
- this.frame.on('content:render:browse', this.gallerySettings, this);
85
 
86
  media.controller.Library.prototype.activate.apply(this, arguments);
87
  },
@@ -93,7 +103,7 @@
93
  // Stop watching for uploaded attachments.
94
  this.get('library').unobserve(wp.Uploader.queue);
95
 
96
- this.frame.off('content:render:browse', this.gallerySettings, this);
97
 
98
  media.controller.Library.prototype.deactivate.apply(this, arguments);
99
  },
@@ -103,7 +113,7 @@
103
  *
104
  * @param browser
105
  */
106
- gallerySettings: function (browser) {
107
  if (!this.get('displaySettings')) {
108
  return;
109
  }
@@ -114,12 +124,12 @@
114
  return;
115
  }
116
 
117
- library.gallery = library.gallery || new Backbone.Model();
118
 
119
  browser.sidebar.set({
120
- document_gallery: new media.view.Settings.DocumentGallery({
121
  controller: this,
122
- model: library.gallery,
123
  priority: 40
124
  })
125
  });
@@ -136,7 +146,7 @@
136
  });
137
 
138
  /**
139
- * A state for selecting more images to add to a Document Gallery.
140
  *
141
  * @class
142
  * @augments wp.media.controller.Library
@@ -144,34 +154,34 @@
144
  * @augments Backbone.Model
145
  *
146
  * @param {object} [attributes] The attributes hash passed to the state.
147
- * @param {string} [attributes.id=gallery-library] Unique identifier.
148
- * @param {string} [attributes.title=Add to Gallery] Title for the state. Displays in the frame's title region.
149
  * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
150
  * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
151
  * If one is not supplied, a collection of all images will be created.
152
  * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
153
  * Accepts 'all', 'uploaded', or 'unattached'.
154
- * @param {string} [attributes.menu=gallery] Initial mode for the menu region.
155
  * @param {string} [attributes.content=upload] Initial mode for the content region.
156
  * Overridden by persistent user setting if 'contentUserSetting' is true.
157
  * @param {string} [attributes.router=browse] Initial mode for the router region.
158
- * @param {string} [attributes.toolbar=gallery-add] Initial mode for the toolbar region.
159
  * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
160
  * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
161
  * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
162
  * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
163
  * @param {int} [attributes.priority=100] The priority for the state link in the media menu.
164
  * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
165
- * Defaults to false because for this state, because the library of the Edit Gallery state is the selection.
166
  */
167
- media.controller.DocumentGalleryAdd = media.controller.Library.extend({
168
  defaults: _.defaults({
169
- id: 'document-gallery-library',
170
- title: l10n.addToDocumentGalleryTitle,
171
  multiple: 'add',
172
  filterable: 'uploaded',
173
- menu: 'document-gallery',
174
- toolbar: 'document-gallery-add',
175
  priority: 100,
176
  syncSelection: false
177
  }, media.controller.Library.prototype.defaults),
@@ -192,13 +202,13 @@
192
  */
193
  activate: function () {
194
  var library = this.get('library'),
195
- edit = this.frame.state('document-gallery-edit').get('library');
196
 
197
  if (this.editLibrary && this.editLibrary !== edit)
198
  library.unobserve(this.editLibrary);
199
 
200
  // Accepts attachments that exist in the original library and
201
- // that do not exist in gallery's library.
202
  library.validator = function (attachment) {
203
  return !!this.mirroring.get(attachment.cid) && !edit.get(attachment.cid) && media.model.Selection.prototype.validator.apply(this, arguments);
204
  };
@@ -215,7 +225,7 @@
215
  });
216
 
217
  /**
218
- * wp.media.view.Settings.DocumentGallery
219
  *
220
  * @class
221
  * @augments wp.media.view.Settings
@@ -223,12 +233,53 @@
223
  * @augments wp.Backbone.View
224
  * @augments Backbone.View
225
  */
226
- media.view.Settings.DocumentGallery = media.view.Settings.extend({
227
- className: 'collection-settings gallery-settings document-gallery-settings',
228
- template: media.template('document-gallery-settings')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  });
230
 
231
  // supersede the default MediaFrame.Post view
 
232
  var wpMediaFramePost = wp.media.view.MediaFrame.Post;
233
  wp.media.view.MediaFrame.Post = wpMediaFramePost.extend(
234
  {
@@ -236,10 +287,10 @@
236
  wpMediaFramePost.prototype.initialize.apply(this, arguments);
237
  this.states.add([
238
  new media.controller.Library({
239
- id: 'document-gallery',
240
- title: l10n.documentGalleryMenuTitle,
241
  priority: 50,
242
- toolbar: 'main-document-gallery',
243
  filterable: 'all',
244
  multiple: 'add',
245
  editable: false,
@@ -248,32 +299,35 @@
248
  }),
249
 
250
  // Document Gallery states.
251
- new media.controller.DocumentGalleryEdit({
252
  library: this.options.selection,
253
  editing: this.options.editing,
254
- menu: 'document-gallery'
255
  }),
256
 
257
- new media.controller.DocumentGalleryAdd()
258
  ]);
259
 
260
- this.on('menu:create:document-gallery', this.createMenu, this);
261
- this.on('toolbar:create:main-document-gallery', this.createToolbar, this);
262
 
263
- this.on('menu:render:document-gallery', this.documentGalleryMenu, this);
264
- this.on('toolbar:render:main-document-gallery', this.mainDocumentGalleryToolbar, this);
265
- this.on('toolbar:render:document-gallery-edit', this.documentGalleryEditToolbar, this);
266
- this.on('toolbar:render:document-gallery-add', this.documentGalleryAddToolbar, this);
267
  },
268
 
269
- documentGalleryMenu: function (view) {
 
 
 
270
  var lastState = this.lastState(),
271
  previous = lastState && lastState.id,
272
  frame = this;
273
 
274
  view.set({
275
  cancel: {
276
- text: l10n.cancelDocumentGalleryTitle,
277
  priority: 20,
278
  click: function () {
279
  if (previous) {
@@ -283,7 +337,7 @@
283
  }
284
 
285
  // Keep focus inside media modal
286
- // after canceling a gallery
287
  this.controller.modal.focusManager.focus();
288
  }
289
  },
@@ -297,41 +351,44 @@
297
  /**
298
  * @param {wp.Backbone.View} view
299
  */
300
- mainDocumentGalleryToolbar: function (view) {
301
  var controller = this;
302
 
303
  this.selectionStatusToolbar(view);
304
 
305
- view.set('document-gallery', {
306
  style: 'primary',
307
- text: l10n.documentGalleryButton,
308
  priority: 60,
309
  requires: {selection: true},
310
 
311
  click: function () {
312
  var selection = controller.state().get('selection'),
313
- edit = controller.state('document-gallery-edit'),
314
  models = selection.models;
315
 
316
- edit.set('library', selection);
 
 
 
317
 
318
- this.controller.setState('document-gallery-edit');
319
 
320
  // Keep focus inside media modal
321
- // after jumping to gallery view
322
  this.controller.modal.focusManager.focus();
323
  }
324
  });
325
  },
326
 
327
- documentGalleryEditToolbar: function () {
328
  var editing = this.state().get('editing');
329
  this.toolbar.set(new media.view.Toolbar({
330
  controller: this,
331
  items: {
332
  insert: {
333
  style: 'primary',
334
- text: editing ? l10n.updateDocumentGallery : l10n.insertDocumentGallery,
335
  priority: 80,
336
  requires: {library: true},
337
 
@@ -343,25 +400,28 @@
343
  state = controller.state();
344
 
345
  controller.close();
346
- //state.trigger( 'update', state.get('library') );
347
- wp.media.editor.insert(wp.media.gallery.shortcode(state.get('library')).string().replace(/^\[gallery/ig, '[dg').replace(/DGorderby/ig, 'orderby'));
348
 
349
  // Restore and reset the default state.
350
  controller.setState(controller.options.state);
351
  controller.reset();
 
 
 
352
  }
353
  }
354
  }
355
  }));
356
  },
357
 
358
- documentGalleryAddToolbar: function () {
359
  this.toolbar.set(new media.view.Toolbar({
360
  controller: this,
361
  items: {
362
  insert: {
363
  style: 'primary',
364
- text: l10n.addToDocumentGallery,
365
  priority: 80,
366
  requires: {selection: true},
367
 
@@ -371,15 +431,126 @@
371
  click: function () {
372
  var controller = this.controller,
373
  state = controller.state(),
374
- edit = controller.state('document-gallery-edit');
375
 
376
  edit.get('library').add(state.get('selection').models);
377
  state.trigger('reset');
378
- controller.setState('document-gallery-edit');
379
  }
380
  }
381
  }
382
  }));
383
  }
384
  });
385
- }(jQuery, _));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Cause gallery shortcode is hardcoded into WP core and has a lot of relations with Media Manager and other parts
3
+ * had to copy related code and adjust to the DG needs.
4
+ * Since WP core make use of shortcode tag to get (build) needed function name while handling shortcode (especially in visual editor),
5
+ * named all DG related variables and functions using prefix or suffix (depending on context) same as shortcode tag - dg
6
+ * As long as WP core has strange conditional statements for order and orderby attributes had to rename them by adding dg prefix.
7
+ *
8
+ */
9
  ( function ($, _) {
10
+ var l10n,
11
+ media = wp.media;
12
 
13
  // Link any localized strings.
14
  l10n = media.view.l10n = typeof _wpMediaViewsL10n === 'undefined' ? {} : _wpMediaViewsL10n;
15
  jQuery.extend(l10n, DGl10n);
16
 
17
+ // Based on code from /wp-includes/js/media-views.js WP 4.1
18
  /**
19
+ * wp.media.controller.dgEdit
20
  *
21
+ * A state for editing a dg's images and settings.
22
  *
23
  * @class
24
  * @augments wp.media.controller.Library
26
  * @augments Backbone.Model
27
  *
28
  * @param {object} [attributes] The attributes hash passed to the state.
29
+ * @param {string} [attributes.id=dg-edit] Unique identifier.
30
+ * @param {string} [attributes.title=Edit Document Gallery] Title for the state. Displays in the frame's title region.
31
+ * @param {wp.media.model.Attachments} [attributes.library] The collection of attachments in the dg.
32
  * If one is not supplied, an empty media.model.Selection collection is created.
33
  * @param {boolean} [attributes.multiple=false] Whether multi-select is enabled.
34
  * @param {boolean} [attributes.searchable=false] Whether the library is searchable.
35
  * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
36
  * @param {string|false} [attributes.content=browse] Initial mode for the content region.
37
  * @param {string|false} [attributes.toolbar=image-details] Initial mode for the toolbar region.
38
+ * @param {boolean} [attributes.describe=true] Whether to offer UI to describe attachments - e.g. captioning images in a dg.
39
  * @param {boolean} [attributes.displaySettings=true] Whether to show the attachment display settings interface.
40
  * @param {boolean} [attributes.dragInfo=true] Whether to show instructional text about the attachments being sortable.
41
  * @param {int} [attributes.idealColumnWidth=170] The ideal column width in pixels for attachments.
42
+ * @param {boolean} [attributes.editing=false] Whether the dg is being created, or editing an existing instance.
43
  * @param {int} [attributes.priority=60] The priority for the state link in the media menu.
44
  * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
45
  * Defaults to false for this state, because the library passed in *is* the selection.
46
  * @param {view} [attributes.AttachmentView] The single `Attachment` view to be used in the `Attachments`.
47
  * If none supplied, defaults to wp.media.view.Attachment.EditLibrary.
48
  */
49
+ media.controller.dgEdit = media.controller.Library.extend({
50
  defaults: {
51
+ id: 'dg-edit',
52
+ title: l10n.editdgTitle,
53
  multiple: false,
54
  searchable: false,
55
  sortable: true,
56
  display: false,
57
  content: 'browse',
58
+ toolbar: 'dg-edit',
59
  describe: true,
60
  displaySettings: true,
61
  dragInfo: true,
86
  var library = this.get('library');
87
 
88
  // Limit the library to images only.
89
+ //library.props.set( 'type', 'image' );
90
 
91
  // Watch for uploaded attachments.
92
  this.get('library').observe(wp.Uploader.queue);
93
 
94
+ this.frame.on('content:render:browse', this.dgSettings, this);
95
 
96
  media.controller.Library.prototype.activate.apply(this, arguments);
97
  },
103
  // Stop watching for uploaded attachments.
104
  this.get('library').unobserve(wp.Uploader.queue);
105
 
106
+ this.frame.off('content:render:browse', this.dgSettings, this);
107
 
108
  media.controller.Library.prototype.deactivate.apply(this, arguments);
109
  },
113
  *
114
  * @param browser
115
  */
116
+ dgSettings: function (browser) {
117
  if (!this.get('displaySettings')) {
118
  return;
119
  }
124
  return;
125
  }
126
 
127
+ library.dg = library.dg || new Backbone.Model();
128
 
129
  browser.sidebar.set({
130
+ dg: new media.view.Settings.dg({
131
  controller: this,
132
+ model: library.dg,
133
  priority: 40
134
  })
135
  });
146
  });
147
 
148
  /**
149
+ * A state for selecting more images to add to a dg.
150
  *
151
  * @class
152
  * @augments wp.media.controller.Library
154
  * @augments Backbone.Model
155
  *
156
  * @param {object} [attributes] The attributes hash passed to the state.
157
+ * @param {string} [attributes.id=dg-library] Unique identifier.
158
+ * @param {string} [attributes.title=Add to Document Gallery] Title for the state. Displays in the frame's title region.
159
  * @param {boolean} [attributes.multiple=add] Whether multi-select is enabled. @todo 'add' doesn't seem do anything special, and gets used as a boolean.
160
  * @param {wp.media.model.Attachments} [attributes.library] The attachments collection to browse.
161
  * If one is not supplied, a collection of all images will be created.
162
  * @param {boolean|string} [attributes.filterable=uploaded] Whether the library is filterable, and if so what filters should be shown.
163
  * Accepts 'all', 'uploaded', or 'unattached'.
164
+ * @param {string} [attributes.menu=dg] Initial mode for the menu region.
165
  * @param {string} [attributes.content=upload] Initial mode for the content region.
166
  * Overridden by persistent user setting if 'contentUserSetting' is true.
167
  * @param {string} [attributes.router=browse] Initial mode for the router region.
168
+ * @param {string} [attributes.toolbar=dg-add] Initial mode for the toolbar region.
169
  * @param {boolean} [attributes.searchable=true] Whether the library is searchable.
170
  * @param {boolean} [attributes.sortable=true] Whether the Attachments should be sortable. Depends on the orderby property being set to menuOrder on the attachments collection.
171
  * @param {boolean} [attributes.autoSelect=true] Whether an uploaded attachment should be automatically added to the selection.
172
  * @param {boolean} [attributes.contentUserSetting=true] Whether the content region's mode should be set and persisted per user.
173
  * @param {int} [attributes.priority=100] The priority for the state link in the media menu.
174
  * @param {boolean} [attributes.syncSelection=false] Whether the Attachments selection should be persisted from the last state.
175
+ * Defaults to false because for this state, because the library of the Edit Document Gallery state is the selection.
176
  */
177
+ media.controller.dgAdd = media.controller.Library.extend({
178
  defaults: _.defaults({
179
+ id: 'dg-library',
180
+ title: l10n.addTodgTitle,
181
  multiple: 'add',
182
  filterable: 'uploaded',
183
+ menu: 'dg',
184
+ toolbar: 'dg-add',
185
  priority: 100,
186
  syncSelection: false
187
  }, media.controller.Library.prototype.defaults),
202
  */
203
  activate: function () {
204
  var library = this.get('library'),
205
+ edit = this.frame.state('dg-edit').get('library');
206
 
207
  if (this.editLibrary && this.editLibrary !== edit)
208
  library.unobserve(this.editLibrary);
209
 
210
  // Accepts attachments that exist in the original library and
211
+ // that do not exist in dg's library.
212
  library.validator = function (attachment) {
213
  return !!this.mirroring.get(attachment.cid) && !edit.get(attachment.cid) && media.model.Selection.prototype.validator.apply(this, arguments);
214
  };
225
  });
226
 
227
  /**
228
+ * wp.media.view.Settings.dg
229
  *
230
  * @class
231
  * @augments wp.media.view.Settings
233
  * @augments wp.Backbone.View
234
  * @augments Backbone.View
235
  */
236
+ media.view.Settings.dg = media.view.Settings.extend({
237
+ update: function( key ) {
238
+ var value = this.model.get( key ),
239
+ $setting = this.$('[data-setting="' + key + '"]'),
240
+ $buttons, $value;
241
+
242
+ // Bail if we didn't find a matching setting.
243
+ if ( ! $setting.length ) {
244
+ return;
245
+ }
246
+
247
+ // Attempt to determine how the setting is rendered and update
248
+ // the selected value.
249
+
250
+ // Handle dropdowns.
251
+ if ( $setting.is('select') ) {
252
+ $value = $setting.find('[value="' + value + '"]');
253
+
254
+ if ( $value.length ) {
255
+ $setting.find('option').prop( 'selected', false );
256
+ $value.prop( 'selected', true );
257
+ } else {
258
+ // If we can't find the desired value, record what *is* selected.
259
+ this.model.set( key, $setting.find(':selected').val() );
260
+ }
261
+
262
+ // Handle button groups.
263
+ } else if ( $setting.hasClass('button-group') ) {
264
+ $buttons = $setting.find('button').removeClass('active');
265
+ $buttons.filter( '[value="' + value + '"]' ).addClass('active');
266
+
267
+ // Handle text inputs and textareas.
268
+ } else if ( $setting.is('input[type="text"], input[type="number"], textarea') ) {
269
+ if ( ! $setting.is(':focus') ) {
270
+ $setting.val( value );
271
+ }
272
+ // Handle checkboxes.
273
+ } else if ( $setting.is('input[type="checkbox"]') ) {
274
+ $setting.prop( 'checked', !! value && 'false' !== value );
275
+ }
276
+ },
277
+ className: 'collection-settings dg-settings',
278
+ template: media.template('dg-settings')
279
  });
280
 
281
  // supersede the default MediaFrame.Post view
282
+ // hint from https://gist.github.com/Fab1en/4586865
283
  var wpMediaFramePost = wp.media.view.MediaFrame.Post;
284
  wp.media.view.MediaFrame.Post = wpMediaFramePost.extend(
285
  {
287
  wpMediaFramePost.prototype.initialize.apply(this, arguments);
288
  this.states.add([
289
  new media.controller.Library({
290
+ id: 'dg',
291
+ title: l10n.dgMenuTitle,
292
  priority: 50,
293
+ toolbar: 'main-dg',
294
  filterable: 'all',
295
  multiple: 'add',
296
  editable: false,
299
  }),
300
 
301
  // Document Gallery states.
302
+ new media.controller.dgEdit({
303
  library: this.options.selection,
304
  editing: this.options.editing,
305
+ menu: 'dg'
306
  }),
307
 
308
+ new media.controller.dgAdd()
309
  ]);
310
 
311
+ this.on('menu:create:dg', this.createMenu, this);
312
+ this.on('toolbar:create:main-dg', this.createToolbar, this);
313
 
314
+ this.on('menu:render:dg', this.dgMenu, this);
315
+ this.on('toolbar:render:main-dg', this.maindgToolbar, this);
316
+ this.on('toolbar:render:dg-edit', this.dgEditToolbar, this);
317
+ this.on('toolbar:render:dg-add', this.dgAddToolbar, this);
318
  },
319
 
320
+ /**
321
+ * @param {wp.Backbone.View} view
322
+ */
323
+ dgMenu: function (view) {
324
  var lastState = this.lastState(),
325
  previous = lastState && lastState.id,
326
  frame = this;
327
 
328
  view.set({
329
  cancel: {
330
+ text: l10n.canceldgTitle,
331
  priority: 20,
332
  click: function () {
333
  if (previous) {
337
  }
338
 
339
  // Keep focus inside media modal
340
+ // after canceling a dg
341
  this.controller.modal.focusManager.focus();
342
  }
343
  },
351
  /**
352
  * @param {wp.Backbone.View} view
353
  */
354
+ maindgToolbar: function (view) {
355
  var controller = this;
356
 
357
  this.selectionStatusToolbar(view);
358
 
359
+ view.set('dg', {
360
  style: 'primary',
361
+ text: l10n.dgButton,
362
  priority: 60,
363
  requires: {selection: true},
364
 
365
  click: function () {
366
  var selection = controller.state().get('selection'),
367
+ edit = controller.state('dg-edit'),
368
  models = selection.models;
369
 
370
+ edit.set('library', new media.model.Selection(models, {
371
+ props: selection.props.toJSON(),
372
+ multiple: true
373
+ }));
374
 
375
+ this.controller.setState('dg-edit');
376
 
377
  // Keep focus inside media modal
378
+ // after jumping to dg view
379
  this.controller.modal.focusManager.focus();
380
  }
381
  });
382
  },
383
 
384
+ dgEditToolbar: function () {
385
  var editing = this.state().get('editing');
386
  this.toolbar.set(new media.view.Toolbar({
387
  controller: this,
388
  items: {
389
  insert: {
390
  style: 'primary',
391
+ text: editing ? l10n.updatedg : l10n.insertdg,
392
  priority: 80,
393
  requires: {library: true},
394
 
400
  state = controller.state();
401
 
402
  controller.close();
403
+ //state.trigger( 'update', state.get('library') ); // calling for workflow.state update, so just execute its contents
404
+ wp.media.editor.insert(wp.media.dg.shortcode(state.get('library')).string().replace(/\sdgorder=/ig, ' order=').replace(/\sdgorderby=/ig, ' orderby='));
405
 
406
  // Restore and reset the default state.
407
  controller.setState(controller.options.state);
408
  controller.reset();
409
+ if (typeof tinyMCE != 'undefined') {
410
+ tinyMCE.activeEditor.fire('dgUpdate');
411
+ }
412
  }
413
  }
414
  }
415
  }));
416
  },
417
 
418
+ dgAddToolbar: function () {
419
  this.toolbar.set(new media.view.Toolbar({
420
  controller: this,
421
  items: {
422
  insert: {
423
  style: 'primary',
424
+ text: l10n.addTodg,
425
  priority: 80,
426
  requires: {selection: true},
427
 
431
  click: function () {
432
  var controller = this.controller,
433
  state = controller.state(),
434
+ edit = controller.state('dg-edit');
435
 
436
  edit.get('library').add(state.get('selection').models);
437
  state.trigger('reset');
438
+ controller.setState('dg-edit');
439
  }
440
  }
441
  }
442
  }));
443
  }
444
  });
445
+
446
+ // Based on code from /wp-includes/js/media-editor.js WP 4.1
447
+
448
+ wp.media._dgDefaults = {
449
+ id: wp.media.view.settings.post && wp.media.view.settings.post.id,
450
+ columns: dgDefaults.columns,
451
+ fancy: dgDefaults.fancy,
452
+ relation: dgDefaults.relation,
453
+ limit: dgDefaults.limit,
454
+ mime_types: dgDefaults.mime_types,
455
+ post_status: dgDefaults.post_status,
456
+ post_type: dgDefaults.post_type,
457
+ attachment_pg: dgDefaults.attachment_pg,
458
+ descriptions: dgDefaults.descriptions,
459
+ new_window: dgDefaults.new_window,
460
+ paginate: dgDefaults.paginate,
461
+ dgorder: dgDefaults.order,
462
+ dgorderby: dgDefaults.orderby
463
+ };
464
+
465
+ if (wp.media.view.settings.dgDefaults) {
466
+ wp.media.dgDefaults = _.extend({}, wp.media._dgDefaults, wp.media.view.settings.dgDefaults);
467
+ } else {
468
+ wp.media.dgDefaults = wp.media._dgDefaults;
469
+ }
470
+
471
+ wp.media.dg = new wp.media.collection({
472
+ tag: 'dg',
473
+ //type : 'image',
474
+ editTitle: wp.media.view.l10n.editdgTitle,
475
+ defaults: wp.media.dgDefaults,
476
+
477
+ setDefaults: function (attrs) {
478
+ var self = this, changed = !_.isEqual(wp.media.dgDefaults, wp.media._dgDefaults);
479
+ _.each(this.defaults, function (value, key) {
480
+ attrs[key] = self.coerce(attrs, key);
481
+ if (value === attrs[key] && ( !changed || value === wp.media._dgDefaults[key] )) {
482
+ delete attrs[key];
483
+ }
484
+ });
485
+ return attrs;
486
+ }
487
+ });
488
+ }(jQuery, _));
489
+
490
+ // Based on code from /wp-includes/js/mce-view.js WP 4.2
491
+ /*
492
+ * The WordPress core TinyMCE views.
493
+ * View for the dg shortcode.
494
+ */
495
+ (function (window, views, media, $) {
496
+ var dg;
497
+
498
+ base = {
499
+ state: [],
500
+
501
+ edit: function (text, update) {
502
+ // currently the shortcode *must* include ids attribute and may include any of the listed attributes to be editable
503
+ if ( text.search( /\sids\s*=/gi ) == -1 || text.search( /\s(?!(?:ids|attachment_pg|columns|new_window|descriptions|fancy|orderby|order|paginate|limit)\s*=)[\w\-]+\s*=/gi ) > -1 ) {
504
+ tinyMCE.activeEditor.windowManager.alert( DGl10n.unfitSCalert );
505
+ } else {
506
+ var type = this.type,
507
+ frame = media[type].edit(text.replace(/\sorder\s*=/ig, ' dgorder=').replace(/\sorderby\s*=/ig, ' dgorderby='));
508
+
509
+ this.pausePlayers && this.pausePlayers();
510
+
511
+ _.each(this.state, function (state) {
512
+ frame.state(state).on('update', function (selection) {
513
+ update(media[type].shortcode(selection).string(), type === 'dg');
514
+ });
515
+ });
516
+
517
+ frame.on('close', function () {
518
+ frame.detach();
519
+ });
520
+
521
+ frame.open();
522
+ }
523
+ }
524
+ };
525
+
526
+ dg = _.extend({}, base, {
527
+ state: ['dg-edit'],
528
+ template: media.template('editor-dg'),
529
+
530
+ initialize: function () {
531
+ var attachments = media.dg.attachments(this.shortcode, media.view.settings.post.id),
532
+ attrs = this.shortcode.attrs.named,
533
+ sc = this.text,
534
+ atts = {},
535
+ self = this;
536
+
537
+ for (prop in attrs) {
538
+ if (sc.indexOf(' ' + prop + '=') > -1) {
539
+ atts[prop] = attrs[prop];
540
+ }
541
+ }
542
+ if (sc.indexOf(' dgorderby=') > -1) {
543
+ atts['orderby'] = attrs['dgorderby'];
544
+ }
545
+ if (sc.indexOf(' dgorder=') > -1) {
546
+ atts['order'] = attrs['dgorder'];
547
+ }
548
+ self.render('<div data-shortcode="' +
549
+ encodeURIComponent(JSON.stringify(atts)) +
550
+ '"><div class="loading-placeholder"><div class="dashicons dashicons-admin-media"></div><div class="wpview-loading"><ins></ins></div></div></div>');
551
+ }
552
+ });
553
+
554
+ views.register('dg', _.extend({}, dg));
555
+
556
+ })(window, window.wp.mce.views, window.wp.media, window.jQuery);
assets/js/media_manager.min.js CHANGED
@@ -1,10 +1,15 @@
1
- (function(g,f){var a=wp.media;l10n=a.view.l10n="undefined"===typeof _wpMediaViewsL10n?{}:_wpMediaViewsL10n;jQuery.extend(l10n,DGl10n);a.controller.DocumentGalleryEdit=a.controller.Library.extend({defaults:{id:"document-gallery-edit",title:l10n.editDocumentGalleryTitle,multiple:!1,searchable:!1,sortable:!0,display:!1,content:"browse",toolbar:"document-gallery-edit",describe:!0,displaySettings:!0,dragInfo:!0,idealColumnWidth:170,editing:!1,priority:60,syncSelection:!1},initialize:function(){this.get("library")||
2
- this.set("library",new a.model.Selection);this.get("AttachmentView")||this.set("AttachmentView",a.view.Attachment.EditLibrary);a.controller.Library.prototype.initialize.apply(this,arguments)},activate:function(){this.get("library");this.get("library").observe(wp.Uploader.queue);this.frame.on("content:render:browse",this.gallerySettings,this);a.controller.Library.prototype.activate.apply(this,arguments)},deactivate:function(){this.get("library").unobserve(wp.Uploader.queue);this.frame.off("content:render:browse",
3
- this.gallerySettings,this);a.controller.Library.prototype.deactivate.apply(this,arguments)},gallerySettings:function(c){if(this.get("displaySettings")){var b=this.get("library");b&&c&&(b.gallery=b.gallery||new Backbone.Model,c.sidebar.set({document_gallery:new a.view.Settings.DocumentGallery({controller:this,model:b.gallery,priority:40})}),c.toolbar.set("reverse",{text:l10n.reverseOrder,priority:80,click:function(){b.reset(b.toArray().reverse())}}))}}});a.controller.DocumentGalleryAdd=a.controller.Library.extend({defaults:f.defaults({id:"document-gallery-library",
4
- title:l10n.addToDocumentGalleryTitle,multiple:"add",filterable:"uploaded",menu:"document-gallery",toolbar:"document-gallery-add",priority:100,syncSelection:!1},a.controller.Library.prototype.defaults),initialize:function(){this.get("library")||this.set("library",a.query());a.controller.Library.prototype.initialize.apply(this,arguments)},activate:function(){var c=this.get("library"),b=this.frame.state("document-gallery-edit").get("library");this.editLibrary&&this.editLibrary!==b&&c.unobserve(this.editLibrary);
5
- c.validator=function(c){return!!this.mirroring.get(c.cid)&&!b.get(c.cid)&&a.model.Selection.prototype.validator.apply(this,arguments)};c.reset(c.mirroring.models,{silent:!0});c.observe(b);this.editLibrary=b;a.controller.Library.prototype.activate.apply(this,arguments)}});a.view.Settings.DocumentGallery=a.view.Settings.extend({className:"collection-settings gallery-settings document-gallery-settings",template:a.template("document-gallery-settings")});var d=wp.media.view.MediaFrame.Post;wp.media.view.MediaFrame.Post=
6
- d.extend({initialize:function(){d.prototype.initialize.apply(this,arguments);this.states.add([new a.controller.Library({id:"document-gallery",title:l10n.documentGalleryMenuTitle,priority:50,toolbar:"main-document-gallery",filterable:"all",multiple:"add",editable:!1,library:a.query(this.options.library)}),new a.controller.DocumentGalleryEdit({library:this.options.selection,editing:this.options.editing,menu:"document-gallery"}),new a.controller.DocumentGalleryAdd]);this.on("menu:create:document-gallery",
7
- this.createMenu,this);this.on("toolbar:create:main-document-gallery",this.createToolbar,this);this.on("menu:render:document-gallery",this.documentGalleryMenu,this);this.on("toolbar:render:main-document-gallery",this.mainDocumentGalleryToolbar,this);this.on("toolbar:render:document-gallery-edit",this.documentGalleryEditToolbar,this);this.on("toolbar:render:document-gallery-add",this.documentGalleryAddToolbar,this)},documentGalleryMenu:function(c){var b=this.lastState(),e=b&&b.id,d=this;c.set({cancel:{text:l10n.cancelDocumentGalleryTitle,
8
- priority:20,click:function(){e?d.setState(e):d.close();this.controller.modal.focusManager.focus()}},separateCancel:new a.View({className:"separator",priority:40})})},mainDocumentGalleryToolbar:function(a){var b=this;this.selectionStatusToolbar(a);a.set("document-gallery",{style:"primary",text:l10n.documentGalleryButton,priority:60,requires:{selection:!0},click:function(){var a=b.state().get("selection");b.state("document-gallery-edit").set("library",a);this.controller.setState("document-gallery-edit");
9
- this.controller.modal.focusManager.focus()}})},documentGalleryEditToolbar:function(){var c=this.state().get("editing");this.toolbar.set(new a.view.Toolbar({controller:this,items:{insert:{style:"primary",text:c?l10n.updateDocumentGallery:l10n.insertDocumentGallery,priority:80,requires:{library:!0},click:function(){var a=this.controller,c=a.state();a.close();wp.media.editor.insert(wp.media.gallery.shortcode(c.get("library")).string().replace(/^\[gallery/ig,"[dg").replace(/DGorderby/ig,"orderby"));a.setState(a.options.state);
10
- a.reset()}}}}))},documentGalleryAddToolbar:function(){this.toolbar.set(new a.view.Toolbar({controller:this,items:{insert:{style:"primary",text:l10n.addToDocumentGallery,priority:80,requires:{selection:!0},click:function(){var a=this.controller,b=a.state();a.state("document-gallery-edit").get("library").add(b.get("selection").models);b.trigger("reset");a.setState("document-gallery-edit")}}}}))}})})(jQuery,_);
 
 
 
 
 
1
+ (function(h,g){var d,a=wp.media;d=a.view.l10n="undefined"===typeof _wpMediaViewsL10n?{}:_wpMediaViewsL10n;jQuery.extend(d,DGl10n);a.controller.dgEdit=a.controller.Library.extend({defaults:{id:"dg-edit",title:d.editdgTitle,multiple:!1,searchable:!1,sortable:!0,display:!1,content:"browse",toolbar:"dg-edit",describe:!0,displaySettings:!0,dragInfo:!0,idealColumnWidth:170,editing:!1,priority:60,syncSelection:!1},initialize:function(){this.get("library")||this.set("library",new a.model.Selection);this.get("AttachmentView")||
2
+ this.set("AttachmentView",a.view.Attachment.EditLibrary);a.controller.Library.prototype.initialize.apply(this,arguments)},activate:function(){this.get("library");this.get("library").observe(wp.Uploader.queue);this.frame.on("content:render:browse",this.dgSettings,this);a.controller.Library.prototype.activate.apply(this,arguments)},deactivate:function(){this.get("library").unobserve(wp.Uploader.queue);this.frame.off("content:render:browse",this.dgSettings,this);a.controller.Library.prototype.deactivate.apply(this,
3
+ arguments)},dgSettings:function(b){if(this.get("displaySettings")){var c=this.get("library");c&&b&&(c.dg=c.dg||new Backbone.Model,b.sidebar.set({dg:new a.view.Settings.dg({controller:this,model:c.dg,priority:40})}),b.toolbar.set("reverse",{text:d.reverseOrder,priority:80,click:function(){c.reset(c.toArray().reverse())}}))}}});a.controller.dgAdd=a.controller.Library.extend({defaults:g.defaults({id:"dg-library",title:d.addTodgTitle,multiple:"add",filterable:"uploaded",menu:"dg",toolbar:"dg-add",priority:100,
4
+ syncSelection:!1},a.controller.Library.prototype.defaults),initialize:function(){this.get("library")||this.set("library",a.query());a.controller.Library.prototype.initialize.apply(this,arguments)},activate:function(){var b=this.get("library"),c=this.frame.state("dg-edit").get("library");this.editLibrary&&this.editLibrary!==c&&b.unobserve(this.editLibrary);b.validator=function(b){return!!this.mirroring.get(b.cid)&&!c.get(b.cid)&&a.model.Selection.prototype.validator.apply(this,arguments)};b.reset(b.mirroring.models,
5
+ {silent:!0});b.observe(c);this.editLibrary=c;a.controller.Library.prototype.activate.apply(this,arguments)}});a.view.Settings.dg=a.view.Settings.extend({update:function(b){var c=this.model.get(b),a=this.$('[data-setting="'+b+'"]');a.length&&(a.is("select")?(c=a.find('[value="'+c+'"]'),c.length?(a.find("option").prop("selected",!1),c.prop("selected",!0)):this.model.set(b,a.find(":selected").val())):a.hasClass("button-group")?(b=a.find("button").removeClass("active"),b.filter('[value="'+c+'"]').addClass("active")):
6
+ a.is('input[type="text"], input[type="number"], textarea')?a.is(":focus")||a.val(c):a.is('input[type="checkbox"]')&&a.prop("checked",!!c&&"false"!==c))},className:"collection-settings dg-settings",template:a.template("dg-settings")});var e=wp.media.view.MediaFrame.Post;wp.media.view.MediaFrame.Post=e.extend({initialize:function(){e.prototype.initialize.apply(this,arguments);this.states.add([new a.controller.Library({id:"dg",title:d.dgMenuTitle,priority:50,toolbar:"main-dg",filterable:"all",multiple:"add",
7
+ editable:!1,library:a.query(this.options.library)}),new a.controller.dgEdit({library:this.options.selection,editing:this.options.editing,menu:"dg"}),new a.controller.dgAdd]);this.on("menu:create:dg",this.createMenu,this);this.on("toolbar:create:main-dg",this.createToolbar,this);this.on("menu:render:dg",this.dgMenu,this);this.on("toolbar:render:main-dg",this.maindgToolbar,this);this.on("toolbar:render:dg-edit",this.dgEditToolbar,this);this.on("toolbar:render:dg-add",this.dgAddToolbar,this)},dgMenu:function(b){var c=
8
+ this.lastState(),f=c&&c.id,e=this;b.set({cancel:{text:d.canceldgTitle,priority:20,click:function(){f?e.setState(f):e.close();this.controller.modal.focusManager.focus()}},separateCancel:new a.View({className:"separator",priority:40})})},maindgToolbar:function(b){var c=this;this.selectionStatusToolbar(b);b.set("dg",{style:"primary",text:d.dgButton,priority:60,requires:{selection:!0},click:function(){var b=c.state().get("selection");c.state("dg-edit").set("library",new a.model.Selection(b.models,{props:b.props.toJSON(),
9
+ multiple:!0}));this.controller.setState("dg-edit");this.controller.modal.focusManager.focus()}})},dgEditToolbar:function(){var b=this.state().get("editing");this.toolbar.set(new a.view.Toolbar({controller:this,items:{insert:{style:"primary",text:b?d.updatedg:d.insertdg,priority:80,requires:{library:!0},click:function(){var c=this.controller,b=c.state();c.close();wp.media.editor.insert(wp.media.dg.shortcode(b.get("library")).string().replace(/\sdgorder=/ig," order=").replace(/\sdgorderby=/ig," orderby="));
10
+ c.setState(c.options.state);c.reset();"undefined"!=typeof tinyMCE&&tinyMCE.activeEditor.fire("dgUpdate")}}}}))},dgAddToolbar:function(){this.toolbar.set(new a.view.Toolbar({controller:this,items:{insert:{style:"primary",text:d.addTodg,priority:80,requires:{selection:!0},click:function(){var b=this.controller,c=b.state();b.state("dg-edit").get("library").add(c.get("selection").models);c.trigger("reset");b.setState("dg-edit")}}}}))}});wp.media._dgDefaults={id:wp.media.view.settings.post&&wp.media.view.settings.post.id,
11
+ columns:dgDefaults.columns,fancy:dgDefaults.fancy,relation:dgDefaults.relation,limit:dgDefaults.limit,mime_types:dgDefaults.mime_types,post_status:dgDefaults.post_status,post_type:dgDefaults.post_type,attachment_pg:dgDefaults.attachment_pg,descriptions:dgDefaults.descriptions,new_window:dgDefaults.new_window,paginate:dgDefaults.paginate,dgorder:dgDefaults.order,dgorderby:dgDefaults.orderby};wp.media.dgDefaults=wp.media.view.settings.dgDefaults?g.extend({},wp.media._dgDefaults,wp.media.view.settings.dgDefaults):
12
+ wp.media._dgDefaults;wp.media.dg=new wp.media.collection({tag:"dg",editTitle:wp.media.view.l10n.editdgTitle,defaults:wp.media.dgDefaults,setDefaults:function(b){var c=this,a=!g.isEqual(wp.media.dgDefaults,wp.media._dgDefaults);g.each(this.defaults,function(d,e){b[e]=c.coerce(b,e);d!==b[e]||a&&d!==wp.media._dgDefaults[e]||delete b[e]});return b}})})(jQuery,_);
13
+ (function(h,g,d,a){base={state:[],edit:function(a,b){if(-1==a.search(/\sids\s*=/gi)||-1<a.search(/\s(?!(?:ids|attachment_pg|columns|new_window|descriptions|fancy|orderby|order|paginate|limit)\s*=)[\w\-]+\s*=/gi))tinyMCE.activeEditor.windowManager.alert(DGl10n.unfitSCalert);else{var c=this.type,f=d[c].edit(a.replace(/\sorder\s*=/ig," dgorder=").replace(/\sorderby\s*=/ig," dgorderby="));this.pausePlayers&&this.pausePlayers();_.each(this.state,function(a){f.state(a).on("update",function(a){b(d[c].shortcode(a).string(),
14
+ "dg"===c)})});f.on("close",function(){f.detach()});f.open()}}};h=_.extend({},base,{state:["dg-edit"],template:d.template("editor-dg"),initialize:function(){d.dg.attachments(this.shortcode,d.view.settings.post.id);var a=this.shortcode.attrs.named,b=this.text,c={};for(prop in a)-1<b.indexOf(" "+prop+"=")&&(c[prop]=a[prop]);-1<b.indexOf(" dgorderby=")&&(c.orderby=a.dgorderby);-1<b.indexOf(" dgorder=")&&(c.order=a.dgorder);this.render('<div data-shortcode="'+encodeURIComponent(JSON.stringify(c))+'"><div class="loading-placeholder"><div class="dashicons dashicons-admin-media"></div><div class="wpview-loading"><ins></ins></div></div></div>')}});
15
+ g.register("dg",_.extend({},h))})(window,window.wp.mce.views,window.wp.media,window.jQuery);
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: 3.5.4
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.5.4' );
16
 
17
  // define helper paths & URLs
18
  define( 'DG_BASENAME', plugin_basename( __FILE__ ) );
@@ -28,8 +28,7 @@ $dg_options = get_option( DG_OPTION_NAME, null );
28
 
29
  // core functionality
30
  include_once DG_PATH . 'inc/class-document-gallery.php';
31
-
32
- // DG general utility functions
33
  include_once DG_PATH . 'inc/class-util.php';
34
 
35
  // logging functionality
@@ -51,12 +50,15 @@ add_action( 'plugins_loaded', array( 'DocumentGallery', 'loadTextDomain' ) );
51
 
52
  // cleanup cached data when thumbed attachment deleted
53
  include_once DG_PATH . 'inc/class-thumber.php';
54
- add_action( 'delete_attachment', array( 'DG_Thumber', 'deleteThumbMeta' ) );
55
 
56
  if ( is_admin() ) {
57
  // admin house keeping
58
  include_once DG_PATH . 'admin/class-admin.php';
59
 
 
 
 
60
  // add links to plugin index
61
  add_filter( 'plugin_action_links_' . DG_BASENAME, array( 'DG_Admin', 'addSettingsLink' ) );
62
  add_filter( 'plugin_row_meta', array( 'DG_Admin', 'addDonateLink' ), 10, 2 );
@@ -77,9 +79,6 @@ if ( is_admin() ) {
77
  if ( DG_Admin::doRegisterSettings() ) {
78
  add_action( 'admin_init', array( 'DG_Admin', 'registerSettings' ) );
79
  }
80
-
81
- add_action( 'wp_ajax_dg_generate_icons', array( 'DocumentGallery', 'ajaxGenerateIcons' ) );
82
- add_action( 'wp_ajax_nopriv_dg_generate_icons', array( 'DocumentGallery', 'ajaxGenerateIcons' ) );
83
  } else {
84
  // styling for gallery
85
  if ( apply_filters( 'dg_use_default_gallery_style', true ) ) {
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: 4.0
9
  Author: Dan Rossiter
10
  Author URI: http://danrossiter.org/
11
  License: GPLv2
12
  Text Domain: document-gallery
13
  */
14
 
15
+ define( 'DG_VERSION', '4.0' );
16
 
17
  // define helper paths & URLs
18
  define( 'DG_BASENAME', plugin_basename( __FILE__ ) );
28
 
29
  // core functionality
30
  include_once DG_PATH . 'inc/class-document-gallery.php';
31
+ include_once DG_PATH . 'inc/class-thumb.php';
 
32
  include_once DG_PATH . 'inc/class-util.php';
33
 
34
  // logging functionality
50
 
51
  // cleanup cached data when thumbed attachment deleted
52
  include_once DG_PATH . 'inc/class-thumber.php';
53
+ add_action( 'delete_attachment', array( 'DG_Thumb', 'cleanupAttachmentMeta' ) );
54
 
55
  if ( is_admin() ) {
56
  // admin house keeping
57
  include_once DG_PATH . 'admin/class-admin.php';
58
 
59
+ // AJAX handling
60
+ include_once DG_PATH . 'admin/class-ajax-handler.php';
61
+
62
  // add links to plugin index
63
  add_filter( 'plugin_action_links_' . DG_BASENAME, array( 'DG_Admin', 'addSettingsLink' ) );
64
  add_filter( 'plugin_row_meta', array( 'DG_Admin', 'addDonateLink' ), 10, 2 );
79
  if ( DG_Admin::doRegisterSettings() ) {
80
  add_action( 'admin_init', array( 'DG_Admin', 'registerSettings' ) );
81
  }
 
 
 
82
  } else {
83
  // styling for gallery
84
  if ( apply_filters( 'dg_use_default_gallery_style', true ) ) {
inc/class-document-gallery.php CHANGED
@@ -56,7 +56,7 @@ class DocumentGallery {
56
  // need AJAX URL variable in frontend
57
  ?>
58
  <script type="text/javascript">
59
- var ajaxurl = '<?php echo admin_url('admin-ajax.php'); ?>';
60
  </script>
61
  <?php
62
  }
@@ -76,27 +76,6 @@ class DocumentGallery {
76
  * HELPER FUNCTIONS
77
  *=========================================================================*/
78
 
79
- /**
80
- * Accepts AJAX request containing list of IDs to be generated and returned.
81
- * Returns associative array mapping ID to thumbnail URL for all icons that were generated,
82
- * skipping any that could not be processed.
83
- */
84
- public static function ajaxGenerateIcons() {
85
- $ret = array();
86
-
87
- if ( array_key_exists( 'ids', $_REQUEST ) ) {
88
- foreach ( $_REQUEST['ids'] as $id ) {
89
- // only return URL if different from default -- default image is already displayed on the client side
90
- $url = DG_Thumber::getThumbnail( $id );
91
- if ( $url !== DG_Thumber::getDefaultThumbnail( $id ) ) {
92
- $ret[$id] = $url;
93
- }
94
- }
95
- }
96
-
97
- wp_send_json($ret);
98
- }
99
-
100
  /**
101
  * @param int $blog ID of the blog to be retrieved in multisite env.
102
  *
@@ -207,16 +186,17 @@ class DocumentGallery {
207
  * @return string The local time in the WP date/time format.
208
  */
209
  public static function localDateTimeFromTimestamp( $timestamp ) {
210
- static $gmt_offet = null;
211
  static $wp_date_format = null;
212
  static $wp_time_format = null;
213
- if ( is_null( $gmt_offet ) ) {
214
- $gmt_offet = get_option( 'gmt_offset' );
215
  $wp_date_format = get_option( 'date_format' );
216
  $wp_time_format = get_option( 'time_format' );
217
  }
218
 
219
- 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>';
 
220
  }
221
 
222
  /**
56
  // need AJAX URL variable in frontend
57
  ?>
58
  <script type="text/javascript">
59
+ ajaxurl = typeof(ajaxurl) !== 'string' ? '<?php echo admin_url( 'admin-ajax.php' ); ?>' : ajaxurl;
60
  </script>
61
  <?php
62
  }
76
  * HELPER FUNCTIONS
77
  *=========================================================================*/
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  /**
80
  * @param int $blog ID of the blog to be retrieved in multisite env.
81
  *
186
  * @return string The local time in the WP date/time format.
187
  */
188
  public static function localDateTimeFromTimestamp( $timestamp ) {
189
+ static $gmt_offset = null;
190
  static $wp_date_format = null;
191
  static $wp_time_format = null;
192
+ if ( is_null( $gmt_offset ) ) {
193
+ $gmt_offset = get_option( 'gmt_offset' );
194
  $wp_date_format = get_option( 'date_format' );
195
  $wp_time_format = get_option( 'time_format' );
196
  }
197
 
198
+ return '<span class="nowrap">' . date_i18n( $wp_date_format, $timestamp + $gmt_offset * 3600 ) . '</span> ' .
199
+ '<span class="nowrap">' . date_i18n( $wp_time_format, $timestamp + $gmt_offset * 3600 ) . '</span>';
200
  }
201
 
202
  /**
inc/class-document.php CHANGED
@@ -9,15 +9,15 @@ defined( 'WPINC' ) OR exit;
9
  class DG_Document {
10
 
11
  /*==========================================================================
12
- * PRIVATE FIELDS
13
- *=========================================================================*/
14
 
15
  // general document data
16
  private $description, $gallery, $ID, $link, $title, $title_attribute, $path, $extension, $size;
17
 
18
  /*==========================================================================
19
- * INIT GALLERY
20
- *=========================================================================*/
21
 
22
  /**
23
  * Constructs instance of Document.
@@ -39,12 +39,20 @@ class DG_Document {
39
  $this->path = get_attached_file( $attachment->ID );
40
  $wp_filetype = wp_check_filetype_and_ext( $this->path, basename( $this->path ) );
41
  $this->extension = $wp_filetype['ext'];
42
- $this->size = size_format( filesize( $this->path ) );
 
 
 
 
 
 
 
 
43
  }
44
 
45
  /*==========================================================================
46
- * OUTPUT HTML STRING
47
- *=========================================================================*/
48
 
49
  /**
50
  * Returns HTML representing this Document.
@@ -54,29 +62,30 @@ class DG_Document {
54
  */
55
  public function __toString() {
56
  include_once DG_PATH . 'inc/class-thumber.php';
57
- $options = DG_Thumber::getOptions();
58
 
59
- $thumb = null;
60
  $data = '';
61
  $description = '';
62
  $target = $this->gallery->openLinkInNewWindow() ? '_blank' : '_self';
63
 
64
  if ( $this->gallery->useFancyThumbs() ) {
65
- if ( array_key_exists( $this->ID, $options['thumbs'] ) ) {
66
- // icon has already been generated so include it in generated gallery
67
- $thumb = DG_Thumber::getThumbnail( $this->ID, 1, false );
 
 
 
68
  } else {
69
  // include a data-* attribute for client side to asynchronously request icon after gallery load
70
- $data = ' data-dg-id="' . $this->ID . '"';
71
  }
72
  }
73
 
74
- if ( is_null($thumb) ) {
75
  $thumb = DG_Thumber::getDefaultThumbnail( $this->ID );
76
  }
77
 
78
- $repl = array( $this->link, $thumb, $this->title_attribute, $this->title, $target, $this->extension, $this->size, $this->path );
79
- $find = array( '%link%', '%img%', '%title_attribute%', '%title%', '%target%', '%extension%', '%size%', '%path%' );
80
 
81
  // if descriptions then add filterable tag and value to replaced tag
82
  if ( $this->gallery->useDescriptions() ) {
@@ -86,17 +95,16 @@ class DG_Document {
86
  }
87
 
88
  $doc_icon =
89
- ' <div class="document-icon"'. $data . '>' . PHP_EOL .
90
- ' <a href="%link%" target="%target%"><img src="%img%" title="%title_attribute%" alt="%title_attribute%" /><br>%title%</a>' . PHP_EOL .
 
 
 
91
  ' </div>' . PHP_EOL .
92
  $description;
93
 
94
  // allow developers to filter icon output
95
- $doc_icon = apply_filters(
96
- 'dg_icon_template',
97
- $doc_icon,
98
- $this->gallery->useDescriptions(),
99
- $this->ID );
100
 
101
  return str_replace( $find, $repl, $doc_icon );
102
  }
9
  class DG_Document {
10
 
11
  /*==========================================================================
12
+ * PRIVATE FIELDS
13
+ *=========================================================================*/
14
 
15
  // general document data
16
  private $description, $gallery, $ID, $link, $title, $title_attribute, $path, $extension, $size;
17
 
18
  /*==========================================================================
19
+ * INIT GALLERY
20
+ *=========================================================================*/
21
 
22
  /**
23
  * Constructs instance of Document.
39
  $this->path = get_attached_file( $attachment->ID );
40
  $wp_filetype = wp_check_filetype_and_ext( $this->path, basename( $this->path ) );
41
  $this->extension = $wp_filetype['ext'];
42
+ $size = @filesize( $this->path );
43
+ $this->size = ($size !== false) ? size_format( $size ) : 0;
44
+ }
45
+
46
+ /**
47
+ * @return int The attachment ID.
48
+ */
49
+ public function getId() {
50
+ return $this->ID;
51
  }
52
 
53
  /*==========================================================================
54
+ * OUTPUT HTML STRING
55
+ *=========================================================================*/
56
 
57
  /**
58
  * Returns HTML representing this Document.
62
  */
63
  public function __toString() {
64
  include_once DG_PATH . 'inc/class-thumber.php';
 
65
 
 
66
  $data = '';
67
  $description = '';
68
  $target = $this->gallery->openLinkInNewWindow() ? '_blank' : '_self';
69
 
70
  if ( $this->gallery->useFancyThumbs() ) {
71
+ $thumb_obj = DG_Thumb::getThumb( $this->ID );
72
+ if ( ! is_null( $thumb_obj ) ) {
73
+ if ( $thumb_obj->isSuccess() ) {
74
+ // icon has already been generated so include it in generated gallery
75
+ $thumb = $thumb_obj->getUrl();
76
+ }
77
  } else {
78
  // include a data-* attribute for client side to asynchronously request icon after gallery load
79
+ $data = 'data-id="' . $this->ID . '"';
80
  }
81
  }
82
 
83
+ if ( ! isset( $thumb ) ) {
84
  $thumb = DG_Thumber::getDefaultThumbnail( $this->ID );
85
  }
86
 
87
+ $repl = array( $this->link, $thumb, $this->title_attribute, $this->title, $target, $this->extension, $this->size, $this->path, $data );
88
+ $find = array( '%link%', '%img%', '%title_attribute%', '%title%', '%target%', '%extension%', '%size%', '%path%', '%data%' );
89
 
90
  // if descriptions then add filterable tag and value to replaced tag
91
  if ( $this->gallery->useDescriptions() ) {
95
  }
96
 
97
  $doc_icon =
98
+ ' <div class="document-icon">' . PHP_EOL .
99
+ ' <a href="%link%" target="%target%">' . PHP_EOL .
100
+ ' <img src="%img%" title="%title_attribute%" alt="%title_attribute%" %data%/>' . PHP_EOL .
101
+ ' <span class="title">%title%</span>' . PHP_EOL .
102
+ ' </a>' . PHP_EOL .
103
  ' </div>' . PHP_EOL .
104
  $description;
105
 
106
  // allow developers to filter icon output
107
+ $doc_icon = apply_filters( 'dg_icon_template', $doc_icon, $this->gallery->useDescriptions(), $this->ID );
 
 
 
 
108
 
109
  return str_replace( $find, $repl, $doc_icon );
110
  }
inc/class-gallery-sanitization.php ADDED
@@ -0,0 +1,496 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ defined( 'WPINC' ) OR exit;
3
+
4
+ DG_GallerySanitization::init();
5
+
6
+ /**
7
+ * Holds data specific to a given document gallery.
8
+ *
9
+ * @author drossiter
10
+ */
11
+ class DG_GallerySanitization {
12
+
13
+ /*==========================================================================
14
+ * PRIVATE FIELDS
15
+ *=========================================================================*/
16
+
17
+ private static $unary_err, $binary_err;
18
+
19
+ /**
20
+ * @return string The unary error format string.
21
+ */
22
+ public static function getUnaryErr() {
23
+ return self::$unary_err;
24
+ }
25
+
26
+ /**
27
+ * @return string The binary error format string.
28
+ */
29
+ public static function getBinaryErr() {
30
+ return self::$binary_err;
31
+ }
32
+
33
+ /*==========================================================================
34
+ * INIT SANITIZATION
35
+ *=========================================================================*/
36
+
37
+ /**
38
+ * Initializes static values for this class.
39
+ */
40
+ public static function init() {
41
+ if ( ! isset( self::$unary_err ) ) {
42
+ self::$unary_err = __( 'The %s value entered, "%s", is not valid.', 'document-gallery' );
43
+ self::$binary_err = __( 'The %s parameter may only be "%s" or "%s." You entered "%s."', 'document-gallery' );
44
+ }
45
+ }
46
+
47
+ /*==========================================================================
48
+ * SANITIZATION
49
+ *=========================================================================*/
50
+
51
+ /**
52
+ * Sanitize the given key/value pair, passing any error to $errs if given.
53
+ *
54
+ * @param string $key The key to reference the current value in the defaults array.
55
+ * @param mixed $value The value to be sanitized.
56
+ * @param array $errs The array of errors, which will be appended with any errors found.
57
+ *
58
+ * @return mixed The sanitized value, falling back to the current default value when invalid value given.
59
+ */
60
+ public static function sanitizeParameter( $key, $value, &$errs = null ) {
61
+ // all sanitize methods must be in the following form: sanitize<UpperCamelCaseKey>
62
+ $funct = $key;
63
+ $funct[0] = strtoupper( $funct[0] );
64
+ $funct = 'sanitize' . preg_replace_callback( '/_([a-z])/', array( __CLASS__, 'secondCharToUpper' ), $funct );
65
+
66
+ $callable = array( __CLASS__, $funct );
67
+
68
+ // avoid looking for method beforehand unless we're running in debug mode -- expensive call
69
+ if ( DG_Logger::logEnabled() && ! method_exists( __CLASS__, $funct ) ) {
70
+ DG_Logger::writeLog(
71
+ DG_LogLevel::Error,
72
+ __( 'Attempted to call invalid function: ', 'document-gallery' ) . implode( '::', $callable ),
73
+ true );
74
+ }
75
+
76
+ // call param-specific sanitization
77
+ $ret = call_user_func_array( $callable, array( $value, &$err ) );
78
+
79
+ // check for error and return default
80
+ if ( isset( $err ) ) {
81
+ $defaults = DG_Gallery::getOptions();
82
+ $ret = $defaults[ $key ];
83
+
84
+ if ( ! is_null($errs) ) {
85
+ $errs[ $key ] = $err;
86
+ }
87
+ }
88
+
89
+ return $ret;
90
+ }
91
+
92
+ /**
93
+ * Takes the provided value and returns a sanitized value.
94
+ *
95
+ * @param string $value The attachment_pg value to be sanitized.
96
+ * @param string &$err String to be initialized with error, if any.
97
+ *
98
+ * @return bool The sanitized attachment_pg value.
99
+ */
100
+ private static function sanitizeAttachmentPg( $value, &$err ) {
101
+ $ret = DG_Util::toBool( $value );
102
+
103
+ if ( is_null( $ret ) ) {
104
+ $err = sprintf( self::$binary_err, 'attachment_pg', 'true', 'false', $value );
105
+ }
106
+
107
+ return $ret;
108
+ }
109
+
110
+ /**
111
+ * Takes the provided value and returns a sanitized value.
112
+ *
113
+ * @param string $value The columns value to be sanitized.
114
+ * @param string &$err String to be initialized with error, if any.
115
+ *
116
+ * @return int The sanitized columns value.
117
+ */
118
+ public static function sanitizeColumns( $value, &$err ) {
119
+ return $value != -1 ? absint( $value ) : null;
120
+ }
121
+
122
+ /**
123
+ * Takes the provided value and returns a sanitized value.
124
+ *
125
+ * @param string $value The descriptions value to be sanitized.
126
+ * @param string &$err String to be initialized with error, if any.
127
+ *
128
+ * @return bool The sanitized descriptions value.
129
+ */
130
+ private static function sanitizeDescriptions( $value, &$err ) {
131
+ $ret = DG_Util::toBool( $value );
132
+
133
+ if ( is_null( $ret ) ) {
134
+ $err = sprintf( self::$binary_err, 'descriptions', 'true', 'false', $value );
135
+ }
136
+
137
+ return $ret;
138
+ }
139
+
140
+ /**
141
+ * Takes the provided value and returns a sanitized value.
142
+ *
143
+ * @param string $value The exclude value to be sanitized.
144
+ * @param string &$err String to be initialized with error, if any.
145
+ *
146
+ * @return bool The sanitized exclude value.
147
+ */
148
+ private static function sanitizeExclude( $value, &$err ) {
149
+ return self::sanitizeIdList( 'Exclude', $value, $err );
150
+ }
151
+
152
+ /**
153
+ * Takes the provided value and returns a sanitized value.
154
+ *
155
+ * @param string $value The fancy value to be sanitized.
156
+ * @param string &$err String to be initialized with error, if any.
157
+ *
158
+ * @return bool The sanitized fancy value.
159
+ */
160
+ private static function sanitizeFancy( $value, &$err ) {
161
+ $ret = DG_Util::toBool( $value );
162
+
163
+ if ( is_null( $ret ) ) {
164
+ $err = sprintf( self::$binary_err, 'fancy', 'true', 'false', $value );
165
+ }
166
+
167
+ return $ret;
168
+ }
169
+
170
+ /**
171
+ * Takes the provided value and returns a sanitized value.
172
+ *
173
+ * @param string $value The id value to be sanitized.
174
+ * @param string &$err String to be initialized with error, if any.
175
+ *
176
+ * @return int The sanitized id value.
177
+ */
178
+ private static function sanitizeId( $value, &$err ) {
179
+ return $value != -1 ? absint( $value ) : null;
180
+ }
181
+
182
+ /**
183
+ * Takes the provided comma-delimited list of IDs and returns null if it is invalid.
184
+ *
185
+ * @param string $name Name of the value being sanitized. Used in error string when needed.
186
+ * @param string $value The ids value to be sanitized.
187
+ * @param string &$err String to be initialized with error, if any.
188
+ *
189
+ * @return bool|multitype:int The sanitized comma-delimited list of IDs value.
190
+ */
191
+ private static function sanitizeIdList( $name, $value, &$err ) {
192
+ static $regex = '/(?:|\d+(?:,\d+)*)/';
193
+
194
+ $ret = $value;
195
+
196
+ if ( ! preg_match( $regex, $value ) ) {
197
+ $err = sprintf( __( '%s may only be a comma-delimited list of integers.', 'document-gallery' ), $name );
198
+ $ret = null;
199
+ }
200
+
201
+ return $ret;
202
+ }
203
+
204
+ /**
205
+ * Takes the provided value and returns a sanitized value.
206
+ *
207
+ * @param string $value The ids value to be sanitized.
208
+ * @param string &$err String to be initialized with error, if any.
209
+ *
210
+ * @return bool|multitype:int The sanitized ids value.
211
+ */
212
+ private static function sanitizeInclude( $value, &$err ) {
213
+ return self::sanitizeIdList( 'Include', $value, $err );
214
+ }
215
+
216
+ /**
217
+ * Takes the provided value and returns a sanitized value.
218
+ *
219
+ * @param string $value The limit value to be sanitized.
220
+ * @param string &$err String to be initialized with error, if any.
221
+ *
222
+ * @return int The sanitized limit value.
223
+ */
224
+ private static function sanitizeLimit( $value, &$err ) {
225
+ $ret = intval( $value );
226
+
227
+ if ( is_null( $ret ) || $ret < -1 ) {
228
+ $err = sprintf( self::$unary_err, 'limit', '>= -1' );
229
+ $ret = null;
230
+ }
231
+
232
+ return $ret;
233
+ }
234
+
235
+ /**
236
+ * Takes the provided value and returns a sanitized value.
237
+ *
238
+ * @param string $value The mime_types value to be sanitized.
239
+ * @param string &$err String to be initialized with error, if any.
240
+ *
241
+ * @return string The sanitized mime_types value.
242
+ */
243
+ private static function sanitizeMimeTypes( $value, &$err ) {
244
+ // TODO: do some actual sanitization...
245
+ return $value;
246
+ }
247
+
248
+ /**
249
+ * Takes the provided value and returns a sanitized value.
250
+ *
251
+ * @param string $value The new_window value to be sanitized.
252
+ * @param string &$err String to be initialized with error, if any.
253
+ *
254
+ * @return bool The sanitized new_window value.
255
+ */
256
+ private static function sanitizeNewWindow( $value, &$err ) {
257
+ $ret = DG_Util::toBool( $value );
258
+
259
+ if ( is_null( $ret ) ) {
260
+ $err = sprintf( self::$binary_err, 'new_window', 'true', 'false', $value );
261
+ }
262
+
263
+ return $ret;
264
+ }
265
+
266
+ /**
267
+ * Takes the provided value and returns a sanitized value.
268
+ *
269
+ * @param string $value The order value to be sanitized.
270
+ * @param string &$err String to be initialized with error, if any.
271
+ *
272
+ * @return string The sanitized order value.
273
+ */
274
+ private static function sanitizeOrder( $value, &$err ) {
275
+ $ret = strtoupper( $value );
276
+
277
+ if ( ! in_array( $ret, self::getOrderOptions() ) ) {
278
+ $err = sprintf( self::$binary_err, 'order', 'ASC', 'DESC', $value );
279
+ $ret = null;
280
+ }
281
+
282
+ return $ret;
283
+ }
284
+
285
+ /**
286
+ * @return array The valid options for order parameter.
287
+ */
288
+ public static function getOrderOptions() {
289
+ return array( 'ASC', 'DESC' );
290
+ }
291
+
292
+ /**
293
+ * Takes the provided value and returns a sanitized value.
294
+ *
295
+ * @param string $value The orderby value to be sanitized.
296
+ * @param string &$err String to be initialized with error, if any.
297
+ *
298
+ * @return string The sanitized orderby value.
299
+ */
300
+ private static function sanitizeOrderby( $value, &$err ) {
301
+ $ret = ( 'ID' === strtoupper( $value ) ) ? 'ID' : strtolower( $value );
302
+
303
+ if ( ! in_array( $ret, self::getOrderbyOptions() ) ) {
304
+ $err = sprintf( self::$unary_err, 'orderby', $value );
305
+ $ret = null;
306
+ }
307
+
308
+ return $ret;
309
+ }
310
+
311
+ /**
312
+ * @return array The valid options for orderby parameter.
313
+ */
314
+ public static function getOrderbyOptions() {
315
+ return array(
316
+ 'author',
317
+ 'comment_count',
318
+ 'date',
319
+ 'ID',
320
+ 'menu_order',
321
+ 'modified',
322
+ 'name',
323
+ 'none',
324
+ 'parent',
325
+ 'post__in',
326
+ 'rand',
327
+ 'title'
328
+ );
329
+ }
330
+
331
+ /**
332
+ * Takes the provided value and returns a sanitized value.
333
+ *
334
+ * @param string $value The paginate value to be sanitized.
335
+ * @param string &$err String to be initialized with error, if any.
336
+ *
337
+ * @return string The sanitized paginate value.
338
+ */
339
+ private static function sanitizePaginate( $value, &$err ) {
340
+ $ret = DG_Util::toBool( $value );
341
+
342
+ if ( is_null( $ret ) ) {
343
+ $err = sprintf( self::$binary_err, 'paginate', 'true', 'false', $value );
344
+ }
345
+
346
+ return $ret;
347
+ }
348
+
349
+ /**
350
+ * Takes the provided value and returns a sanitized value.
351
+ *
352
+ * @param string $value The post_status value to be sanitized.
353
+ * @param string &$err String to be initialized with error, if any.
354
+ *
355
+ * @return string The sanitized post_status value.
356
+ */
357
+ private static function sanitizePostStatus( $value, &$err ) {
358
+ $ret = preg_grep( '/^' . preg_quote( $value ) . '$/i', self::getPostStatuses() );
359
+ $ret = reset( $ret );
360
+
361
+ if ( $ret === false ) {
362
+ $err = sprintf( self::$unary_err, 'post_status', $value );
363
+ }
364
+
365
+ return $ret;
366
+ }
367
+
368
+ /**
369
+ * @return array All registered post statuses.
370
+ */
371
+ public static function getPostStatuses() {
372
+ static $statuses;
373
+ if ( ! isset( $statuses ) ) {
374
+ $statuses = get_post_stati();
375
+ $statuses[] = 'any';
376
+ asort( $statuses );
377
+ }
378
+
379
+ return $statuses;
380
+ }
381
+
382
+ /**
383
+ * Takes the provided value and returns a sanitized value.
384
+ *
385
+ * @param string $value The post_type value to be sanitized.
386
+ * @param string &$err String to be initialized with error, if any.
387
+ *
388
+ * @return string The sanitized post_type value.
389
+ */
390
+ private static function sanitizePostType( $value, &$err ) {
391
+ $ret = preg_grep( '/^' . preg_quote( $value ) . '$/i', self::getPostTypes() );
392
+ $ret = reset( $ret );
393
+
394
+ if ( $ret === false ) {
395
+ $err = sprintf( self::$unary_err, 'post_type', $value );
396
+ }
397
+
398
+ return $ret;
399
+ }
400
+
401
+ /**
402
+ * @return array All registered post types.
403
+ */
404
+ public static function getPostTypes() {
405
+ static $types;
406
+ if ( ! isset( $types ) ) {
407
+ $types = get_post_types();
408
+ $types[] = 'any';
409
+ asort( $types );
410
+ }
411
+
412
+ return $types;
413
+ }
414
+
415
+ /**
416
+ * Takes the provided value and returns a sanitized value.
417
+ *
418
+ * @param string $value The relation value to be sanitized.
419
+ * @param string &$err String to be initialized with error, if any.
420
+ *
421
+ * @return string The sanitized relation value.
422
+ */
423
+ private static function sanitizeRelation( $value, &$err ) {
424
+ $ret = strtoupper( $value );
425
+
426
+ if ( ! in_array( $ret, self::getRelationOptions() ) ) {
427
+ $err = sprintf( self::$binary_err, 'relation', 'AND', 'OR', $value );
428
+ $ret = null;
429
+ }
430
+
431
+ return $ret;
432
+ }
433
+
434
+ /**
435
+ * @return array The valid options for relation parameter.
436
+ */
437
+ public static function getRelationOptions() {
438
+ return array( 'AND', 'OR' );
439
+ }
440
+
441
+ /**
442
+ * Takes the provided value and returns a sanitized value.
443
+ *
444
+ * @param string $value The skip value to be sanitized.
445
+ * @param string &$err String to be initialized with error, if any.
446
+ *
447
+ * @return string The sanitized skip value.
448
+ */
449
+ private static function sanitizeSkip( $value, &$err ) {
450
+ $ret = intval( $value );
451
+
452
+ if ( is_null( $ret ) || $ret < 0 ) {
453
+ $err = sprintf( self::$unary_err, 'skip', '>= 0' );
454
+ $ret = null;
455
+ }
456
+
457
+ return $ret;
458
+ }
459
+
460
+ /**
461
+ * Takes the provided value and returns a sanitized value.
462
+ *
463
+ * @param string $operator The operator value to be sanitized.
464
+ * @param string &$err String to be initialized with error, if any.
465
+ *
466
+ * @return string The sanitized operator value.
467
+ */
468
+ private static function sanitizeOperator( $operator, &$err ) {
469
+ $ret = strtoupper( $operator );
470
+
471
+ if ( ! in_array( $ret, self::getOperatorOptions() ) ) {
472
+ $err = sprintf( self::$binary_err, 'IN", "NOT IN", "OR', 'AND', $operator );
473
+ $ret = null;
474
+ } else if ( $ret === 'OR' ) {
475
+ $ret = 'IN';
476
+ }
477
+
478
+ return $ret;
479
+ }
480
+
481
+ /**
482
+ * @return array The valid options for *_relation/*_operator parameter.
483
+ */
484
+ public static function getOperatorOptions() {
485
+ return array( 'IN', 'NOT IN', 'AND', 'OR' );
486
+ }
487
+
488
+ /**
489
+ * @param string $string To take second char from.
490
+ *
491
+ * @return string Capitalized second char of given string.
492
+ */
493
+ private static function secondCharToUpper( $string ) {
494
+ return strtoupper( $string[1] );
495
+ }
496
+ }
inc/class-gallery.php CHANGED
@@ -1,6 +1,8 @@
1
  <?php
2
  defined( 'WPINC' ) OR exit;
3
 
 
 
4
  DG_Gallery::init();
5
 
6
  /**
@@ -11,19 +13,22 @@ DG_Gallery::init();
11
  class DG_Gallery {
12
 
13
  /*==========================================================================
14
- * PRIVATE FIELDS
15
- *=========================================================================*/
16
 
17
  private $atts, $taxa;
18
  private $docs = array();
19
  private $errs = array();
20
 
 
 
 
21
  // templates for HTML output
22
- private static $no_docs, $comment, $unary_err, $binary_err;
23
 
24
  /*==========================================================================
25
- * PUBLIC FUNCTIONS
26
- *=========================================================================*/
27
 
28
  /**
29
  * @return bool Whether to link to attachment pg.
@@ -54,8 +59,8 @@ class DG_Gallery {
54
  }
55
 
56
  /*==========================================================================
57
- * GET AND SET OPTIONS
58
- *=========================================================================*/
59
 
60
  /**
61
  * @param int $blog The blog we're retrieving options for (null => current blog).
@@ -79,20 +84,19 @@ class DG_Gallery {
79
  }
80
 
81
  /*==========================================================================
82
- * INIT GALLERY
83
- *=========================================================================*/
84
 
85
  /**
86
  * Initializes static values for this class.
87
  */
88
  public static function init() {
89
  if ( ! isset( self::$comment ) ) {
90
- self::$comment =
91
  PHP_EOL . '<!-- ' . __( 'Generated using Document Gallery. Get yours here: ', 'document-gallery' ) .
92
  'http://wordpress.org/extend/plugins/document-gallery -->' . PHP_EOL;
93
- self::$no_docs = '<!-- ' . __( 'No attachments to display. How boring! :(', 'document-gallery' ) . ' -->';
94
- self::$unary_err = __( 'The %s value entered, "%s", is not valid.', 'document-gallery' );
95
- self::$binary_err = __( 'The %s parameter may only be "%s" or "%s." You entered "%s."', 'document-gallery' );
96
  }
97
  }
98
 
@@ -104,11 +108,13 @@ class DG_Gallery {
104
  public function __construct( $atts ) {
105
  include_once DG_PATH . 'inc/class-document.php';
106
 
107
- $post = get_post();
108
-
109
  // empty string is passed when no arguments are given, but constructor expects an array
110
  $atts = empty( $atts ) ? array() : $atts;
111
 
 
 
 
 
112
  if ( ! empty( $atts['ids'] ) ) {
113
  // 'ids' is explicitly ordered, unless you specify otherwise.
114
  if ( empty( $atts['orderby'] ) ) {
@@ -141,14 +147,11 @@ class DG_Gallery {
141
  * @deprecated localpost will be removed at some point.
142
  */
143
  if ( ! empty( $atts['localpost'] ) ) {
144
- $atts['id'] = - 1;
145
  unset( $atts['localpost'] );
146
  }
147
 
148
- // merge options w/ default values not stored in options
149
- $defaults = array_merge(
150
- array( 'id' => $post->ID, 'include' => '', 'exclude' => '' ),
151
- self::getOptions() );
152
 
153
  // values used to construct tax query (may be empty)
154
  $this->taxa = array_diff_key( $atts, $defaults );
@@ -200,409 +203,13 @@ class DG_Gallery {
200
  }
201
  } else if ( $sanitized[ $k ] !== $v ) { //Sometimes we get boolean in the string form for checkboxes
202
  // sanitize value if different from old value
203
- $sanitized[ $k ] = self::sanitizeParameter( $k, $sanitized[ $k ], $errs );
204
  }
205
  }
206
 
207
  return $sanitized;
208
  }
209
 
210
- /**
211
- *
212
- * @param string $key The key to reference the current value in the defaults array.
213
- * @param mixed $value The value to be sanitized.
214
- * @param array $errs The array of errors, which will be appended with any errors found.
215
- *
216
- * @return mixed The sanitized value, falling back to the current default value when invalid value given.
217
- */
218
- private static function sanitizeParameter( $key, $value, &$errs ) {
219
- // all sanitize methods must be in the following form: sanitize<UpperCammelCaseKey>
220
- $funct = $key;
221
- $funct[0] = strtoupper( $funct[0] );
222
- $funct = 'sanitize' . preg_replace_callback( '/_([a-z])/', array( __CLASS__, 'secondCharToUpper' ), $funct );
223
-
224
- $callable = array( __CLASS__, $funct );
225
-
226
- // avoid looking for method beforehand unless we're running in debug mode -- expensive call
227
- if ( DG_Logger::logEnabled() && ! method_exists( __CLASS__, $funct ) ) {
228
- DG_Logger::writeLog(
229
- DG_LogLevel::Error,
230
- __( 'Attempted to call invalid function: ', 'document-gallery' ) . implode( '::', $callable ),
231
- true );
232
- }
233
-
234
- // call param-specific sanitization
235
- $ret = call_user_func_array( $callable, array( $value, &$err ) );
236
-
237
- // check for error and return default
238
- if ( isset( $err ) ) {
239
- $defaults = self::getOptions();
240
- $ret = $defaults[ $key ];
241
-
242
- $errs[ $key ] = $err;
243
- }
244
-
245
- return $ret;
246
- }
247
-
248
- /**
249
- * Takes the provided value and returns a sanitized value.
250
- *
251
- * @param string $value The attachment_pg value to be sanitized.
252
- * @param string &$err String to be initialized with error, if any.
253
- *
254
- * @return bool The sanitized attachment_pg value.
255
- */
256
- private static function sanitizeAttachmentPg( $value, &$err ) {
257
- $ret = DG_Util::toBool( $value );
258
-
259
- if ( is_null( $ret ) ) {
260
- $err = sprintf( self::$binary_err, 'attachment_pg', 'true', 'false', $value );
261
- }
262
-
263
- return $ret;
264
- }
265
-
266
- /**
267
- * Takes the provided value and returns a sanitized value.
268
- *
269
- * @param string $value The columns value to be sanitized.
270
- * @param string &$err String to be initialized with error, if any.
271
- *
272
- * @return int The sanitized columns value.
273
- */
274
- public static function sanitizeColumns( $value, &$err ) {
275
- return $value != - 1 ? absint( $value ) : null;
276
- }
277
-
278
- /**
279
- * Takes the provided value and returns a sanitized value.
280
- *
281
- * @param string $value The descriptions value to be sanitized.
282
- * @param string &$err String to be initialized with error, if any.
283
- *
284
- * @return bool The sanitized descriptions value.
285
- */
286
- private static function sanitizeDescriptions( $value, &$err ) {
287
- $ret = DG_Util::toBool( $value );
288
-
289
- if ( is_null( $ret ) ) {
290
- $err = sprintf( self::$binary_err, 'descriptions', 'true', 'false', $value );
291
- }
292
-
293
- return $ret;
294
- }
295
-
296
- /**
297
- * Takes the provided value and returns a sanitized value.
298
- *
299
- * @param string $value The exclude value to be sanitized.
300
- * @param string &$err String to be initialized with error, if any.
301
- *
302
- * @return bool The sanitized exclude value.
303
- */
304
- private static function sanitizeExclude( $value, &$err ) {
305
- return self::sanitizeIdList( 'Exclude', $value, $err );
306
- }
307
-
308
- /**
309
- * Takes the provided value and returns a sanitized value.
310
- *
311
- * @param string $value The fancy value to be sanitized.
312
- * @param string &$err String to be initialized with error, if any.
313
- *
314
- * @return bool The sanitized fancy value.
315
- */
316
- private static function sanitizeFancy( $value, &$err ) {
317
- $ret = DG_Util::toBool( $value );
318
-
319
- if ( is_null( $ret ) ) {
320
- $err = sprintf( self::$binary_err, 'fancy', 'true', 'false', $value );
321
- }
322
-
323
- return $ret;
324
- }
325
-
326
- /**
327
- * Takes the provided value and returns a sanitized value.
328
- *
329
- * @param string $value The id value to be sanitized.
330
- * @param string &$err String to be initialized with error, if any.
331
- *
332
- * @return int The sanitized id value.
333
- */
334
- private static function sanitizeId( $value, &$err ) {
335
- return $value != - 1 ? absint( $value ) : null;
336
- }
337
-
338
- /**
339
- * Takes the provided comma-delimited list of IDs and returns null if it is invalid.
340
- *
341
- * @param string $name Name of the value being sanitized. Used in error string when needed.
342
- * @param string $value The ids value to be sanitized.
343
- * @param string &$err String to be initialized with error, if any.
344
- *
345
- * @return bool|multitype:int The sanitized comma-delimited list of IDs value.
346
- */
347
- private static function sanitizeIdList( $name, $value, &$err ) {
348
- static $regex = '/(?:|\d+(?:,\d+)*)/';
349
-
350
- $ret = $value;
351
-
352
- if ( ! preg_match( $regex, $value ) ) {
353
- $err = sprintf( __( '%s may only be a comma-delimited list of integers.', 'document-gallery' ), $name );
354
- $ret = null;
355
- }
356
-
357
- return $ret;
358
- }
359
-
360
- /**
361
- * Takes the provided value and returns a sanitized value.
362
- *
363
- * @param string $value The ids value to be sanitized.
364
- * @param string &$err String to be initialized with error, if any.
365
- *
366
- * @return bool|multitype:int The sanitized ids value.
367
- */
368
- private static function sanitizeInclude( $value, &$err ) {
369
- return self::sanitizeIdList( 'Include', $value, $err );
370
- }
371
-
372
- /**
373
- * Takes the provided value and returns a sanitized value.
374
- *
375
- * @param string $value The limit value to be sanitized.
376
- * @param string &$err String to be initialized with error, if any.
377
- *
378
- * @return int The sanitized limit value.
379
- */
380
- private static function sanitizeLimit( $value, &$err ) {
381
- $ret = intval( $value );
382
-
383
- if ( is_null( $ret ) || $ret < - 1 ) {
384
- $err = sprintf( self::$unary_err, 'limit', '>= -1' );
385
- $ret = null;
386
- }
387
-
388
- return $ret;
389
- }
390
-
391
- /**
392
- * Takes the provided value and returns a sanitized value.
393
- *
394
- * @param string $value The mime_types value to be sanitized.
395
- * @param string &$err String to be initialized with error, if any.
396
- *
397
- * @return string The sanitized mime_types value.
398
- */
399
- private static function sanitizeMimeTypes( $value, &$err ) {
400
- // TODO: do some actual sanitization...
401
- return $value;
402
- }
403
-
404
- /**
405
- * Takes the provided value and returns a sanitized value.
406
- *
407
- * @param string $value The new_window value to be sanitized.
408
- * @param string &$err String to be initialized with error, if any.
409
- *
410
- * @return bool The sanitized new_window value.
411
- */
412
- private static function sanitizeNewWindow( $value, &$err ) {
413
- $ret = DG_Util::toBool( $value );
414
-
415
- if ( is_null( $ret ) ) {
416
- $err = sprintf( self::$binary_err, 'new_window', 'true', 'false', $value );
417
- }
418
-
419
- return $ret;
420
- }
421
-
422
- /**
423
- * Takes the provided value and returns a sanitized value.
424
- *
425
- * @param string $value The order value to be sanitized.
426
- * @param string &$err String to be initialized with error, if any.
427
- *
428
- * @return string The sanitized order value.
429
- */
430
- private static function sanitizeOrder( $value, &$err ) {
431
- $ret = strtoupper( $value );
432
-
433
- if ( ! in_array( $ret, self::getOrderOptions() ) ) {
434
- $err = sprintf( self::$binary_err, 'order', 'ASC', 'DESC', $value );
435
- $ret = null;
436
- }
437
-
438
- return $ret;
439
- }
440
-
441
- /**
442
- * @return array The valid options for order parameter.
443
- */
444
- public static function getOrderOptions() {
445
- return array( 'ASC', 'DESC' );
446
- }
447
-
448
- /**
449
- * Takes the provided value and returns a sanitized value.
450
- *
451
- * @param string $value The orderby value to be sanitized.
452
- * @param string &$err String to be initialized with error, if any.
453
- *
454
- * @return string The sanitized orderby value.
455
- */
456
- private static function sanitizeOrderby( $value, &$err ) {
457
- $ret = ( 'ID' === strtoupper( $value ) ) ? 'ID' : strtolower( $value );
458
-
459
- if ( ! in_array( $ret, self::getOrderbyOptions() ) ) {
460
- $err = sprintf( self::$unary_err, 'orderby', $value );
461
- $ret = null;
462
- }
463
-
464
- return $ret;
465
- }
466
-
467
- /**
468
- * @return array The valid options for orderby parameter.
469
- */
470
- public static function getOrderbyOptions() {
471
- return array(
472
- 'author',
473
- 'comment_count',
474
- 'date',
475
- 'ID',
476
- 'menu_order',
477
- 'modified',
478
- 'name',
479
- 'none',
480
- 'parent',
481
- 'post__in',
482
- 'rand',
483
- 'title'
484
- );
485
- }
486
-
487
- /**
488
- * Takes the provided value and returns a sanitized value.
489
- *
490
- * @param string $value The post_status value to be sanitized.
491
- * @param string &$err String to be initialized with error, if any.
492
- *
493
- * @return string The sanitized post_status value.
494
- */
495
- private static function sanitizePostStatus( $value, &$err ) {
496
- $ret = preg_grep( '/^' . preg_quote( $value ) . '$/i', self::getPostStatuses() );
497
- $ret = reset( $ret );
498
-
499
- if ( $ret === false ) {
500
- $err = sprintf( self::$unary_err, 'post_status', $value );
501
- }
502
-
503
- return $ret;
504
- }
505
-
506
- /**
507
- * @return array All registered post statuses.
508
- */
509
- public static function getPostStatuses() {
510
- static $statuses;
511
- if ( ! isset( $statuses ) ) {
512
- $statuses = get_post_stati();
513
- $statuses[] = 'any';
514
- asort( $statuses );
515
- }
516
-
517
- return $statuses;
518
- }
519
-
520
- /**
521
- * Takes the provided value and returns a sanitized value.
522
- *
523
- * @param string $value The post_type value to be sanitized.
524
- * @param string &$err String to be initialized with error, if any.
525
- *
526
- * @return string The sanitized post_type value.
527
- */
528
- private static function sanitizePostType( $value, &$err ) {
529
- $ret = preg_grep( '/^' . preg_quote( $value ) . '$/i', self::getPostTypes() );
530
- $ret = reset( $ret );
531
-
532
- if ( $ret === false ) {
533
- $err = sprintf( self::$unary_err, 'post_type', $value );
534
- }
535
-
536
- return $ret;
537
- }
538
-
539
- /**
540
- * @return array All registered post types.
541
- */
542
- public static function getPostTypes() {
543
- static $types;
544
- if ( ! isset( $types ) ) {
545
- $types = get_post_types();
546
- $types[] = 'any';
547
- asort( $types );
548
- }
549
-
550
- return $types;
551
- }
552
-
553
- /**
554
- * Takes the provided value and returns a sanitized value.
555
- *
556
- * @param string $value The relation value to be sanitized.
557
- * @param string &$err String to be initialized with error, if any.
558
- *
559
- * @return string The sanitized relation value.
560
- */
561
- private static function sanitizeRelation( $value, &$err ) {
562
- $ret = strtoupper( $value );
563
-
564
- if ( ! in_array( $ret, self::getRelationOptions() ) ) {
565
- $err = sprintf( self::$binary_err, 'relation', 'AND', 'OR', $value );
566
- $ret = null;
567
- }
568
-
569
- return $ret;
570
- }
571
-
572
- /**
573
- * @return array The valid options for relation parameter.
574
- */
575
- public static function getRelationOptions() {
576
- return array( 'AND', 'OR' );
577
- }
578
-
579
- /**
580
- * Takes the provided value and returns a sanitized value.
581
- *
582
- * @param string $operator The operator value to be sanitized.
583
- *
584
- * @return string The sanitized operator value.
585
- */
586
- private function sanitizeOperator( $operator ) {
587
- $ret = strtoupper( $operator );
588
-
589
- if ( ! in_array( $ret, self::getOperatorOptions() ) ) {
590
- $this->errs[] = sprintf( self::$binary_err, 'IN", "NOT IN", "OR', 'AND', $operator );
591
- $ret = null;
592
- } else if ( $ret === 'OR' ) {
593
- $ret = 'IN';
594
- }
595
-
596
- return $ret;
597
- }
598
-
599
- /**
600
- * @return array The valid options for *_relation/*_operator parameter.
601
- */
602
- public static function getOperatorOptions() {
603
- return array( 'IN', 'NOT IN', 'AND', 'OR' );
604
- }
605
-
606
  /**
607
  * Gets all valid Documents based on the attributes passed by the user.
608
  * NOTE: Keys in returned array are arbitrary and will vary. They should be ignored.
@@ -611,12 +218,14 @@ class DG_Gallery {
611
  */
612
  private function getDocuments() {
613
  $query = array(
614
- 'numberposts' => $this->atts['limit'],
615
- 'orderby' => $this->atts['orderby'],
616
- 'order' => $this->atts['order'],
617
- 'post_status' => $this->atts['post_status'],
618
- 'post_type' => $this->atts['post_type'],
619
- 'post_mime_type' => $this->atts['mime_types']
 
 
620
  );
621
 
622
  $this->setTaxa( $query );
@@ -625,21 +234,22 @@ class DG_Gallery {
625
  throw new InvalidArgumentException();
626
  }
627
 
628
- // NOTE: Derived from gallery shortcode
629
  if ( ! empty( $this->atts['include'] ) ) {
630
- $query['include'] = $this->atts['include'];
631
- $attachments = get_posts( $query );
632
  } else {
633
  // id == 0 => all attachments w/o a parent
634
  // id == null => all matched attachments
635
  $query['post_parent'] = $this->atts['id'];
636
- if ( ! empty( $exclude ) ) {
637
- $query['exclude'] = $this->atts['exclude'];
638
  }
639
-
640
- $attachments = get_children( $query );
641
  }
642
 
 
 
 
 
 
643
  return $attachments;
644
  }
645
 
@@ -652,25 +262,26 @@ class DG_Gallery {
652
  */
653
  private function setTaxa( &$query ) {
654
  if ( ! empty( $this->taxa ) ) {
 
655
  $taxa = array( 'relation' => $this->atts['relation'] );
656
  $operator = array();
657
- $suffix = array( 'relation', 'operator' );
658
- $pattern = '/(.+)_(?:' . implode( '|', $suffix ) . ')$/i';
659
 
660
  // find any relations for taxa
661
- $iterable = $this->taxa;
662
- foreach ( $iterable as $key => $value ) {
663
  if ( preg_match( $pattern, $key, $matches ) ) {
664
  $base = $matches[1];
665
- if ( array_key_exists( $base, $this->taxa ) ) {
666
- $operator[ $base ] = self::sanitizeOperator( $value );
667
- unset( $this->taxa[ $key ] );
668
  }
669
  }
670
  }
671
 
672
  // build tax query
673
  foreach ( $this->taxa as $taxon => $terms ) {
 
 
674
  $terms = $this->getTermIdsByNames( $taxon, explode( ',', $terms ) );
675
 
676
  $taxa[] = array(
@@ -725,11 +336,11 @@ class DG_Gallery {
725
  *
726
  * @param string $x Field to retrieve from matched term.
727
  * @param string $taxon The taxon these terms are a member of.
728
- * @param array $term_names Terms to retrieve.
729
  *
730
  * @return array All matched terms.
731
  */
732
- private function getTermXByNames( $x, $taxon, $term_names ) {
733
  $ret = array();
734
  $valid = true;
735
 
@@ -740,19 +351,24 @@ class DG_Gallery {
740
  if ( $count > 0 && taxonomy_exists( $tmp ) ) {
741
  $taxon = $tmp;
742
  } else {
743
- $this->errs[] = sprintf( self::$unary_err, 'taxon', $taxon );
744
  $valid = false;
745
  }
746
  }
747
 
748
  // only check terms if we first have a valid taxon
749
  if ( $valid ) {
750
- foreach ( $term_names as $name ) {
751
- if ( ( $term = get_term_by( 'name', $name, $taxon ) ) ) {
 
 
 
 
 
752
  $ret[] = $term->{$x};
753
  } else {
754
- $this->errs[] = sprintf( __( '%s is not a valid term name in %s.',
755
- 'document-gallery' ), $name, $taxon );
756
  }
757
  }
758
  }
@@ -760,15 +376,6 @@ class DG_Gallery {
760
  return $ret;
761
  }
762
 
763
- /**
764
- * @param string $string To take second char from.
765
- *
766
- * @return string Capitalized second char of given string.
767
- */
768
- private static function secondCharToUpper( $string ) {
769
- return strtoupper( $string[1] );
770
- }
771
-
772
  /**
773
  * Function returns false for positive ints, true otherwise.
774
  *
@@ -783,8 +390,22 @@ class DG_Gallery {
783
  }
784
 
785
  /*==========================================================================
786
- * OUTPUT HTML STRING
787
- *=========================================================================*/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788
 
789
  /**
790
  * @filter dg_gallery_template Allows the user to filter anything content surrounding the generated gallery.
@@ -794,12 +415,6 @@ class DG_Gallery {
794
  */
795
  public function __toString() {
796
  static $instance = 0;
797
- $instance++;
798
-
799
- static $find = null;
800
- if ( is_null( $find ) ) {
801
- $find = array( '%class%', '%icons%' );
802
- }
803
 
804
  if ( ! empty( $this->errs ) ) {
805
  return '<p>' . implode( '</p><p>', $this->errs ) . '</p>';
@@ -809,56 +424,88 @@ class DG_Gallery {
809
  return self::$no_docs;
810
  }
811
 
812
- $style = '';
813
- $selector = "document-gallery-$instance";
814
- $template =
815
- "<div class='%class%'>" . PHP_EOL .
816
- '%icons%' . PHP_EOL .
817
- '</div>' . PHP_EOL;
818
 
819
- $icon_wrapper = apply_filters(
820
- 'dg_row_template',
821
- $template,
822
- $this->useDescriptions() );
 
 
823
 
824
- $core = '';
825
- $classes = array( 'document-icon-wrapper' );
826
  if ( $this->useDescriptions() ) {
827
- $classes[] = 'descriptions';
828
  }
829
 
830
- $repl = array( implode( ' ', $classes ) );
 
 
 
 
 
 
831
  if ( $this->useDescriptions() ) {
832
  foreach ( $this->docs as $doc ) {
833
- $repl[1] = $doc;
834
- $core .= str_replace( $find, $repl, $icon_wrapper );
835
  }
836
  } else {
837
  $count = count( $this->docs );
838
- $cols = ! is_null( $this->atts['columns'] ) ? $this->atts['columns'] : $count;
839
 
840
- // TODO: Invalid HTML. WP Core does it this way for [gallery], but consider setting width for each
841
- // .document-icon as style attribute in element.
842
  if ( apply_filters( 'dg_use_default_gallery_style', true ) ) {
843
  $itemwidth = $cols > 0 ? ( floor( 100 / $cols ) - 1 ) : 100;
844
- $style = "<style type='text/css'>#$selector .document-icon{width:$itemwidth%}</style>" . PHP_EOL;
845
  }
846
 
847
  for ( $i = 0; $i < $count; $i += $cols ) {
848
- $repl[1] = '';
849
 
850
  $min = min( $i + $cols, $count );
851
  for ( $x = $i; $x < $min; $x ++ ) {
852
- $repl[1] .= $this->docs[ $x ];
853
  }
854
 
855
- $core .= str_replace( $find, $repl, $icon_wrapper );
856
  }
857
  }
858
 
859
  // allow user to filter gallery wrapper
860
- $gallery = apply_filters( 'dg_gallery_template', '<div id="%id%">' . PHP_EOL . '%rows%</div>', $this->useDescriptions() );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
 
862
- return self::$comment . $style . str_replace( array( '%id%', '%rows%' ), array( $selector, $core ), $gallery );
863
  }
864
  }
1
  <?php
2
  defined( 'WPINC' ) OR exit;
3
 
4
+ include_once DG_PATH . 'inc/class-gallery-sanitization.php';
5
+
6
  DG_Gallery::init();
7
 
8
  /**
13
  class DG_Gallery {
14
 
15
  /*==========================================================================
16
+ * PRIVATE FIELDS
17
+ *=========================================================================*/
18
 
19
  private $atts, $taxa;
20
  private $docs = array();
21
  private $errs = array();
22
 
23
+ private $has_prev = false;
24
+ private $has_next = false;
25
+
26
  // templates for HTML output
27
+ private static $no_docs, $comment, $defaults;
28
 
29
  /*==========================================================================
30
+ * PUBLIC FUNCTIONS
31
+ *=========================================================================*/
32
 
33
  /**
34
  * @return bool Whether to link to attachment pg.
59
  }
60
 
61
  /*==========================================================================
62
+ * GET AND SET OPTIONS
63
+ *=========================================================================*/
64
 
65
  /**
66
  * @param int $blog The blog we're retrieving options for (null => current blog).
84
  }
85
 
86
  /*==========================================================================
87
+ * INIT GALLERY
88
+ *=========================================================================*/
89
 
90
  /**
91
  * Initializes static values for this class.
92
  */
93
  public static function init() {
94
  if ( ! isset( self::$comment ) ) {
95
+ self::$comment =
96
  PHP_EOL . '<!-- ' . __( 'Generated using Document Gallery. Get yours here: ', 'document-gallery' ) .
97
  'http://wordpress.org/extend/plugins/document-gallery -->' . PHP_EOL;
98
+ self::$no_docs = '<!-- ' . __( 'No attachments to display. How boring! :(', 'document-gallery' ) . ' -->';
99
+ self::$defaults = array_merge( array( 'include' => '', 'exclude' => '' ), self::getOptions() );
 
100
  }
101
  }
102
 
108
  public function __construct( $atts ) {
109
  include_once DG_PATH . 'inc/class-document.php';
110
 
 
 
111
  // empty string is passed when no arguments are given, but constructor expects an array
112
  $atts = empty( $atts ) ? array() : $atts;
113
 
114
+ // get_post will return null during AJAX requests
115
+ $post = get_post();
116
+ $post_id = !is_null( $post ) ? $post->ID : -1;
117
+
118
  if ( ! empty( $atts['ids'] ) ) {
119
  // 'ids' is explicitly ordered, unless you specify otherwise.
120
  if ( empty( $atts['orderby'] ) ) {
147
  * @deprecated localpost will be removed at some point.
148
  */
149
  if ( ! empty( $atts['localpost'] ) ) {
150
+ $atts['id'] = -1;
151
  unset( $atts['localpost'] );
152
  }
153
 
154
+ $defaults = array_merge( array( 'id' => $post_id ), self::$defaults );
 
 
 
155
 
156
  // values used to construct tax query (may be empty)
157
  $this->taxa = array_diff_key( $atts, $defaults );
203
  }
204
  } else if ( $sanitized[ $k ] !== $v ) { //Sometimes we get boolean in the string form for checkboxes
205
  // sanitize value if different from old value
206
+ $sanitized[ $k ] = DG_GallerySanitization::sanitizeParameter( $k, $sanitized[ $k ], $errs );
207
  }
208
  }
209
 
210
  return $sanitized;
211
  }
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
  /**
214
  * Gets all valid Documents based on the attributes passed by the user.
215
  * NOTE: Keys in returned array are arbitrary and will vary. They should be ignored.
218
  */
219
  private function getDocuments() {
220
  $query = array(
221
+ 'posts_per_page' => $this->atts['limit'],
222
+ 'offset' => $this->atts['skip'],
223
+ 'orderby' => $this->atts['orderby'],
224
+ 'order' => $this->atts['order'],
225
+ 'post_status' => $this->atts['post_status'],
226
+ 'post_type' => $this->atts['post_type'],
227
+ 'post_mime_type' => $this->atts['mime_types'],
228
+ 'suppress_filters' => true
229
  );
230
 
231
  $this->setTaxa( $query );
234
  throw new InvalidArgumentException();
235
  }
236
 
 
237
  if ( ! empty( $this->atts['include'] ) ) {
238
+ $query['post__in'] = wp_parse_id_list( $this->atts['include'] );
 
239
  } else {
240
  // id == 0 => all attachments w/o a parent
241
  // id == null => all matched attachments
242
  $query['post_parent'] = $this->atts['id'];
243
+ if ( ! empty( $this->atts['exclude'] ) ) {
244
+ $query['post__not_in'] = wp_parse_id_list( $this->atts['exclude'] );
245
  }
 
 
246
  }
247
 
248
+ $wpq = new WP_Query();
249
+ $attachments = $wpq->query( $query );
250
+ $this->has_next = $wpq->found_posts > ( $this->atts['skip'] + $this->atts['limit'] );
251
+ $this->has_prev = $this->atts['skip'] > 0;
252
+
253
  return $attachments;
254
  }
255
 
262
  */
263
  private function setTaxa( &$query ) {
264
  if ( ! empty( $this->taxa ) ) {
265
+ static $pattern = '/(.+)_(?:relation|operator)$/i';
266
  $taxa = array( 'relation' => $this->atts['relation'] );
267
  $operator = array();
 
 
268
 
269
  // find any relations for taxa
270
+ $operator_keys = array();
271
+ foreach ( $this->taxa as $key => $value ) {
272
  if ( preg_match( $pattern, $key, $matches ) ) {
273
  $base = $matches[1];
274
+ if ( isset( $this->taxa[$base] ) ) {
275
+ $operator[$base] = DG_GallerySanitization::sanitizeParameter( 'operator', $value, $this->errs );
276
+ $operator_keys[] = $key;
277
  }
278
  }
279
  }
280
 
281
  // build tax query
282
  foreach ( $this->taxa as $taxon => $terms ) {
283
+ if ( in_array( $taxon, $operator_keys ) ) continue;
284
+
285
  $terms = $this->getTermIdsByNames( $taxon, explode( ',', $terms ) );
286
 
287
  $taxa[] = array(
336
  *
337
  * @param string $x Field to retrieve from matched term.
338
  * @param string $taxon The taxon these terms are a member of.
339
+ * @param array $term_idents Terms to retrieve, identified by either slug or name.
340
  *
341
  * @return array All matched terms.
342
  */
343
+ private function getTermXByNames( $x, $taxon, $term_idents ) {
344
  $ret = array();
345
  $valid = true;
346
 
351
  if ( $count > 0 && taxonomy_exists( $tmp ) ) {
352
  $taxon = $tmp;
353
  } else {
354
+ $this->errs[] = sprintf( DG_GallerySanitization::getUnaryErr(), 'taxon', $taxon );
355
  $valid = false;
356
  }
357
  }
358
 
359
  // only check terms if we first have a valid taxon
360
  if ( $valid ) {
361
+ foreach ( $term_idents as $ident ) {
362
+ $term = get_term_by( 'slug', $ident, $taxon );
363
+ if ( ! $term ) {
364
+ $term = get_term_by( 'name', $ident, $taxon );
365
+ }
366
+
367
+ if ( $term ) {
368
  $ret[] = $term->{$x};
369
  } else {
370
+ $this->errs[] = sprintf( __( '%s is not a valid term slug/name in %s.',
371
+ 'document-gallery' ), $ident, $taxon );
372
  }
373
  }
374
  }
376
  return $ret;
377
  }
378
 
 
 
 
 
 
 
 
 
 
379
  /**
380
  * Function returns false for positive ints, true otherwise.
381
  *
390
  }
391
 
392
  /*==========================================================================
393
+ * OUTPUT HTML STRING
394
+ *=========================================================================*/
395
+
396
+ /**
397
+ * @return array The data to be used in the data-shortcode attribute.
398
+ */
399
+ private function getShortcodeData() {
400
+ $ret = array_merge( $this->atts, $this->taxa );
401
+
402
+ // need to undo nulling of -1 ID for version sent out to JSON
403
+ if ( is_null( $ret['id'] ) ) {
404
+ $ret['id'] = -1;
405
+ }
406
+
407
+ return $ret;
408
+ }
409
 
410
  /**
411
  * @filter dg_gallery_template Allows the user to filter anything content surrounding the generated gallery.
415
  */
416
  public function __toString() {
417
  static $instance = 0;
 
 
 
 
 
 
418
 
419
  if ( ! empty( $this->errs ) ) {
420
  return '<p>' . implode( '</p><p>', $this->errs ) . '</p>';
424
  return self::$no_docs;
425
  }
426
 
427
+ $instance++;
 
 
 
 
 
428
 
429
+ $icon_find = array( '%class%', '%icons%' );
430
+ $icon_repl = array();
431
+ $icon_classes = array( 'document-icon-row' );
432
+ $gallery_find = array( '%id%', '%data%', '%rows%', '%class%' );
433
+ $gallery_repl = array( "document-gallery-$instance", ( "data-shortcode='" . wp_json_encode( self::getShortcodeData() ) . "'" ), '' );
434
+ $gallery_classes = array( 'document-gallery' );
435
 
 
 
436
  if ( $this->useDescriptions() ) {
437
+ $icon_classes[] = 'descriptions';
438
  }
439
 
440
+ $icon_repl[] = implode( ' ', $icon_classes );
441
+
442
+ $icon_wrapper = apply_filters(
443
+ 'dg_row_template',
444
+ "<div class='%class%'>" . PHP_EOL . '%icons%' . PHP_EOL . '</div>' . PHP_EOL,
445
+ $this->useDescriptions() );
446
+
447
  if ( $this->useDescriptions() ) {
448
  foreach ( $this->docs as $doc ) {
449
+ $icon_repl[1] = $doc;
450
+ $gallery_repl[2] .= str_replace( $icon_find, $icon_repl, $icon_wrapper );
451
  }
452
  } else {
453
  $count = count( $this->docs );
454
+ $cols = !is_null( $this->atts['columns'] ) ? $this->atts['columns'] : $count;
455
 
 
 
456
  if ( apply_filters( 'dg_use_default_gallery_style', true ) ) {
457
  $itemwidth = $cols > 0 ? ( floor( 100 / $cols ) - 1 ) : 100;
458
+ $gallery_repl[1] .= " data-icon-width='$itemwidth'";
459
  }
460
 
461
  for ( $i = 0; $i < $count; $i += $cols ) {
462
+ $icon_repl[1] = '';
463
 
464
  $min = min( $i + $cols, $count );
465
  for ( $x = $i; $x < $min; $x ++ ) {
466
+ $icon_repl[1] .= $this->docs[ $x ];
467
  }
468
 
469
+ $gallery_repl[2] .= str_replace( $icon_find, $icon_repl, $icon_wrapper );
470
  }
471
  }
472
 
473
  // allow user to filter gallery wrapper
474
+ $gallery = apply_filters( 'dg_gallery_template', '<div id="%id%" class="%class%" %data%>' . PHP_EOL . '%rows%</div>', $this->useDescriptions() );
475
+
476
+ // build pagination section
477
+ // TODO: We should be using WP core get_the_posts_pagination()
478
+ if ( $this->atts['paginate'] && $this->atts['limit'] > 0 ) {
479
+ $left_href = $right_href = ' href="#"';
480
+ $left_tag = $right_tag = 'a';
481
+
482
+ if ( !$this->has_prev ) {
483
+ $left_href = '';
484
+ $left_tag = 'span';
485
+ }
486
+ if ( !$this->has_next ) {
487
+ $right_href = '';
488
+ $right_tag = 'span';
489
+ }
490
+
491
+ $prev = __( 'Prev', 'document-gallery' );
492
+ $next = __( 'Next', 'document-gallery' );
493
+ $gallery_repl[2] .= "<span class='pagination'><$left_tag$left_href class='paginate left'>$prev</$left_tag> | <$right_tag$right_href class='paginate right'>$next</$right_tag></span>";
494
+ $gallery_classes[] = 'dg-paginate-wrapper';
495
+ }
496
+
497
+ $gallery_repl[] = implode( ' ', $gallery_classes );
498
+
499
+ $comment = self::$comment;
500
+ if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
501
+ $ids = array();
502
+ foreach ( $this->docs as $doc ) {
503
+ $ids[] = $doc->getId();
504
+ }
505
+
506
+ $comment .= '<!-- Attachment IDs: ' . implode( $ids, ', ' ) . ' -->' . PHP_EOL;
507
+ }
508
 
509
+ return $comment . str_replace( $gallery_find, $gallery_repl, $gallery );
510
  }
511
  }
inc/class-image-editor-imagick.php CHANGED
@@ -32,9 +32,8 @@ class DG_Image_Editor_Imagick extends WP_Image_Editor_Imagick {
32
  $ret = parent::load();
33
 
34
  // set correct page number
35
- if ( ! is_wp_error( $ret ) && ! is_null( $this->pg )
36
- && is_callable( array( $this->image, 'setIteratorIndex' ) )
37
- ) {
38
  $err = __( 'Failed to set Imagick page number', 'document-gallery' );
39
 
40
  // setIteratorIndex() should return false on failure, but I've found
32
  $ret = parent::load();
33
 
34
  // set correct page number
35
+ if ( ! is_null( $this->pg ) && ! is_wp_error( $ret )
36
+ && is_callable( array( $this->image, 'setIteratorIndex' ) ) ) {
 
37
  $err = __( 'Failed to set Imagick page number', 'document-gallery' );
38
 
39
  // setIteratorIndex() should return false on failure, but I've found
inc/class-setup.php CHANGED
@@ -26,8 +26,6 @@ class DG_Setup {
26
 
27
  return array(
28
  'thumber' => array(
29
- // cached thumbnails, keyed by post ID
30
- 'thumbs' => array(),
31
  // Ghostscript path
32
  'gs' => $gs,
33
  // which thumbnail generation methods are available
@@ -40,28 +38,32 @@ class DG_Setup {
40
  'gallery' => array(
41
  // default: link directly to file (true to link to attachment pg)
42
  'attachment_pg' => false,
 
 
43
  // include the attachment description in output
44
  'descriptions' => false,
45
  // include thumbnail of actual document in gallery display
46
  'fancy' => true,
 
 
47
  // comma-delimited list of all mime types to be included
48
  'mime_types' => implode( ',', self::getDefaultMimeTypes() ),
 
 
49
  // ascending/descending order for included documents
50
  'order' => 'ASC',
51
  // which property to order by
52
  'orderby' => 'menu_order',
53
- // AND or OR
54
- 'relation' => 'AND',
55
  // the status the post must be in when returned by DG
56
  'post_status' => 'any',
57
  // the type of post to be returned
58
  'post_type' => 'attachment',
59
- // the max number of thumbnails to return
60
- 'limit' => - 1,
61
- // # of columns to be used in gallery
62
- 'columns' => 4,
63
- // whether to open documents in new window
64
- 'new_window' => false
65
  ),
66
  'css' => array(
67
  // plain text of CSS to be edited by user
@@ -70,6 +72,8 @@ class DG_Setup {
70
  'meta' => array(
71
  // current DG version
72
  'version' => DG_VERSION,
 
 
73
  // URL to donate to plugin development
74
  'donate_link' => $donate_link
75
  ),
@@ -100,7 +104,7 @@ class DG_Setup {
100
  }
101
 
102
  // version has historically been in two locations -- must check both to continue supporting upgrading from those old versions
103
- $old_version = array_key_exists( 'version', $dg_options ) ? $dg_options['version'] : $dg_options['meta']['version'];
104
  if ( ! is_null( $dg_options ) && DG_VERSION !== $old_version ) {
105
  DG_Logger::writeLog( DG_LogLevel::Detail, "Upgrading Document Gallery from version $old_version to " . DG_VERSION );
106
 
@@ -137,18 +141,14 @@ class DG_Setup {
137
  self::threePointThree( $options );
138
  self::threePointFour( $options );
139
  self::threePointFive( $options );
 
140
 
141
  // update plugin meta data
142
  $options['meta']['version'] = DG_VERSION;
143
  $options['meta']['donate_link'] = self::getDonateLink();
144
 
145
  // remove previously-failed thumbs
146
- $thumbs = $options['thumber']['thumbs'];
147
- foreach ( $thumbs as $k => $v ) {
148
- if ( empty( $v['thumber'] ) ) {
149
- unset( $options['thumber']['thumbs'][ $k ] );
150
- }
151
- }
152
 
153
  DocumentGallery::setOptions( $options, $blog );
154
  }
@@ -318,7 +318,6 @@ class DG_Setup {
318
 
319
  /**
320
  * Removes the validation option. Validation is now non-optional.
321
- * Adds the meta items_per_page default value.
322
  *
323
  * @param array $options The options to be modified.
324
  */
@@ -329,7 +328,7 @@ class DG_Setup {
329
  if ( ! DocumentGallery::isValidOptionsStructure( $options ) ) {
330
  DG_Logger::writeLog(
331
  DG_LogLevel::Error,
332
- "Found invalid options structure. Reverting to default options.",
333
  false,
334
  true );
335
  $options = self::getDefaultOptions();
@@ -349,6 +348,39 @@ class DG_Setup {
349
  }
350
  }
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  /**
353
  * Sets up Document Gallery on all blog(s) activated.
354
  *
@@ -425,17 +457,7 @@ class DG_Setup {
425
  * Runs when DG is uninstalled for an individual blog.
426
  */
427
  private static function _uninstall( $blog ) {
428
- $options = DG_Thumber::getOptions( $blog );
429
- if ( is_null( $options ) ) {
430
- return;
431
- }
432
-
433
- foreach ( $options['thumbs'] as $val ) {
434
- if ( isset( $val['thumber'] ) ) {
435
- @unlink( $val['thumb_path'] );
436
- }
437
- }
438
-
439
  DocumentGallery::deleteOptions( $blog );
440
  }
441
 
26
 
27
  return array(
28
  'thumber' => array(
 
 
29
  // Ghostscript path
30
  'gs' => $gs,
31
  // which thumbnail generation methods are available
38
  'gallery' => array(
39
  // default: link directly to file (true to link to attachment pg)
40
  'attachment_pg' => false,
41
+ // # of columns to be used in gallery
42
+ 'columns' => 4,
43
  // include the attachment description in output
44
  'descriptions' => false,
45
  // include thumbnail of actual document in gallery display
46
  'fancy' => true,
47
+ // the max number of thumbnails to return
48
+ 'limit' => -1,
49
  // comma-delimited list of all mime types to be included
50
  'mime_types' => implode( ',', self::getDefaultMimeTypes() ),
51
+ // whether to open documents in new window
52
+ 'new_window' => false,
53
  // ascending/descending order for included documents
54
  'order' => 'ASC',
55
  // which property to order by
56
  'orderby' => 'menu_order',
57
+ // whether to paginate galleries with a "limit"
58
+ 'paginate' => true,
59
  // the status the post must be in when returned by DG
60
  'post_status' => 'any',
61
  // the type of post to be returned
62
  'post_type' => 'attachment',
63
+ // AND or OR
64
+ 'relation' => 'AND',
65
+ // how many documents to skip
66
+ 'skip' => 0
 
 
67
  ),
68
  'css' => array(
69
  // plain text of CSS to be edited by user
72
  'meta' => array(
73
  // current DG version
74
  'version' => DG_VERSION,
75
+ // items per page at Thumbnail Management tab
76
+ 'items_per_page' => 10,
77
  // URL to donate to plugin development
78
  'donate_link' => $donate_link
79
  ),
104
  }
105
 
106
  // version has historically been in two locations -- must check both to continue supporting upgrading from those old versions
107
+ $old_version = isset( $dg_options['version'] ) ? $dg_options['version'] : $dg_options['meta']['version'];
108
  if ( ! is_null( $dg_options ) && DG_VERSION !== $old_version ) {
109
  DG_Logger::writeLog( DG_LogLevel::Detail, "Upgrading Document Gallery from version $old_version to " . DG_VERSION );
110
 
141
  self::threePointThree( $options );
142
  self::threePointFour( $options );
143
  self::threePointFive( $options );
144
+ self::fourPointZero( $options );
145
 
146
  // update plugin meta data
147
  $options['meta']['version'] = DG_VERSION;
148
  $options['meta']['donate_link'] = self::getDonateLink();
149
 
150
  // remove previously-failed thumbs
151
+ DG_Thumb::purgeFailedThumbs();
 
 
 
 
 
152
 
153
  DocumentGallery::setOptions( $options, $blog );
154
  }
318
 
319
  /**
320
  * Removes the validation option. Validation is now non-optional.
 
321
  *
322
  * @param array $options The options to be modified.
323
  */
328
  if ( ! DocumentGallery::isValidOptionsStructure( $options ) ) {
329
  DG_Logger::writeLog(
330
  DG_LogLevel::Error,
331
+ 'Found invalid options structure. Reverting to default options.',
332
  false,
333
  true );
334
  $options = self::getDefaultOptions();
348
  }
349
  }
350
 
351
+ /**
352
+ * Adds the meta items_per_page default value.
353
+ * Paginate & skip options were added.
354
+ * Moving cached thumbs into postmeta table.
355
+ *
356
+ * @param array $options The options to be modified.
357
+ */
358
+ private static function fourPointZero( &$options ) {
359
+ if ( version_compare( $options['meta']['version'], '4.0', '<' ) ) {
360
+ $options['gallery']['paginate'] = true;
361
+ $options['gallery']['skip'] = 0;
362
+ $options['meta']['items_per_page'] = 10;
363
+
364
+ $upload_dir = wp_upload_dir();
365
+ $upload_len = strlen( $upload_dir['basedir'] );
366
+ $dimensions = $options['thumber']['width'] . 'x' . $options['thumber']['height'];
367
+ foreach ( $options['thumber']['thumbs'] as $id => $thumb ) {
368
+ $thumb_obj = new DG_Thumb();
369
+ $thumb_obj->setPostId( $id );
370
+ $thumb_obj->setTimestamp( $thumb['timestamp'] );
371
+ $thumb_obj->setDimensions( $dimensions );
372
+ if ( isset( $thumb['thumb_path'] ) ) {
373
+ $thumb_obj->setRelativePath( substr( $thumb['thumb_path'], $upload_len + 1 ) );
374
+ $thumb_obj->setGenerator( DG_Util::callableToString( $thumb['thumber'] ) );
375
+ }
376
+
377
+ $thumb_obj->save();
378
+ }
379
+
380
+ unset( $options['thumber']['thumbs'] );
381
+ }
382
+ }
383
+
384
  /**
385
  * Sets up Document Gallery on all blog(s) activated.
386
  *
457
  * Runs when DG is uninstalled for an individual blog.
458
  */
459
  private static function _uninstall( $blog ) {
460
+ DG_Thumb::purgeThumbs( null, $blog );
 
 
 
 
 
 
 
 
 
 
461
  DocumentGallery::deleteOptions( $blog );
462
  }
463
 
inc/class-thumb.php ADDED
@@ -0,0 +1,412 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Holds data specific to a specific thumbnail
5
+ *
6
+ * @author drossiter
7
+ */
8
+ class DG_Thumb {
9
+
10
+ /**
11
+ * TODO: Replace w/ https://codex.wordpress.org/Class_Reference/WP_Object_Cache
12
+ * @var array The cached copy of thumbs.
13
+ */
14
+ private static $thumbs = null;
15
+
16
+ /**
17
+ * @var string The meta key identifying DG thumbs.
18
+ */
19
+ private static $meta_key = '_dg_thumbnail';
20
+
21
+ /**
22
+ * @var int The DB row ID.
23
+ */
24
+ private $meta_id;
25
+
26
+ /**
27
+ * @var int The post (attachment) ID.
28
+ */
29
+ private $post_id;
30
+
31
+ /**
32
+ * @return int The post ID.
33
+ */
34
+ public function getPostId() {
35
+ return $this->post_id;
36
+ }
37
+
38
+ /**
39
+ * @param $post_id int The post ID.
40
+ */
41
+ public function setPostId($post_id) {
42
+ $this->post_id = $post_id;
43
+ }
44
+
45
+ /**
46
+ * @var string The thumb dimensions (format: WIDTHxHEIGHT).
47
+ */
48
+ private $dimensions = '';
49
+
50
+ /**
51
+ * @return string The dimensions (WIDTHxHEIGHT).
52
+ */
53
+ public function getDimensions() {
54
+ return $this->dimensions;
55
+ }
56
+
57
+ /**
58
+ * @param $dimensions string The dimensions (WIDTHxHEIGHT).
59
+ */
60
+ public function setDimensions($dimensions) {
61
+ $this->dimensions = $dimensions;
62
+ }
63
+
64
+ /**
65
+ * @var string The path relative to the WP uploads directory.
66
+ */
67
+ private $relative_path = '';
68
+
69
+ /**
70
+ * @return string The path relative to the WP uploads directory.
71
+ */
72
+ public function getRelativePath() {
73
+ return $this->relative_path;
74
+ }
75
+
76
+ /**
77
+ * @param $relative_path string The path relative to the WP uploads directory.
78
+ */
79
+ public function setRelativePath($relative_path) {
80
+ $this->relative_path = $relative_path;
81
+ }
82
+
83
+ /**
84
+ * @var int The time when thumbnail was generated.
85
+ */
86
+ private $timestamp;
87
+
88
+ /**
89
+ * @return int The time when thumbnail was generated.
90
+ */
91
+ public function getTimestamp() {
92
+ return $this->timestamp;
93
+ }
94
+
95
+ /**
96
+ * @param $timestamp int The time when thumbnail was generated.
97
+ */
98
+ public function setTimestamp($timestamp) {
99
+ $this->timestamp = $timestamp;
100
+ }
101
+
102
+ /**
103
+ * @var string The function used in generating this thumb.
104
+ */
105
+ private $generator = '';
106
+
107
+ /**
108
+ * @return string The function used in generating this thumb.
109
+ */
110
+ public function getGenerator() {
111
+ return $this->generator;
112
+ }
113
+
114
+ /**
115
+ * @param $generator string The function used in generating this thumb.
116
+ */
117
+ public function setGenerator($generator) {
118
+ $this->generator = $generator;
119
+ }
120
+
121
+ /**
122
+ * DG_Thumb constructor.
123
+ * @param $db_row object The result of querying WP postmeta.
124
+ */
125
+ public function __construct($db_row = null) {
126
+ if ( is_null ($db_row) ) return;
127
+
128
+ $this->meta_id = $db_row->meta_id;
129
+ $this->post_id = $db_row->post_id;
130
+ $this->setMetaValue( $db_row->meta_value );
131
+ }
132
+
133
+ /**
134
+ * Meta value is in the following format: [width]x[height]:[timestamp]:[path]:[generator].
135
+ * Note that generator may contain colons, so parsing of this field must be done carefully.
136
+ *
137
+ * @param $meta_value string Populates the dimensions and path from meta_value.
138
+ */
139
+ private function setMetaValue($meta_value) {
140
+ $split = explode( ':', $meta_value, 4 );
141
+ $this->dimensions = $split[0];
142
+ $this->timestamp = absint( $split[1] );
143
+ $this->relative_path = $split[2];
144
+ $this->generator = $split[3];
145
+ }
146
+
147
+ /**
148
+ * Meta value is in the following format: [width]x[height]:[timestamp]:[path]:[generator].
149
+ *
150
+ * @return string The meta_value generated from the dimensions & path fields.
151
+ */
152
+ private function getMetaValue() {
153
+ return $this->dimensions . ':' . $this->timestamp . ':' . $this->relative_path . ':' . $this->generator;
154
+ }
155
+
156
+ /**
157
+ * @return int Parses dimensions string pulling out the width.
158
+ */
159
+ public function getWidth() {
160
+ $split = explode( 'x', $this->dimensions, 2 );
161
+ return absint( $split[0] );
162
+ }
163
+
164
+ /**
165
+ * @return int Parses dimensions string pulling out the height.
166
+ */
167
+ public function getHeight() {
168
+ $split = explode( 'x', $this->dimensions, 2 );
169
+ return absint( $split[1] );
170
+ }
171
+
172
+ /**
173
+ * @return string Get fully-qualified URL pointing to this thumb.
174
+ */
175
+ public function getUrl() {
176
+ $upload_dir = wp_upload_dir();
177
+ return $upload_dir['baseurl'] . '/' . $this->relative_path;
178
+ }
179
+
180
+ /**
181
+ * @return string Get fully-qualified system path pointing to this thumb.
182
+ */
183
+ public function getPath() {
184
+ $upload_dir = wp_upload_dir();
185
+ return $upload_dir['basedir'] . DIRECTORY_SEPARATOR . $this->relative_path;
186
+ }
187
+
188
+ /**
189
+ * @return bool Whether this instance represents a successful thumb generation.
190
+ */
191
+ public function isSuccess() {
192
+ return ! empty( $this->relative_path );
193
+ }
194
+
195
+ /**
196
+ * Saves thumb to DB. Either an UPDATE or an INSERT is performed depending on whether meta_id isset and
197
+ * updates the instance meta_id if INSERT is performed.
198
+ */
199
+ public function save() {
200
+ global $wpdb;
201
+
202
+ // thumbs are immutable -- once created they can only be read or deleted
203
+ if ( isset( $this->meta_id ) ) return;
204
+
205
+ // post_id + dimensions must be unique so purge the old entry if one exists
206
+ $old_thumb = self::getThumb( $this->post_id, $this->getWidth(), $this->getHeight() );
207
+ if ( ! is_null( $old_thumb ) ) {
208
+ $old_thumb->delete();
209
+ }
210
+
211
+ DG_Logger::writeLog( DG_LogLevel::Detail, 'Saving thumb with post_id = ' . $this->post_id );
212
+
213
+ // perform save to DB
214
+ $values = array( 'post_id' => $this->post_id, 'meta_key' => self::$meta_key, 'meta_value' => $this->getMetaValue() );
215
+ $value_formats = array( '%d', '%s', '%s' );
216
+ $wpdb->insert( $wpdb->postmeta, $values, $value_formats );
217
+ $this->meta_id = $wpdb->insert_id;
218
+
219
+ self::initThumbs();
220
+ if ( ! isset( self::$thumbs[$this->post_id] ) ) {
221
+ self::$thumbs[$this->post_id] = array();
222
+ }
223
+
224
+ self::$thumbs[$this->post_id][$this->dimensions] = $this;
225
+ }
226
+
227
+ /**
228
+ * Deletes the current instances from the DB and filesystem.
229
+ */
230
+ public function delete() {
231
+ if ( ! isset( $this->meta_id ) ) return;
232
+
233
+ DG_Logger::writeLog( DG_LogLevel::Detail, 'Deleting thumb with post_id = ' . $this->post_id );
234
+
235
+ global $wpdb;
236
+ $wpdb->delete( $wpdb->postmeta, array( 'meta_id' => $this->meta_id ), array( '%d' ) );
237
+ self::cleanupThumbFiles( $this );
238
+ unset( $this->meta_id );
239
+ if ( 1 === sizeof( self::$thumbs[$this->post_id] ) ) {
240
+ unset( self::$thumbs[$this->post_id] );
241
+ } else {
242
+ unset( self::$thumbs[$this->post_id][$this->dimensions] );
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Whether the given attachment has a thumb graphic.
248
+ *
249
+ * @param $ID int The id of the attachment to be checked.
250
+ * @param $dimensions string Optional. The dimensions for the thumbnail we're seeking.
251
+ * @param $success_matters bool Optional. Whether return value should be false when DG_Thumb exists, but is not successful.
252
+ * @return bool Whether the given attachment has a thumbnail image.
253
+ */
254
+ public static function thumbExists($ID, $dimensions = null, $success_matters = true) {
255
+ $thumb = self::getThumb( $ID, $dimensions );
256
+ return ! is_null( $thumb ) && ( ! $success_matters || $thumb->isSuccess() );
257
+ }
258
+
259
+ /**
260
+ * Whether the given attachment has a thumb graphic.
261
+ *
262
+ * @param $ID int The id of the attachment to be checked.
263
+ * @param $dimensions string The dimensions for the thumbnail we're seeking.
264
+ * @return DG_Thumb|null The thumbnail at the requested dimensions.
265
+ */
266
+ public static function getThumb($ID, $dimensions = null) {
267
+ $thumbs = self::getThumbs();
268
+ $ret = null;
269
+ if ( isset( $thumbs[$ID] ) && ( is_null( $dimensions ) || isset( $thumbs[$ID][$dimensions] ) ) ) {
270
+ $ret = ! is_null( $dimensions ) ? $thumbs[$ID][$dimensions] : reset( $thumbs[$ID] );
271
+ }
272
+
273
+ return $ret;
274
+ }
275
+
276
+ /**
277
+ * Initializes the thumbs variable if not already initialized.
278
+ */
279
+ private static function initThumbs() {
280
+ if ( !isset( self::$thumbs ) ) {
281
+ DG_Logger::writeLog( DG_LogLevel::Detail, 'Populating thumbnail cache.' );
282
+ global $wpdb;
283
+ self::$thumbs = array();
284
+
285
+ $meta_key = self::$meta_key;
286
+ $sql = "SELECT post_id, meta_id, meta_value FROM $wpdb->postmeta WHERE meta_key = '$meta_key'";
287
+ foreach ( $wpdb->get_results( $sql ) as $row ) {
288
+ $key = $row->post_id;
289
+ $new = new DG_Thumb( $row );
290
+ if ( ! isset( self::$thumbs[$key] ) ) {
291
+ self::$thumbs[$key] = array();
292
+ } elseif ( isset( self::$thumbs[$key][$new->dimensions] ) ) {
293
+ // it is possible to end up with duplicate thumbnails -- cleanup here
294
+ $old = self::$thumbs[$key][$new->dimensions];
295
+ if ( $old->timestamp < $new->timestamp ) {
296
+ $old->delete();
297
+ } else {
298
+ $new->delete();
299
+ continue;
300
+ }
301
+ }
302
+
303
+ self::$thumbs[$key][$new->dimensions] = $new;
304
+ }
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Gets either a nested associative array mapping ID to dimension to thumb or an associative array mapping ID to thumb.
310
+ *
311
+ * @param $dimensions string WIDTHxHEIGHT
312
+ * @return array The matched thumbs.
313
+ */
314
+ public static function getThumbs($dimensions = null) {
315
+ self::initThumbs();
316
+ if ( is_null( $dimensions ) ) {
317
+ $ret = self::$thumbs;
318
+ } else {
319
+ $ret = array();
320
+ foreach ( self::$thumbs as $thumbs ) {
321
+ if ( isset( $thumbs[$dimensions] ) ) {
322
+ $thumb = $thumbs[$dimensions];
323
+ $ret[$thumb->post_id] = $thumb;
324
+ }
325
+ }
326
+ }
327
+
328
+ return $ret;
329
+ }
330
+
331
+ /**
332
+ * Removes thumbs from the DB.
333
+ * @param $ids array|int|null Optional. The post IDs to be purged. If not given then all are purged.
334
+ * @param $blog_id null|int Optional. The blog to purge from. Defaults to active blog.
335
+ */
336
+ public static function purgeThumbs($ids = null, $blog_id = null) {
337
+ global $wpdb;
338
+
339
+ if ( ! is_null( $ids ) ) {
340
+ $ids = (array)$ids;
341
+ }
342
+
343
+ $postmeta = $wpdb->get_blog_prefix( $blog_id ) . 'postmeta';
344
+ $meta_key = self::$meta_key;
345
+
346
+ self::initThumbs();
347
+
348
+ if ( ! is_array( $ids ) ) {
349
+ $sql = "DELETE FROM $postmeta WHERE meta_key = '$meta_key'";
350
+ $result = $wpdb->query( $sql );
351
+
352
+ if ( $result ) {
353
+ // cleanup filesystem
354
+ foreach ( self::$thumbs as $thumbs ) {
355
+ self::cleanupThumbFiles( $thumbs );
356
+ }
357
+
358
+ self::$thumbs = null;
359
+ }
360
+ } else {
361
+ $sql = "DELETE FROM $postmeta WHERE meta_key = '$meta_key' AND post_id IN(" . rtrim( str_repeat( '%d,', sizeof( $ids ) ), ',' ) . ")";
362
+ $result = $wpdb->query( $wpdb->prepare( $sql, $ids ) );
363
+
364
+ if ( $result ) {
365
+ foreach ( $ids as $id ) {
366
+ if ( isset( self::$thumbs[$id] ) ) {
367
+ self::cleanupThumbFiles( self::$thumbs[$id] );
368
+ unset( self::$thumbs[$id] );
369
+ }
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Delete all thumb entries that do not have an actual image associated with them due to failed generation.
377
+ */
378
+ public static function purgeFailedThumbs() {
379
+ global $wpdb;
380
+ $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_key = '" . self::$meta_key . "' AND meta_value LIKE '%:%::'" );
381
+
382
+ // force re-query next time
383
+ self::$thumbs = null;
384
+ }
385
+
386
+ /**
387
+ * Removes meta data associated w/ attachment.
388
+ *
389
+ * @param $id int The post ID.
390
+ */
391
+ public static function cleanupAttachmentMeta($id) {
392
+ $thumb = self::getThumb( $id );
393
+ if ( ! is_null( $thumb ) ) {
394
+ self::cleanupThumbFiles($thumb);
395
+ }
396
+ }
397
+
398
+ /**
399
+ * @param $thumbs array|DG_Thumb Removes files associated with given thumb(s).
400
+ */
401
+ private static function cleanupThumbFiles($thumbs) {
402
+ if ( is_a( $thumbs, __CLASS__ ) ) {
403
+ $thumbs = array( $thumbs );
404
+ }
405
+
406
+ foreach ( $thumbs as $thumb ) {
407
+ if ( isset( $thumb->relative_path ) ) {
408
+ @unlink( $thumb->getPath() );
409
+ }
410
+ }
411
+ }
412
+ }
inc/class-thumber.php CHANGED
@@ -38,7 +38,7 @@ class DG_Thumber {
38
  * @param string $path System path to thumbnail.
39
  * @param string $generator Descriptor for generation method -- usually method name.
40
  *
41
- * @return bool Whether set was successful.
42
  */
43
  public static function setThumbnail( $ID, $path, $generator = 'unknown' ) {
44
  return self::thumbnailGenerationHarness( $generator, $ID, $path );
@@ -50,9 +50,12 @@ class DG_Thumber {
50
  * @param int $ID Document ID.
51
  */
52
  public static function setThumbnailFailed( $ID ) {
53
- $options = self::getOptions();
54
- $options['thumbs'][ $ID ] = array( 'timestamp' => time() );
55
- self::setOptions( $options );
 
 
 
56
  }
57
 
58
  /**
@@ -61,14 +64,16 @@ class DG_Thumber {
61
  * @param int $ID Document ID
62
  * @param int $pg Page number to get thumb from.
63
  * @param bool $generate_if_missing Whether to attempt generating the thumbnail if missing.
 
64
  *
65
- * @return string URL to the thumbnail.
66
  */
67
- public static function getThumbnail( $ID, $pg = 1, $generate_if_missing = true ) {
68
  $options = self::getOptions();
 
69
 
70
  // if we haven't saved a thumb, generate one
71
- if ( empty( $options['thumbs'][ $ID ] ) ) {
72
  // short-circuit generation if not required
73
  if ( ! $generate_if_missing ) {
74
  return null;
@@ -83,38 +88,37 @@ class DG_Thumber {
83
  if ( preg_match( $ext_preg, $file ) ) {
84
  if ( DG_Logger::logEnabled() ) {
85
  $toLog = sprintf( __( 'Attempting to generate thumbnail for attachment #%d with (%s)',
86
- 'document-gallery' ), $ID, is_array( $thumber ) ? implode( '::', $thumber ) : print_r( $thumber, true ) );
87
  DG_Logger::writeLog( DG_LogLevel::Detail, $toLog );
88
  }
89
 
90
  if ( self::thumbnailGenerationHarness( $thumber, $ID, $pg ) ) {
91
- // harness updates options so we need a new copy
92
- $options = self::getOptions();
93
  break;
94
  }
95
  }
96
  }
97
  }
98
 
99
- $new = empty( $options['thumbs'][ $ID ] );
100
- if ( $new || empty( $options['thumbs'][ $ID ]['thumber'] ) ) {
101
- if ( $new ) {
102
  self::setThumbnailFailed( $ID );
103
  }
104
 
105
  // fallback to default thumb for attachment type
106
  $url = self::getDefaultThumbnail( $ID, $pg );
 
107
  } else {
108
- // use generated thumbnail
109
- $url = $options['thumbs'][ $ID ]['thumb_url'];
110
  }
111
 
112
  return $url;
113
  }
114
 
115
  /*==========================================================================
116
- * AUDIO VIDEO THUMBNAILS
117
- *=========================================================================*/
118
 
119
  /**
120
  * Uses wp_read_video_metadata() and wp_read_audio_metadata() to retrieve
@@ -180,8 +184,8 @@ class DG_Thumber {
180
  }
181
 
182
  /*==========================================================================
183
- * IMAGICK THUMBNAILS
184
- *=========================================================================*/
185
 
186
  /**
187
  * Uses WP_Image_Editor_Imagick to generate thumbnails.
@@ -367,8 +371,8 @@ class DG_Thumber {
367
  }
368
 
369
  /*==========================================================================
370
- * DEFAULT THUMBNAILS
371
- *=========================================================================*/
372
 
373
  /**
374
  * Get thumbnail for document with given ID from default images.
@@ -467,22 +471,24 @@ class DG_Thumber {
467
  }
468
 
469
  /*==========================================================================
470
- * GENERAL THUMBNAIL HELPER FUNCTIONS
471
- *=========================================================================*/
472
 
473
  /**
474
  * @return array WP_Post objects for each attachment that has been processed.
475
  */
476
  public static function getThumbed() {
477
- $options = self::getOptions();
478
- $args = array(
 
 
479
  'post_type' => 'attachment',
480
  'post_status' => 'inherit',
481
  'post_per_page' => - 1,
482
- 'post__in' => array_keys( $options['thumbs'] )
483
  );
484
 
485
- return count( $args['post__in'] ) ? get_posts( $args ) : array();
486
  }
487
 
488
  /**
@@ -594,7 +600,7 @@ class DG_Thumber {
594
  * @param int $ID ID for the attachment that we need a thumbnail for.
595
  * @param int|string $pg Page number of the attachment to get a thumbnail for or the system path to the image to be used.
596
  *
597
- * @return bool Whether generation was successful.
598
  */
599
  private static function thumbnailGenerationHarness( $generator, $ID, $pg = 1 ) {
600
  // handle system page in $pg variable
@@ -607,7 +613,6 @@ class DG_Thumber {
607
 
608
  // get some useful stuff
609
  $doc_path = get_attached_file( $ID );
610
- $doc_url = wp_get_attachment_url( $ID );
611
  $dirname = dirname( $doc_path );
612
  $basename = basename( $doc_path );
613
  if ( false === ( $len = strrpos( $basename, '.' ) ) ) {
@@ -617,7 +622,7 @@ class DG_Thumber {
617
  $ext = self::getExt( $temp_path );
618
 
619
  $thumb_name = self::getUniqueThumbName( $dirname, $extless, $ext );
620
- $thumb_path = $dirname . DIRECTORY_SEPARATOR . $thumb_name;
621
 
622
  // scale generated image down
623
  $img = wp_get_image_editor( $temp_path );
@@ -645,18 +650,19 @@ class DG_Thumber {
645
 
646
  // do some cleanup
647
  @unlink( $temp_path );
648
- self::deleteThumbMeta( $ID );
649
-
650
- // store new thumbnail in DG options
651
- $options['thumbs'][ $ID ] = array(
652
- 'timestamp' => time(),
653
- 'thumb_url' => preg_replace( '#' . preg_quote( $basename ) . '$#', $thumb_name, $doc_url ),
654
- 'thumb_path' => $thumb_path,
655
- 'thumber' => $generator
656
- );
657
- self::setOptions( $options );
658
 
659
- return true;
 
 
 
 
 
 
 
 
 
 
 
660
  }
661
 
662
  /**
@@ -692,36 +698,6 @@ class DG_Thumber {
692
  return wp_unique_filename( $dirname, str_replace( '.', '-', $extless ) . '-thumb.' . $ext );
693
  }
694
 
695
- /**
696
- * Removes the existing thumbnail/document meta for the attachment(s)
697
- * with the ID(s), if such a thumbnails exists.
698
- *
699
- * @param int|array $ids
700
- *
701
- * @return array All IDs that were deleted -- some subset of IDs requested to be deleted.
702
- */
703
- public static function deleteThumbMeta( $ids ) {
704
- $options = self::getOptions();
705
-
706
- $deleted = array();
707
- foreach ( (array) $ids as $id ) {
708
- if ( isset( $options['thumbs'][ $id ] ) ) {
709
- if ( isset( $options['thumbs'][ $id ]['thumber'] ) ) {
710
- @unlink( $options['thumbs'][ $id ]['thumb_path'] );
711
- }
712
-
713
- unset( $options['thumbs'][ $id ] );
714
- $deleted[] = $id;
715
- }
716
- }
717
-
718
- if ( count( $deleted ) > 0 ) {
719
- self::setOptions( $options );
720
- }
721
-
722
- return $deleted;
723
- }
724
-
725
  /**
726
  * Checks whether exec() may be used.
727
  * Source: http://stackoverflow.com/a/12980534/866618
38
  * @param string $path System path to thumbnail.
39
  * @param string $generator Descriptor for generation method -- usually method name.
40
  *
41
+ * @return DG_Thumb|bool Thumb on success, false on failure.
42
  */
43
  public static function setThumbnail( $ID, $path, $generator = 'unknown' ) {
44
  return self::thumbnailGenerationHarness( $generator, $ID, $path );
50
  * @param int $ID Document ID.
51
  */
52
  public static function setThumbnailFailed( $ID ) {
53
+ $options = self::getOptions();
54
+ $thumb = new DG_Thumb();
55
+ $thumb->setPostId( $ID );
56
+ $thumb->setTimestamp( time() );
57
+ $thumb->setDimensions( $options['width'] . 'x' . $options['height'] );
58
+ $thumb->save();
59
  }
60
 
61
  /**
64
  * @param int $ID Document ID
65
  * @param int $pg Page number to get thumb from.
66
  * @param bool $generate_if_missing Whether to attempt generating the thumbnail if missing.
67
+ * @param bool|null &$is_default Whether the returned URL points to a default icon.
68
  *
69
+ * @return string URL to the thumbnail.
70
  */
71
+ public static function getThumbnail( $ID, $pg = 1, $generate_if_missing = true, &$is_default = null ) {
72
  $options = self::getOptions();
73
+ $dimensions = $options['width'] . 'x' . $options['height'];
74
 
75
  // if we haven't saved a thumb, generate one
76
+ if ( ! DG_Thumb::thumbExists( $ID, $dimensions, false ) ) {
77
  // short-circuit generation if not required
78
  if ( ! $generate_if_missing ) {
79
  return null;
88
  if ( preg_match( $ext_preg, $file ) ) {
89
  if ( DG_Logger::logEnabled() ) {
90
  $toLog = sprintf( __( 'Attempting to generate thumbnail for attachment #%d with (%s)',
91
+ 'document-gallery' ), $ID, DG_Util::callableToString( $thumber ) );
92
  DG_Logger::writeLog( DG_LogLevel::Detail, $toLog );
93
  }
94
 
95
  if ( self::thumbnailGenerationHarness( $thumber, $ID, $pg ) ) {
 
 
96
  break;
97
  }
98
  }
99
  }
100
  }
101
 
102
+ $thumb = DG_Thumb::getThumb( $ID, $dimensions );
103
+ if ( is_null( $thumb ) || ! $thumb->isSuccess() ) {
104
+ if ( is_null( $thumb ) ) {
105
  self::setThumbnailFailed( $ID );
106
  }
107
 
108
  // fallback to default thumb for attachment type
109
  $url = self::getDefaultThumbnail( $ID, $pg );
110
+ $is_default = true;
111
  } else {
112
+ $url = $thumb->getUrl();
113
+ $is_default = false;
114
  }
115
 
116
  return $url;
117
  }
118
 
119
  /*==========================================================================
120
+ * AUDIO VIDEO THUMBNAILS
121
+ *=========================================================================*/
122
 
123
  /**
124
  * Uses wp_read_video_metadata() and wp_read_audio_metadata() to retrieve
184
  }
185
 
186
  /*==========================================================================
187
+ * IMAGICK THUMBNAILS
188
+ *=========================================================================*/
189
 
190
  /**
191
  * Uses WP_Image_Editor_Imagick to generate thumbnails.
371
  }
372
 
373
  /*==========================================================================
374
+ * DEFAULT THUMBNAILS
375
+ *=========================================================================*/
376
 
377
  /**
378
  * Get thumbnail for document with given ID from default images.
471
  }
472
 
473
  /*==========================================================================
474
+ * GENERAL THUMBNAIL HELPER FUNCTIONS
475
+ *=========================================================================*/
476
 
477
  /**
478
  * @return array WP_Post objects for each attachment that has been processed.
479
  */
480
  public static function getThumbed() {
481
+ $thumbs = DG_Thumb::getThumbs();
482
+ if ( ! count( $thumbs ) ) return array();
483
+
484
+ $args = array(
485
  'post_type' => 'attachment',
486
  'post_status' => 'inherit',
487
  'post_per_page' => - 1,
488
+ 'post__in' => array_keys( $thumbs )
489
  );
490
 
491
+ return get_posts( $args );
492
  }
493
 
494
  /**
600
  * @param int $ID ID for the attachment that we need a thumbnail for.
601
  * @param int|string $pg Page number of the attachment to get a thumbnail for or the system path to the image to be used.
602
  *
603
+ * @return DG_Thumb|bool The generated thumbnail or false on failure.
604
  */
605
  private static function thumbnailGenerationHarness( $generator, $ID, $pg = 1 ) {
606
  // handle system page in $pg variable
613
 
614
  // get some useful stuff
615
  $doc_path = get_attached_file( $ID );
 
616
  $dirname = dirname( $doc_path );
617
  $basename = basename( $doc_path );
618
  if ( false === ( $len = strrpos( $basename, '.' ) ) ) {
622
  $ext = self::getExt( $temp_path );
623
 
624
  $thumb_name = self::getUniqueThumbName( $dirname, $extless, $ext );
625
+ $thumb_path = "$dirname/$thumb_name";
626
 
627
  // scale generated image down
628
  $img = wp_get_image_editor( $temp_path );
650
 
651
  // do some cleanup
652
  @unlink( $temp_path );
 
 
 
 
 
 
 
 
 
 
653
 
654
+ // save new thumb
655
+ DG_Logger::writeLog( DG_LogLevel::Detail, 'Creating thumb object.' );
656
+ $upload = wp_upload_dir();
657
+ $thumb = new DG_Thumb();
658
+ $thumb->setPostId( $ID );
659
+ $thumb->setDimensions( $options['width'] . 'x' . $options['height'] );
660
+ $thumb->setTimestamp( time() );
661
+ $thumb->setRelativePath( substr( $thumb_path, strlen( $upload['basedir'] ) + 1 ) );
662
+ $thumb->setGenerator( DG_Util::callableToString( $generator ) );
663
+ $thumb->save();
664
+
665
+ return $thumb;
666
  }
667
 
668
  /**
698
  return wp_unique_filename( $dirname, str_replace( '.', '-', $extless ) . '-thumb.' . $ext );
699
  }
700
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
  /**
702
  * Checks whether exec() may be used.
703
  * Source: http://stackoverflow.com/a/12980534/866618
inc/class-util.php CHANGED
@@ -35,18 +35,6 @@ class DG_Util {
35
  return $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
36
  }
37
 
38
- /**
39
- * Whether the given attachment has a thumbanil graphic.
40
- *
41
- * @param $ID int The id of the attachment to be checked.
42
- * @return bool Whether the given attachment has a thumbnail image.
43
- */
44
- public static function hasThumb($ID) {
45
- $options = DocumentGallery::getOptions();
46
- $thumbs = $options['thumber']['thumbs'];
47
- return array_key_exists($ID, $thumbs) && ! empty( $thumbs[ $ID ]['thumber'] );
48
- }
49
-
50
  /**
51
  * @param mixed $maybeint Data you wish to have converted to a positive integer.
52
  *
@@ -107,14 +95,24 @@ class DG_Util {
107
  * @param bool $in_footer For scripts, dictates whether to put in footer.
108
  */
109
  public static function enqueueAsset( $handle, $src, $deps = array(), $in_footer = true ) {
110
- if ( !defined('WP_DEBUG') || !WP_DEBUG ) {
111
- $src = preg_replace('/^(.*)\.(css|js)$/', '$1.min.$2', $src, 1);
112
- }
113
-
114
- if ( preg_match( '/.js/', $src ) ) {
115
- wp_enqueue_script( $handle, DG_URL . $src, $deps, DG_VERSION, $in_footer );
116
  } else {
117
- wp_enqueue_style( $handle, DG_URL . $src, $deps, DG_VERSION );
 
 
 
 
 
 
 
 
 
 
 
 
118
  }
 
119
  }
120
  }
35
  return $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
36
  }
37
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  /**
39
  * @param mixed $maybeint Data you wish to have converted to a positive integer.
40
  *
95
  * @param bool $in_footer For scripts, dictates whether to put in footer.
96
  */
97
  public static function enqueueAsset( $handle, $src, $deps = array(), $in_footer = true ) {
98
+ $src = self::getAssetPath( $src );
99
+ if ( stripos( strrev( $src ), 'sj.' ) === 0 ) {
100
+ wp_enqueue_script( $handle, $src, $deps, DG_VERSION, $in_footer );
 
 
 
101
  } else {
102
+ wp_enqueue_style( $handle, $src, $deps, DG_VERSION );
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Converts path to min version when WP is not running in debug mode and fully-qualifies path.
108
+ *
109
+ * @param $src string Relative path to asset from DG_URL.
110
+ * @return string The fully-qualified, potentially min version of the given path.
111
+ */
112
+ public static function getAssetPath( $src ) {
113
+ if ( !defined('WP_DEBUG') || !WP_DEBUG ) {
114
+ $src = preg_replace( '/^(.*)\.(css|js)$/', '$1.min.$2', $src, 1 );
115
  }
116
+ return DG_URL . $src;
117
  }
118
  }