Document Gallery - Version 3.3

Version Description

  • Enhancement: Developers using the Document Gallery API now have access to new values when using the dg_icon_template filter. (Thanks to pierowbmstr!)
  • Bug Fix: Resolved some advanced CSS commands (e.g.: calc()) breaking in custom CSS.
  • Bug Fix: Some DG options were not being saved correctly resulting in odd behavior in some edge cases.
  • Bug Fix: Resolved Media Manager integration not being available when first creating a post.
Download this release

Release Info

Developer demur
Plugin Icon 128x128 Document Gallery
Version 3.3
Comparing to
See all releases

Code changes from version 3.2 to 3.3

README.txt CHANGED
@@ -4,7 +4,7 @@ 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: 3.6
6
  Tested up to: 4.2.2
7
- Stable tag: 3.2
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -280,10 +280,15 @@ which are replaced during gallery generation with document-specific data.
280
  These tags are as follows:
281
 
282
  * **%link%**: The URL that will be loaded when the user clicks the icon.
 
283
  * **%img%**: The URL pointing the the image that will be displayed.
284
  * **%title%**: The human-readable title of the attachment.
285
  * **%title_attribute%**: The escaped title (above), safe for using HTML tag attributes.
286
  * **%description%**: The attachment description (only present when rendering descriptions).
 
 
 
 
287
 
288
  **Filter Thumbnail Generation Methods**
289
 
@@ -433,6 +438,16 @@ To see a list of features planned for the future as well as to propose your own
433
  ideas for future Document Gallery development, take a look at our
434
  [issue tracker](https://github.com/thenadz/document-gallery/issues).
435
 
 
 
 
 
 
 
 
 
 
 
436
  = 3.2 =
437
  * **Enhancement:** The long awaited option to open thumbnail links in a new window
438
  has been added. Simply use `[dg new_window=true]`.
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: 3.6
6
  Tested up to: 4.2.2
7
+ Stable tag: 3.3
8
  License: GPLv2
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
280
  These tags are as follows:
281
 
282
  * **%link%**: The URL that will be loaded when the user clicks the icon.
283
+ * **%target%**: The target attribute for the anchor tag (e.g.: _blank, _self).
284
  * **%img%**: The URL pointing the the image that will be displayed.
285
  * **%title%**: The human-readable title of the attachment.
286
  * **%title_attribute%**: The escaped title (above), safe for using HTML tag attributes.
287
  * **%description%**: The attachment description (only present when rendering descriptions).
288
+ * **%extension%**: The document file extension.
289
+ * **%size%**: The human-readable file size formatted by
290
+ [size_format](https://codex.wordpress.org/Function_Reference/size_format).
291
+ * **%path%**: The system path pointing to the document.
292
 
293
  **Filter Thumbnail Generation Methods**
294
 
438
  ideas for future Document Gallery development, take a look at our
439
  [issue tracker](https://github.com/thenadz/document-gallery/issues).
440
 
441
+ = 3.3 =
442
+ * **Enhancement:** Developers using the Document Gallery API now have access to new
443
+ values when using the `dg_icon_template` filter.
444
+ (Thanks to [pierowbmstr](https://wordpress.org/support/profile/pierowbmstr)!)
445
+ * **Bug Fix:** Resolved some advanced CSS commands (e.g.: `calc()`) breaking in custom CSS.
446
+ * **Bug Fix:** Some DG options were not being saved correctly resulting in odd behavior
447
+ in some edge cases.
448
+ * **Bug Fix:** Resolved Media Manager integration not being available when first creating
449
+ a post.
450
+
451
  = 3.2 =
452
  * **Enhancement:** The long awaited option to open thumbnail links in a new window
453
  has been added. Simply use `[dg new_window=true]`.
admin/class-admin.php CHANGED
@@ -1,1314 +1,1362 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  class DG_Admin {
5
- /**
6
- * @var string The hook for the Document Gallery settings page.
7
- */
8
- private static $hook;
9
-
10
- /**
11
- * @var string The current tab being rendered.
12
- */
13
- private static $current;
14
-
15
- /**
16
- * NOTE: This should only ever be accessed through getTabs().
17
- *
18
- * @var multitype:string Associative array containing all tab names, keyed by tab slug.
19
- */
20
- private static $tabs;
21
-
22
- /**
23
- * Returns reference to tabs array, initializing if needed.
24
- *
25
- * NOTE: This cannot be done in a static constructor due to timing with i18n.
26
- */
27
- public static function &getTabs() {
28
- if (!isset(self::$tabs)) {
29
- self::$tabs = array(
30
- 'General' => __('General', 'document-gallery'),
31
- 'Thumbnail' => __('Thumbnail Management', 'document-gallery'),
32
- 'Logging' => __('Logging', 'document-gallery'),
33
- 'Advanced' => __('Advanced', 'document-gallery'));
34
- }
35
-
36
- return self::$tabs;
37
- }
38
-
39
- /**
40
- * Renders Document Gallery options page.
41
- */
42
- public static function renderOptions() { ?>
43
- <div class="wrap">
44
- <h2><?php echo __('Document Gallery Settings', 'document-gallery'); ?></h2>
45
-
46
- <h2 class="nav-tab-wrapper">
47
- <?php foreach (self::getTabs() as $tab => $name) {
48
- $class = ($tab == self::$current) ? ' nav-tab-active' : '';
49
- echo '<a class="nav-tab '.$tab.'-tab'.$class.'" href="?page=' . DG_OPTION_NAME . '&tab='.$tab.'">'.$name.'</a>';
50
- } ?>
51
- </h2>
52
-
53
- <form method="post" action="options.php" id="tab-<?php echo self::$current?>">
54
- <input type="hidden" name="<?php echo DG_OPTION_NAME; ?>[tab]" value="<?php echo self::$current; ?>" />
55
- <?php
56
- settings_fields(DG_OPTION_NAME);
57
- do_settings_sections(DG_OPTION_NAME);
58
- if (self::$current != 'Thumbnail' && self::$current != 'Logging') {
59
- submit_button();
60
- }
61
- ?>
62
- </form>
63
-
64
- </div>
65
- <?php }
66
-
67
- /**
68
- * Adds settings link to main plugin view.
69
- */
70
- public static function addSettingsLink($links) {
71
- $settings = '<a href="options-general.php?page=' . DG_OPTION_NAME . '">' .
72
- __('Settings', 'document-gallery') . '</a>';
73
- array_unshift($links, $settings);
74
-
75
- return $links;
76
- }
77
-
78
- /**
79
- * Adds donate link to main plugin view.
80
- */
81
- public static function addDonateLink($links, $file) {
82
- if ($file === DG_BASENAME) {
83
- global $dg_options;
84
-
85
- $donate = '<strong><a href="' . $dg_options['meta']['donate_link'] . '">' .
86
- __('Donate', 'document-gallery') . '</a></strong>';
87
- $links[] = $donate;
88
- }
89
-
90
- return $links;
91
- }
92
-
93
- /**
94
- * Adds Document Gallery settings page to admin navigation.
95
- */
96
- public static function addAdminPage() {
97
- DG_Admin::$hook = add_options_page(
98
- __('Document Gallery Settings', 'document-gallery'),
99
- __('Document Gallery', 'document-gallery'),
100
- 'manage_options', DG_OPTION_NAME, array(__CLASS__, 'renderOptions'));
101
- add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueueScriptsAndStyles'));
102
- }
103
-
104
- /**
105
- * Enqueues styles and scripts for the admin settings page.
106
- */
107
- public static function enqueueScriptsAndStyles($hook) {
108
- if ($hook === DG_Admin::$hook) {
109
- // Settings Page
110
- wp_enqueue_style('document-gallery-admin', DG_URL . 'assets/css/admin.css', null, DG_VERSION);
111
-
112
- // gracefully degrade for older WP versions
113
- if (version_compare(get_bloginfo('version'), '3.8', '<')) {
114
- echo '<style type="text/css">.dashicons, .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before, .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after, #ThumbsTable .title a:after, #LogTable>tbody a:after {display: none !important;}</style>' . PHP_EOL;
115
- }
116
-
117
- wp_enqueue_script('document-gallery-admin', DG_URL . 'assets/js/admin.js', array('jquery'), DG_VERSION, true);
118
- wp_localize_script('document-gallery-admin', 'dg_admin_vars', array('upload_limit' => wp_max_upload_size()));
119
- if ($hook == 'post.php') {
120
- wp_localize_script('document-gallery-admin', 'ajax_object', array('ajax_url' => admin_url('admin-ajax.php')));
121
-
122
- // Media Manager
123
- global $dg_options;
124
- wp_enqueue_script('document-gallery-media-manager', DG_URL . 'assets/js/media_manager.js', array('media-views'), DG_VERSION, true);
125
- wp_localize_script( 'document-gallery-media-manager', 'DGl10n', array(
126
- 'documentGalleryMenuTitle' => __('Create Document Gallery', 'document-gallery'),
127
- 'documentGalleryButton' => __('Create a new Document Gallery', 'document-gallery'),
128
- 'cancelDocumentGalleryTitle' => __('&#8592; Cancel Document Gallery', 'document-gallery'),
129
- 'updateDocumentGallery' => __('Update Document Gallery', 'document-gallery'),
130
- 'insertDocumentGallery' => __('Insert Document Gallery', 'document-gallery'),
131
- 'addToDocumentGallery' => __('Add to Document Gallery', 'document-gallery'),
132
- 'addToDocumentGalleryTitle' => __('Add to Document Gallery', 'document-gallery'),
133
- 'editDocumentGalleryTitle' => __('Edit Document Gallery', 'document-gallery')
134
- ) );
135
- wp_localize_script('document-gallery-media-manager', 'documentGalleryDefaults', $dg_options['gallery']);
136
- }
137
- }
138
- }
139
-
140
- /**
141
- * Load Document Gallery Custom templates.
142
- */
143
- public static function loadCustomTemplates() {
144
- include_once DG_PATH . 'admin/media-manager-template.php';
145
- }
146
-
147
- /**
148
- * Registers settings for the Document Gallery options page.
149
- */
150
- public static function registerSettings() {
151
- if (empty($_REQUEST['tab']) || !array_key_exists($_REQUEST['tab'], self::getTabs())) {
152
- reset(self::getTabs());
153
- self::$current = key(self::getTabs());
154
- } else {
155
- self::$current = $_REQUEST['tab'];
156
- }
157
-
158
- register_setting(DG_OPTION_NAME, DG_OPTION_NAME, array(__CLASS__, 'validateSettings'));
159
-
160
- $funct = 'register' . self::$current . 'Settings';
161
- DG_Admin::$funct();
162
- }
163
-
164
- /**
165
- * Registers settings for the general tab.
166
- */
167
- private static function registerGeneralSettings() {
168
- global $dg_options;
169
-
170
- include_once DG_PATH . 'inc/class-gallery.php';
171
- include_once DG_PATH . 'inc/class-thumber.php';
172
-
173
- $defaults = $dg_options['gallery'];
174
- $active = $dg_options['thumber']['active'];
175
-
176
- add_settings_section(
177
- 'gallery_defaults', __('Default Settings', 'document-gallery'),
178
- array(__CLASS__, 'renderDefaultSettingsSection'), DG_OPTION_NAME);
179
-
180
- add_settings_section(
181
- 'thumbnail_generation', __('Thumbnail Generation', 'document-gallery'),
182
- array(__CLASS__, 'renderThumberSection'), DG_OPTION_NAME);
183
-
184
- add_settings_section(
185
- 'css', __('Custom CSS', 'document-gallery'),
186
- array(__CLASS__, 'renderCssSection'), DG_OPTION_NAME);
187
-
188
- add_settings_field(
189
- 'gallery_defaults_attachment_pg', 'attachment_pg',
190
- array(__CLASS__, 'renderCheckboxField'),
191
- DG_OPTION_NAME, 'gallery_defaults',
192
- array (
193
- 'label_for' => 'label_gallery_defaults_attachment_pg',
194
- 'name' => 'gallery_defaults][attachment_pg',
195
- 'value' => esc_attr($defaults['attachment_pg']),
196
- 'option_name' => DG_OPTION_NAME,
197
- 'description' => __('Link to attachment page rather than to file', 'document-gallery')
198
- ));
199
-
200
- add_settings_field(
201
- 'gallery_defaults_columns', 'columns',
202
- array(__CLASS__, 'renderTextField'),
203
- DG_OPTION_NAME, 'gallery_defaults',
204
- array (
205
- 'label_for' => 'label_gallery_defaults_columns',
206
- 'name' => 'gallery_defaults][columns',
207
- 'value' => esc_attr($defaults['columns']),
208
- 'type' => 'number" min="1" step="1',
209
- 'option_name' => DG_OPTION_NAME,
210
- 'description' => __('The number of columns to display when not rendering descriptions.', 'document-gallery')
211
- ));
212
-
213
- add_settings_field(
214
- 'gallery_defaults_descriptions', 'descriptions',
215
- array(__CLASS__, 'renderCheckboxField'),
216
- DG_OPTION_NAME, 'gallery_defaults',
217
- array (
218
- 'label_for' => 'label_gallery_defaults_descriptions',
219
- 'name' => 'gallery_defaults][descriptions',
220
- 'value' => esc_attr($defaults['descriptions']),
221
- 'option_name' => DG_OPTION_NAME,
222
- 'description' => __('Include document descriptions', 'document-gallery')
223
- ));
224
-
225
- add_settings_field(
226
- 'gallery_defaults_fancy', 'fancy',
227
- array(__CLASS__, 'renderCheckboxField'),
228
- DG_OPTION_NAME, 'gallery_defaults',
229
- array (
230
- 'label_for' => 'label_gallery_defaults_fancy',
231
- 'name' => 'gallery_defaults][fancy',
232
- 'value' => esc_attr($defaults['fancy']),
233
- 'option_name' => DG_OPTION_NAME,
234
- 'description' => __('Use auto-generated document thumbnails', 'document-gallery')
235
- ));
236
-
237
- add_settings_field(
238
- 'gallery_defaults_order', 'order',
239
- array(__CLASS__, 'renderSelectField'),
240
- DG_OPTION_NAME, 'gallery_defaults',
241
- array (
242
- 'label_for' => 'label_gallery_defaults_order',
243
- 'name' => 'gallery_defaults][order',
244
- 'value' => esc_attr($defaults['order']),
245
- 'options' => DG_Gallery::getOrderOptions(),
246
- 'option_name' => DG_OPTION_NAME,
247
- 'description' => __('Ascending or descending sorting of documents', 'document-gallery')
248
- ));
249
-
250
- add_settings_field(
251
- 'gallery_defaults_orderby', 'orderby',
252
- array(__CLASS__, 'renderSelectField'),
253
- DG_OPTION_NAME, 'gallery_defaults',
254
- array (
255
- 'label_for' => 'label_gallery_defaults_orderby',
256
- 'name' => 'gallery_defaults][orderby',
257
- 'value' => esc_attr($defaults['orderby']),
258
- 'options' => DG_Gallery::getOrderbyOptions(),
259
- 'option_name' => DG_OPTION_NAME,
260
- 'description' => __('Which field to order documents by', 'document-gallery')
261
- ));
262
-
263
- add_settings_field(
264
- 'gallery_defaults_relation', 'relation',
265
- array(__CLASS__, 'renderSelectField'),
266
- DG_OPTION_NAME, 'gallery_defaults',
267
- array (
268
- 'label_for' => 'label_gallery_defaults_relation',
269
- 'name' => 'gallery_defaults][relation',
270
- 'value' => esc_attr($defaults['relation']),
271
- 'options' => DG_Gallery::getRelationOptions(),
272
- 'option_name' => DG_OPTION_NAME,
273
- 'description' => __('Whether matched documents must have all taxa_names (AND) or at least one (OR)', 'document-gallery')
274
- ));
275
-
276
- add_settings_field(
277
- 'gallery_defaults_limit', 'limit',
278
- array(__CLASS__, 'renderTextField'),
279
- DG_OPTION_NAME, 'gallery_defaults',
280
- array (
281
- 'label_for' => 'label_gallery_defaults_limit',
282
- 'name' => 'gallery_defaults][limit',
283
- 'value' => esc_attr($defaults['limit']),
284
- 'type' => 'number" min="-1" step="1',
285
- 'option_name' => DG_OPTION_NAME,
286
- 'description' => __('Limit the number of documents included. -1 means no limit.', 'document-gallery')
287
- ));
288
-
289
- add_settings_field(
290
- 'gallery_defaults_mime_types', 'mime_types',
291
- array(__CLASS__, 'renderTextField'),
292
- DG_OPTION_NAME, 'gallery_defaults',
293
- array (
294
- 'label_for' => 'label_gallery_defaults_mime_types',
295
- 'name' => 'gallery_defaults][mime_types',
296
- 'value' => esc_attr($defaults['mime_types']),
297
- 'type' => 'text',
298
- 'option_name' => DG_OPTION_NAME,
299
- 'description' => __('Comma-delimited list of <a href="http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types">MIME types</a>.', 'document-gallery')
300
- ));
301
-
302
- add_settings_field(
303
- 'gallery_defaults_new_window', 'new_window',
304
- array(__CLASS__, 'renderCheckboxField'),
305
- DG_OPTION_NAME, 'gallery_defaults',
306
- array (
307
- 'label_for' => 'label_gallery_defaults_new_window',
308
- 'name' => 'gallery_defaults][new_window',
309
- 'value' => esc_attr($defaults['new_window']),
310
- 'option_name' => DG_OPTION_NAME,
311
- 'description' => __('Open thumbnail links in new window.', 'document-gallery')
312
- ));
313
-
314
- add_settings_field(
315
- 'gallery_defaults_post_status', 'post_status',
316
- array(__CLASS__, 'renderSelectField'),
317
- DG_OPTION_NAME, 'gallery_defaults',
318
- array (
319
- 'label_for' => 'label_gallery_defaults_post_status',
320
- 'name' => 'gallery_defaults][post_status',
321
- 'value' => esc_attr($defaults['post_status']),
322
- 'options' => DG_Gallery::getPostStatuses(),
323
- 'option_name' => DG_OPTION_NAME,
324
- 'description' => __('Which post status to look for when querying documents.', 'document-gallery')
325
- ));
326
-
327
- add_settings_field(
328
- 'gallery_defaults_post_type', 'post_type',
329
- array(__CLASS__, 'renderSelectField'),
330
- DG_OPTION_NAME, 'gallery_defaults',
331
- array (
332
- 'label_for' => 'label_gallery_defaults_post_type',
333
- 'name' => 'gallery_defaults][post_type',
334
- 'value' => esc_attr($defaults['post_type']),
335
- 'options' => DG_Gallery::getPostTypes(),
336
- 'option_name' => DG_OPTION_NAME,
337
- 'description' => __('Which post type to look for when querying documents.', 'document-gallery')
338
- ));
339
-
340
- add_settings_field(
341
- 'thumbnail_generation_av', __('Audio/Video', 'document-gallery'),
342
- array(__CLASS__, 'renderCheckboxField'),
343
- DG_OPTION_NAME, 'thumbnail_generation',
344
- array (
345
- 'label_for' => 'label_thumbnail_generation_av',
346
- 'name' => 'thumbnail_generation][av',
347
- 'value' => esc_attr($active['av']),
348
- 'option_name' => DG_OPTION_NAME,
349
- 'description' => esc_html__('Locally generate thumbnails for audio & video files.', 'document-gallery')
350
- ));
351
-
352
- add_settings_field(
353
- 'thumbnail_generation_gs', 'Ghostscript',
354
- array(__CLASS__, 'renderCheckboxField'),
355
- DG_OPTION_NAME, 'thumbnail_generation',
356
- array (
357
- 'label_for' => 'label_thumbnail_generation_gs',
358
- 'name' => 'thumbnail_generation][gs',
359
- 'value' => esc_attr($active['gs']),
360
- 'option_name' => DG_OPTION_NAME,
361
- 'description' => DG_Thumber::isGhostscriptAvailable()
362
- ? __('Use <a href="http://www.ghostscript.com/" target="_blank">Ghostscript</a> for faster local PDF processing (compared to Imagick).', 'document-gallery')
363
- : __('Your server is not configured to run <a href="http://www.ghostscript.com/" target="_blank">Ghostscript</a>.', 'document-gallery'),
364
- 'disabled' => !DG_Thumber::isGhostscriptAvailable()
365
- ));
366
-
367
- add_settings_field(
368
- 'thumbnail_generation_imagick', 'Imagick',
369
- array(__CLASS__, 'renderCheckboxField'),
370
- DG_OPTION_NAME, 'thumbnail_generation',
371
- array (
372
- 'label_for' => 'label_thumbnail_generation_imagick',
373
- 'name' => 'thumbnail_generation][imagick',
374
- 'value' => esc_attr($active['imagick']),
375
- 'option_name' => DG_OPTION_NAME,
376
- 'description' => DG_Thumber::isImagickAvailable()
377
- ? __('Use <a href="http://www.php.net/manual/en/book.imagick.php" target="_blank">Imagick</a> to handle lots of filetypes locally.', 'document-gallery')
378
- : __('Your server is not configured to run <a href="http://www.php.net/manual/en/book.imagick.php" target="_blank">Imagick</a>.', 'document-gallery'),
379
- 'disabled' => !DG_Thumber::isImagickAvailable()
380
- ));
381
-
382
- add_settings_field(
383
- 'thumbnail_generation_width', __('Max Thumbnail Dimensions', 'document-gallery'),
384
- array(__CLASS__, 'renderMultiTextField'),
385
- DG_OPTION_NAME, 'thumbnail_generation',
386
- array (
387
- array (
388
- 'label_for' => 'label_advanced_width',
389
- 'name' => 'thumbnail_generation][width',
390
- 'value' => esc_attr($dg_options['thumber']['width']),
391
- 'type' => 'number" min="1" step="1',
392
- 'option_name' => DG_OPTION_NAME,
393
- 'description' => ' x '),
394
- array (
395
- 'label_for' => 'label_advanced_height',
396
- 'name' => 'thumbnail_generation][height',
397
- 'value' => esc_attr($dg_options['thumber']['height']),
398
- 'type' => 'number" min="1" step="1',
399
- 'option_name' => DG_OPTION_NAME,
400
- 'description' => __('The max width and height (in pixels) that thumbnails will be generated.', 'document-gallery'))
401
- ));
402
- }
403
-
404
- /**
405
- * Registers settings for the thumbnail management tab.
406
- */
407
- private static function registerThumbnailSettings() {
408
- add_settings_section(
409
- 'thumbnail_table', '',
410
- array(__CLASS__, 'renderThumbnailSection'), DG_OPTION_NAME);
411
- }
412
-
413
- /**
414
- * Registers settings for the logging tab.
415
- */
416
- private static function registerLoggingSettings() {
417
- add_settings_section(
418
- 'logging_table', '',
419
- array(__CLASS__, 'renderLoggingSection'), DG_OPTION_NAME);
420
- }
421
-
422
- /**
423
- * Registers settings for the advanced tab.
424
- */
425
- private static function registerAdvancedSettings() {
426
- global $dg_options;
427
-
428
- add_settings_section(
429
- 'advanced', __('Advanced Thumbnail Generation', 'document-gallery'),
430
- array(__CLASS__, 'renderAdvancedSection'), DG_OPTION_NAME);
431
-
432
- add_settings_field(
433
- 'advanced_logging_enabled', __('Logging Enabled', 'document-gallery'),
434
- array(__CLASS__, 'renderCheckboxField'),
435
- DG_OPTION_NAME, 'advanced',
436
- array (
437
- 'label_for' => 'label_advanced_logging_enabled',
438
- 'name' => 'logging_enabled',
439
- 'value' => esc_attr($dg_options['logging']['enabled']),
440
- 'option_name' => DG_OPTION_NAME,
441
- 'description' => __('Whether to log debug and error information related to Document Gallery.', 'document-gallery')
442
- ));
443
-
444
- add_settings_field(
445
- 'advanced_logging_purge_interval', __('Logging Purge Interval', 'document-gallery'),
446
- array(__CLASS__, 'renderTextField'),
447
- DG_OPTION_NAME, 'advanced',
448
- array (
449
- 'label_for' => 'label_advanced_logging_purge_interval',
450
- 'name' => 'logging_purge_interval',
451
- 'value' => esc_attr($dg_options['logging']['purge_interval']),
452
- 'type' => 'number" min="0" step="1',
453
- 'option_name' => DG_OPTION_NAME,
454
- 'description' => __('Number of days to keep old log entries (0 disables purging).', 'document-gallery')
455
- ));
456
-
457
- add_settings_field(
458
- 'advanced_validation', __('Option Validation', 'document-gallery'),
459
- array(__CLASS__, 'renderCheckboxField'),
460
- DG_OPTION_NAME, 'advanced',
461
- array (
462
- 'label_for' => 'label_advanced_validation',
463
- 'name' => 'validation',
464
- 'value' => esc_attr($dg_options['validation']),
465
- 'option_name' => DG_OPTION_NAME,
466
- 'description' => __('Whether option structure should be validated before save. This is not generally necessary.', 'document-gallery')
467
- ));
468
-
469
- add_settings_field(
470
- 'advanced_thumb_timeout', __('Thumbnail Generation Timeout', 'document-gallery'),
471
- array(__CLASS__, 'renderTextField'),
472
- DG_OPTION_NAME, 'advanced',
473
- array (
474
- 'label_for' => 'label_advanced_thumb_timeout',
475
- 'name' => 'timeout',
476
- 'value' => esc_attr($dg_options['thumber']['timeout']),
477
- 'type' => 'number" min="1" step="1',
478
- 'option_name' => DG_OPTION_NAME,
479
- 'description' => __('Max number of seconds to wait for thumbnail generation before defaulting to filetype icons.', 'document-gallery') .
480
- ' <em>' . __('Note that generation will continue where timeout happened next time the gallery is loaded.', 'document-gallery') . '</em>'));
481
-
482
- add_settings_field(
483
- 'advanced_gs', __('Ghostscript Absolute Path', 'document-gallery'),
484
- array(__CLASS__, 'renderTextField'),
485
- DG_OPTION_NAME, 'advanced',
486
- array (
487
- 'label_for' => 'label_advanced_gs',
488
- 'name' => 'gs',
489
- 'value' => esc_attr($dg_options['thumber']['gs']),
490
- 'option_name' => DG_OPTION_NAME,
491
- 'description' => $dg_options['thumber']['gs']
492
- ? __('Successfully auto-detected the location of Ghostscript.', 'document-gallery')
493
- : __('Failed to auto-detect the location of Ghostscript.', 'document-gallery')
494
- ));
495
-
496
- add_settings_section(
497
- 'advanced_options_dump', __('Options Array Dump', 'document-gallery'),
498
- array(__CLASS__, 'renderOptionsDumpSection'), DG_OPTION_NAME);
499
- }
500
-
501
- /**
502
- * Validates submitted options, sanitizing any invalid options.
503
- * @param array $values User-submitted new options.
504
- * @return array Sanitized new options.
505
- */
506
- public static function validateSettings($values) {
507
- // NOTE: WP double-calls this function -- below logic prevents potential
508
- // side effects by processing a maximum of one call to validate
509
- // per page load, re-returning the previous result on any
510
- // subsequent calls.
511
- static $ret = null;
512
- if (is_null($ret)) {
513
- if (empty($values['tab']) || !array_key_exists($values['tab'], self::getTabs())) {
514
- reset(self::getTabs());
515
- $values['tab'] = key(self::getTabs());
516
- }
517
- $funct = 'validate'.$values['tab'].'Settings';
518
- unset($values['tab']);
519
- $ret = DG_Admin::$funct($values);
520
- }
521
-
522
- return $ret;
523
- }
524
-
525
- /**
526
- * Validates general settings, sanitizing any invalid options.
527
- * @param array $values User-submitted new options.
528
- * @return array Sanitized new options.
529
- */
530
- private static function validateGeneralSettings($values) {
531
- global $dg_options;
532
- $ret = $dg_options;
533
-
534
- include_once DG_PATH . 'inc/class-gallery.php';
535
-
536
- $thumbs_cleared = false;
537
-
538
- // handle gallery shortcode defaults
539
- $errs = array();
540
- $ret['gallery'] = DG_Gallery::sanitizeDefaults(null, $values['gallery_defaults'], $errs);
541
-
542
- foreach ($errs as $k => $v) {
543
- add_settings_error(DG_OPTION_NAME, str_replace('_', '-', $k), $v);
544
- }
545
-
546
- // handle setting width
547
- if (isset($values['thumbnail_generation']['width'])) {
548
- $width = (int)$values['thumbnail_generation']['width'];
549
- if ($width > 0) {
550
- $ret['thumber']['width'] = $width;
551
- } else {
552
- add_settings_error(DG_OPTION_NAME, 'thumber-width',
553
- __('Invalid width given: ', 'document-gallery') . $values['thumbnail_generation']['width']);
554
- }
555
-
556
- unset($values['thumbnail_generation']['width']);
557
- }
558
-
559
- // handle setting height
560
- if (isset($values['thumbnail_generation']['height'])) {
561
- $height = (int)$values['thumbnail_generation']['height'];
562
- if ($height > 0) {
563
- $ret['thumber']['height'] = $height;
564
- } else {
565
- add_settings_error(DG_OPTION_NAME, 'thumber-height',
566
- __('Invalid height given: ', 'document-gallery') . $values['thumbnail_generation']['height']);
567
- }
568
-
569
- unset($values['thumbnail_generation']['width']);
570
- }
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
- foreach ($ret['thumber']['thumbs'] as $v) {
576
- if (isset($v['thumber'])) {
577
- @unlink($v['thumb_path']);
578
- }
579
- }
580
-
581
- $ret['thumber']['thumbs'] = array();
582
- $thumbs_cleared = true;
583
- }
584
-
585
- // handle setting the active thumbers
586
- foreach (array_keys($ret['thumber']['active']) as $k) {
587
- $ret['thumber']['active'][$k] = isset($values['thumbnail_generation'][$k]);
588
- }
589
-
590
- // if new thumbers available, clear failed thumbnails for retry
591
- if (!$thumbs_cleared) {
592
- foreach ($dg_options['thumber']['active'] as $k => $v) {
593
- if (!$v && $ret['thumber']['active'][$k]) {
594
- foreach ($dg_options['thumber']['thumbs'] as $k => $v) {
595
- if (empty($v['thumber'])) {
596
- unset($ret['thumber']['thumbs'][$k]);
597
- }
598
- }
599
- break;
600
- }
601
- }
602
- }
603
-
604
- // handle modified CSS
605
- if (trim($ret['css']['text']) !== trim($values['css'])) {
606
- $ret['css']['text'] = trim($values['css']);
607
- $ret['css']['minified'] = DocumentGallery::compileCustomCss($ret['css']['text']);
608
- }
609
-
610
- return $ret;
611
- }
612
-
613
- /**
614
- * Validates thumbnail management settings, sanitizing any invalid options.
615
- * @param array $values User-submitted new options.
616
- * @return array Sanitized new options.
617
- */
618
- private static function validateThumbnailSettings($values) {
619
- global $dg_options;
620
- $ret = $dg_options;
621
- $responseArr = array('result' => false);
622
-
623
- if (isset($values['entry'])) {
624
- $ID = intval($values['entry']);
625
- } else {
626
- $ID = -1;
627
- }
628
-
629
- // Thumbnail(s) cleanup;
630
- // cleanup value is a marker
631
- if ( isset($values['cleanup']) && isset($values['ids']) ) {
632
- $deleted = array_values(array_intersect(array_keys($dg_options['thumber']['thumbs']), $values['ids']));
633
-
634
- foreach ($deleted as $k) {
635
- if (isset($ret['thumber']['thumbs'][$k]['thumber'])) {
636
- @unlink($ret['thumber']['thumbs'][$k]['thumb_path']);
637
- }
638
-
639
- unset($ret['thumber']['thumbs'][$k]);
640
- }
641
-
642
- $responseArr['result'] = true;
643
- $responseArr['deleted'] = $deleted;
644
- }
645
-
646
- // Attachment title update
647
- // title value is a marker
648
- elseif ( isset($values['title']) && $ID != -1 ) {
649
- $attachment = array(
650
- 'ID' => $ID,
651
- 'post_title' => rawurldecode(addslashes($values['title']))
652
- );
653
- if ( wp_update_post( $attachment ) ) {
654
- $responseArr['result'] = true;
655
- }
656
- }
657
-
658
- // Attachment description update
659
- // description value is a marker
660
- elseif ( isset($values['description']) && $ID != -1 ) {
661
- $attachment = array(
662
- 'ID' => $ID,
663
- 'post_content' => rawurldecode(addslashes($values['description']))
664
- );
665
- if ( wp_update_post( $attachment ) ) {
666
- $responseArr['result'] = true;
667
- }
668
- }
669
-
670
- // Thumbnail file manual refresh (one at a time)
671
- // upload value is a marker
672
- elseif ( isset($values['upload']) && isset($_FILES['file']) && isset($ret['thumber']['thumbs'][$ID]) ) {
673
- $old_path = $ret['thumber']['thumbs'][$ID]['thumb_path'];
674
- $uploaded_filename = self::validateUploadedFile();
675
- if ($uploaded_filename && DG_Thumber::setThumbnail($ID, $uploaded_filename)) {
676
- if ($dg_options['thumber']['thumbs'][$ID]['thumb_path'] !== $old_path) {
677
- @unlink($old_path);
678
- }
679
- $responseArr['result'] = true;
680
- $responseArr['url'] = $dg_options['thumber']['thumbs'][$ID]['thumb_url'];
681
- $ret['thumber']['thumbs'][$ID] = $dg_options['thumber']['thumbs'][$ID];
682
- }
683
- }
684
-
685
- if (isset($values['ajax'])) {
686
- echo DG_Util::jsonEncode($responseArr);
687
- add_filter('wp_redirect', array(__CLASS__, '_exit'), 1, 0);
688
- }
689
-
690
- return $ret;
691
- }
692
-
693
- /**
694
- * Validates uploaded file as a semi for potential thumbnail.
695
- * @param str $var File field name.
696
- * @return bool|str False on failure, path to temp file on success.
697
- */
698
- public static function validateUploadedFile($var = 'file') {
699
- // checking if any file was delivered
700
- if (!isset($_FILES[$var]))
701
- return false;
702
- // we gonna process only first one
703
- if ( !is_array($_FILES[$var]['error']) ) {
704
- $upload_err = $_FILES[$var]['error'];
705
- $upload_path = $_FILES[$var]['tmp_name'];
706
- $upload_size = $_FILES[$var]['size'];
707
- $upload_type = $_FILES[$var]['type'];
708
- $upload_name = $_FILES[$var]['name'];
709
- } else {
710
- $upload_err = $_FILES[$var]['error'][0];
711
- $upload_path = $_FILES[$var]['tmp_name'][0];
712
- $upload_size = $_FILES[$var]['size'][0];
713
- $upload_type = $_FILES[$var]['type'][0];
714
- $upload_name = $_FILES[$var]['name'][0];
715
- }
716
- $info = getimagesize($upload_path);
717
- if ($info) {
718
- if ($info['mime']!=$upload_type) {// in DG_Thumber::getExt() we'll define and set appropriate extension
719
- DG_Logger::writeLog(
720
- DG_LogLevel::Warning,
721
- __('File extension doesn\'t match the MIME type of the image: ', 'document-gallery') .
722
- $upload_name.' - '.$info['mime']);
723
- }
724
- if ($upload_size>wp_max_upload_size()) {
725
- DG_Logger::writeLog(
726
- DG_LogLevel::Warning,
727
- __('Uploaded file size exceeds the allowable limit: ', 'document-gallery') .
728
- $upload_name.' - '.$upload_size.'b');
729
- return false;
730
- }
731
- } else {
732
- DG_Logger::writeLog(
733
- DG_LogLevel::Warning,
734
- __('Uploaded file is not an image: ', 'document-gallery') .
735
- $upload_name);
736
- return false;
737
- }
738
- if ($upload_err == UPLOAD_ERR_OK && $upload_size > 0) {
739
- $temp_file = $upload_path;
740
- } else {
741
- DG_Logger::writeLog(
742
- DG_LogLevel::Error,
743
- __('Failed to get uploaded file: ', 'document-gallery') .
744
- $upload_err);
745
- return false;
746
- }
747
-
748
- return $temp_file;
749
- }
750
-
751
- /**
752
- * Validates logging settings, sanitizing any invalid options.
753
- * @param array $values User-submitted new options.
754
- * @return array Sanitized new options.
755
- */
756
- private static function validateLoggingSettings($values) {
757
- global $dg_options;
758
- if (isset($values['clearLog'])) {
759
- DG_Logger::clearLog();
760
- }
761
- return $dg_options;
762
- }
763
-
764
- /**
765
- * Validates advanced settings, sanitizing any invalid options.
766
- * @param array $values User-submitted new options.
767
- * @return array Sanitized new options.
768
- */
769
- private static function validateAdvancedSettings($values) {
770
- global $dg_options;
771
- $ret = $dg_options;
772
-
773
- // handle setting the Ghostscript path
774
- if (isset($values['gs']) &&
775
- 0 != strcmp($values['gs'], $ret['thumber']['gs'])) {
776
- if (false === strpos($values['gs'], ';')) {
777
- $ret['thumber']['gs'] = $values['gs'];
778
- } else {
779
- add_settings_error(DG_OPTION_NAME, 'thumber-gs',
780
- __('Invalid Ghostscript path given: ', 'document-gallery') . $values['gs']);
781
- }
782
- }
783
-
784
- // handle setting timeout
785
- if (isset($values['timeout'])) {
786
- $timeout = (int)$values['timeout'];
787
- if ($timeout > 0) {
788
- $ret['thumber']['timeout'] = $timeout;
789
- } else {
790
- add_settings_error(DG_OPTION_NAME, 'thumber-timeout',
791
- __('Invalid timeout given: ', 'document-gallery') . $values['timeout']);
792
- }
793
- }
794
-
795
- // validation checkbox
796
- $ret['validation'] = isset($values['validation']);
797
-
798
- // logging settings
799
- $ret['logging']['enabled'] = isset($values['logging_enabled']);
800
- if (isset($values['logging_purge_interval'])) {
801
- $purge_interval = (int)$values['logging_purge_interval'];
802
- if ($purge_interval >= 0) {
803
- $ret['logging']['purge_interval'] = $purge_interval;
804
- } else {
805
- add_settings_error(DG_OPTION_NAME, 'thumber-logging-purge-interval',
806
- __('Invalid logging purge interval given: ', 'document-gallery') . $values['logging_purge_interval']);
807
- }
808
- }
809
-
810
- return $ret;
811
- }
812
-
813
- /**
814
- * @return bool Whether to register settings.
815
- */
816
- public static function doRegisterSettings() {
817
- if (!is_multisite()) {
818
- $script = !empty($GLOBALS['pagenow']) ? $GLOBALS['pagenow'] : null;
819
- } else {
820
- $script = parse_url($_SERVER['REQUEST_URI']);
821
- $script = basename($script['path']);
822
- }
823
-
824
- return !empty($script) && ('options-general.php' === $script || 'options.php' === $script);
825
- }
826
-
827
- /**
828
- * Render the Default Settings section.
829
- */
830
- public static function renderDefaultSettingsSection() { ?>
831
- <p><?php _e('The following values will be used by default in the shortcode. You can still manually set each of these values in each individual shortcode.', 'document-gallery'); ?></p>
832
- <?php }
833
-
834
- /**
835
- * Render the Thumber section.
836
- */
837
- public static function renderThumberSection() { ?>
838
- <p><?php _e('Select which tools to use when generating thumbnails.', 'document-gallery'); ?></p>
839
- <?php }
840
-
841
- /**
842
- * Renders a text field for use when modifying the CSS to be printed in addition to the default CSS.
843
- */
844
- public static function renderCssSection() {
845
- global $dg_options; ?>
846
- <p><?php printf(
847
- __('Enter custom CSS styling for use with document galleries. To see which ids and classes you can style, take a look at <a href="%s" target="_blank">style.css</a>.'),
848
- DG_URL . 'assets/css/style.css'); ?></p>
849
- <table class="form-table">
850
- <tbody>
851
- <tr valign="top">
852
- <td>
853
- <textarea name="<?php echo DG_OPTION_NAME; ?>[css]" rows="10" cols="50" class="large-text code"><?php echo $dg_options['css']['text']; ?></textarea>
854
- </td>
855
- </tr>
856
- </tbody>
857
- </table>
858
- <?php }
859
-
860
- /**
861
- * Render the Thumber Advanced section.
862
- */
863
- public static function renderAdvancedSection() {
864
- include_once DG_PATH . 'inc/class-thumber.php';?>
865
- <p><?php _e('Unless you <em>really</em> know what you\'re doing, you should not touch these values.', 'document-gallery'); ?></p>
866
- <?php if (!DG_Thumber::isExecAvailable()) : ?>
867
- <p>
868
- <em><?php _e('NOTE: <code>exec()</code> is not accessible. Ghostscript will not function.', 'document-gallery'); ?></em>
869
- </p>
870
- <?php endif; ?>
871
- <?php }
872
-
873
- /**
874
- * Renders a readonly textfield containing a dump of current DG options.
875
- */
876
- public static function renderOptionsDumpSection() {
877
- global $dg_options; ?>
878
- <p><?php
879
- _e('The following <em>readonly text</em> should be provided when <a href="http://wordpress.org/support/plugin/document-gallery" target="_blank">reporting a bug</a>:', 'documet-gallery');
880
- ?></p>
881
- <table class="form-table">
882
- <tbody>
883
- <tr valign="top">
884
- <td>
885
- <textarea readonly="true" rows="10" cols="50" id="options-dump" class="large-text code"><?php print_r($dg_options); ?></textarea>
886
- </td>
887
- </tr>
888
- </tbody>
889
- </table>
890
- <?php }
891
-
892
- /**
893
- * Render the Thumbnail table.
894
- */
895
- public static function renderThumbnailSection() {
896
- include_once DG_PATH . 'inc/class-thumber.php';
897
- $options = DG_Thumber::getOptions();
898
-
899
- $URL_params = array('page' => DG_OPTION_NAME, 'tab' => 'Thumbnail');
900
- $att_ids = array();
901
-
902
- if (isset($_REQUEST['orderby']) && in_array(strtolower($_REQUEST['orderby']), array('title', 'date'))) {
903
- $orderby = strtolower($_REQUEST['orderby']);
904
- $URL_params['orderby'] = $orderby;
905
-
906
- switch ($orderby) {
907
- case 'date':
908
- foreach ($options['thumbs'] as $key => $node) {
909
- $keyArray[$key] = $node['timestamp'];
910
- $options['thumbs'][$key]['thumb_id'] = $att_ids[] = $key;
911
- }
912
- break;
913
-
914
- case 'title':
915
- foreach ($options['thumbs'] as $key => $node) {
916
- $keyArray[$key] = basename($node['thumb_path']);
917
- $options['thumbs'][$key]['thumb_id'] = $att_ids[] = $key;
918
- }
919
- break;
920
- }
921
-
922
- $order = strtolower($_REQUEST['order']);
923
- if (!isset($_REQUEST['order']) || !in_array($order, array('asc', 'desc'))) {
924
- $order = 'asc';
925
- }
926
- $URL_params['order'] = $order;
927
-
928
- if ($order == 'asc') {
929
- array_multisort($keyArray, SORT_ASC, $options['thumbs']);
930
- } else {
931
- array_multisort($keyArray, SORT_DESC, $options['thumbs']);
932
- }
933
- } else {
934
- $orderby = '';
935
- foreach ($options['thumbs'] as $key => $node) {
936
- $options['thumbs'][$key]['thumb_id'] = $att_ids[] = $key;
937
- }
938
- }
939
-
940
- static $limit_options = array(10, 25, 75);
941
- if (!isset($_REQUEST['limit']) || !in_array(intval($_REQUEST['limit']), $limit_options)) {
942
- $limit = $limit_options[0];
943
- } else {
944
- $limit = intval($_REQUEST['limit']);
945
- }
946
-
947
- $URL_params['limit'] = $limit;
948
- $select_limit = '';
949
- foreach ($limit_options as $l_o) {
950
- $select_limit .= '<option value="'.$l_o.'"'.selected($limit, $l_o, false).'>'.$l_o.'</option>'.PHP_EOL;
951
- }
952
- $thumbs_number = count($options['thumbs']);
953
- $lastsheet = ceil($thumbs_number/$limit);
954
- $sheet = isset($_REQUEST['sheet']) ? intval($_REQUEST['sheet']) : 1;
955
- if ($sheet <= 0 || $sheet > $lastsheet) {
956
- $sheet = 1;
957
- }
958
-
959
- $offset = ($sheet - 1) * $limit;
960
-
961
- $att_ids = array_slice($att_ids, $offset, $limit);
962
-
963
- // https://core.trac.wordpress.org/ticket/12212
964
- $atts = array();
965
- if (!empty($att_ids)) {
966
- $atts = get_posts(
967
- array(
968
- 'post_type' => 'any',
969
- 'post_status' => 'any',
970
- 'numberposts' => -1,
971
- 'post__in' => $att_ids,
972
- 'orderby' => 'post__in'
973
- ));
974
- }
975
-
976
- $titles = array();
977
- $contents = array();
978
- foreach ($atts as $att) {
979
- $path_parts = pathinfo($att->guid);
980
- $titles[$att->ID] = $att->post_title;
981
- $types[$att->ID] = $path_parts['extension'];
982
- $contents[$att->ID] = $att->post_content;
983
- }
984
- unset($atts);
985
-
986
- $thead = '<tr>'.
987
- '<th scope="col" class="manage-column column-cb check-column">'.
988
- '<label class="screen-reader-text" for="cb-select-all-%1$d">'.__('Select All', 'document-gallery').'</label>'.
989
- '<input id="cb-select-all-%1$d" type="checkbox">'.
990
- '</th>'.
991
- '<th scope="col" class="manage-column column-icon">'.__('Thumbnail', 'document-gallery').'</th>'.
992
- '<th scope="col" class="manage-column column-title '.(($orderby != 'title')?'sortable desc':'sorted '.$order).'"><a href="?'.http_build_query(array_merge($URL_params, array('orderby'=>'title','order'=>(($orderby != 'title')?'asc':(($order == 'asc')?'desc':'asc'))))).'"><span>'.__('File name', 'document-gallery').'</span><span class="sorting-indicator"></span></th>'.
993
- '<th scope="col" class="manage-column column-description">'.__('Description', 'document-gallery').'</th>'.
994
- '<th scope="col" class="manage-column column-thumbupload"></th>'.
995
- '<th scope="col" class="manage-column column-date '.(($orderby != 'date')?'sortable asc':'sorted '.$order).'"><a href="?'.http_build_query(array_merge($URL_params, array('orderby'=>'date','order'=>(($orderby != 'date')?'desc':(($order == 'asc')?'desc':'asc'))))).'"><span>'.__('Date', 'document-gallery').'</span><span class="sorting-indicator"></span></th>'.
996
- '</tr>';
997
-
998
- $pagination = '<div class="alignleft bulkactions"><button class="button action deleteSelected">'.__('Delete Selected', 'document-gallery').'</button></div><div class="tablenav-pages">'.
999
- '<span class="displaying-num">'.
1000
- $thumbs_number.' '._n('item', 'items', $thumbs_number).
1001
- '</span>'.($lastsheet>1?
1002
- '<span class="pagination-links">'.
1003
- '<a class="first-page'.( $sheet==1 ? ' disabled' : '').'" title="'.__('Go to the first page', 'document-gallery').'"'.( $sheet==1 ? '' : ' href="?'.http_build_query($URL_params).'"').'>«</a>'.
1004
- '<a class="prev-page'.( $sheet==1 ? ' disabled' : '').'" title="'.__('Go to the previous page', 'document-gallery').'"'.( $sheet==1 ? '' : ' href="?'.http_build_query(array_merge($URL_params, array('sheet'=>$sheet-1))).'"').'>‹</a>'.
1005
- '<span class="paging-input">'.
1006
- '<input class="current-page" title="'.__('Current page', 'document-gallery').'" type="text" name="paged" value="'.$sheet.'" size="'.strlen($sheet).'" maxlength="'.strlen($sheet).'"> '.__('of', 'document-gallery').' <span class="total-pages">'.$lastsheet.'</span></span>'.
1007
- '<a class="next-page'.( $sheet==$lastsheet ? ' disabled' : '').'" title="'.__('Go to the next page', 'document-gallery').'"'.( $sheet==$lastsheet ? '' : ' href="?'.http_build_query(array_merge($URL_params, array('sheet'=>$sheet+1))).'"').'>›</a>'.
1008
- '<a class="last-page'.( $sheet==$lastsheet ? ' disabled' : '').'" title="'.__('Go to the last page', 'document-gallery').'"'.( $sheet==$lastsheet ? '' : ' href="?'.http_build_query(array_merge($URL_params, array('sheet'=>$lastsheet))).'"').'>»</a>'.
1009
- '</span>':' <b>|</b> ').
1010
- '<span class="displaying-num"><select dir="rtl" class="limit_per_page">'.$select_limit.'</select> '.__('items per page', 'document-gallery').'</span>'.
1011
- '</div>'.
1012
- '<br class="clear" />';
1013
- ?>
1014
-
1015
- <script type="text/javascript">
1016
- var URL_params = <?php echo DG_Util::jsonEncode($URL_params); ?>;
1017
- </script>
1018
- <div class="thumbs-list-wrapper">
1019
- <div>
1020
- <div class="tablenav top"><?php echo $pagination; ?></div>
1021
- <table id="ThumbsTable" class="wp-list-table widefat fixed media"
1022
- cellpadding="0" cellspacing="0">
1023
- <thead>
1024
- <?php printf($thead, 1); ?>
1025
- </thead>
1026
- <tfoot>
1027
- <?php printf($thead, 2); ?>
1028
- </tfoot>
1029
- <tbody><?php
1030
- $i = 0;
1031
- foreach ($options['thumbs'] as $v) {
1032
- if ($i < $offset) { $i++; continue; }
1033
- if (++$i > $offset + $limit) { break; }
1034
-
1035
- $icon = isset($v['thumb_url']) ? $v['thumb_url'] : DG_Thumber::getDefaultThumbnail($v['thumb_id']);
1036
- $title = isset($titles[$v['thumb_id']]) ? $titles[$v['thumb_id']] : '';
1037
- $type = $types[$v['thumb_id']];
1038
- $description = $contents[$v['thumb_id']];
1039
- $date = DocumentGallery::localDateTimeFromTimestamp($v['timestamp']);
1040
-
1041
- echo '<tr data-entry="'.$v['thumb_id'].'"><td scope="row" class="check-column"><input type="checkbox" class="cb-ids" name="' . DG_OPTION_NAME . '[ids][]" value="' .
1042
- $v['thumb_id'].'"></td><td class="column-icon media-icon"><img src="' .
1043
- $icon.'" />'.'</td><td class="title column-title">' .
1044
- ($title ? '<strong><a href="' . home_url('/?attachment_id='.$v['thumb_id']).'" target="_blank" title="'.__('View', 'document-gallery').' \'' .
1045
- $title.'\' '.__('attachment page', 'document-gallery').'"><span class="editable-title">'.$title.'</span> <sup>'.$type.'</sup></a></strong>' : __('Attachment not found', 'document-gallery')) .
1046
- '<span class="dashicons dashicons-edit"></span><span class="edit-controls"><span class="dashicons dashicons-yes"></span> <span class="dashicons dashicons-no"></span></span></td><td class="column-description"><div class="editable-description">'.$description.'</div><span class="dashicons dashicons-edit"></span><span class="edit-controls"><span class="dashicons dashicons-yes"></span> <span class="dashicons dashicons-no"></span></span>'. '</td><td class="column-thumbupload">' .
1047
- '<span class="manual-download">' .
1048
- '<span class="dashicons dashicons-upload"></span>' .
1049
- '<span class="html5dndmarker">Drop file here<span> or </span></span>' .
1050
- '<span class="buttons-area">' .
1051
- '<input id="upload-button'.$v['thumb_id'].'" type="file" />' .
1052
- '<input id="trigger-button'.$v['thumb_id'].'" type="button" value="Select File" class="button" />' .
1053
- '</span>' .
1054
- '</span>' .
1055
- '</td><td class="date column-date">'.$date.'</td></tr>'.PHP_EOL;
1056
- } ?>
1057
- </tbody>
1058
- </table>
1059
- <div class="tablenav bottom"><?php echo $pagination; ?></div>
1060
- </div>
1061
- </div>
1062
- <?php }
1063
-
1064
- /**
1065
- * Adds meta box to the attchements' edit pages.
1066
- */
1067
- public static function addMetaBox() {
1068
- $screens = array( 'attachment' );
1069
- foreach ( $screens as $screen ) {
1070
- add_meta_box(
1071
- DG_OPTION_NAME.'_gen_box',
1072
- __( '<b>Thumbnail</b> for <i><b>Document Gallery</b></i>', 'document-gallery' ),
1073
- array(__CLASS__, 'renderMetaBox'),
1074
- $screen,
1075
- 'normal'
1076
- );
1077
- }
1078
- DG_Admin::$hook = 'post.php';
1079
- add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueueScriptsAndStyles'));
1080
- }
1081
-
1082
- /**
1083
- * Render a Meta Box.
1084
- */
1085
- public static function renderMetaBox($post) {
1086
- global $dg_options;
1087
- wp_nonce_field( DG_OPTION_NAME.'_meta_box', DG_OPTION_NAME.'_meta_box_nonce' );
1088
- $ID = $post->ID;
1089
- $icon = isset($dg_options['thumber']['thumbs'][$ID]['thumb_url']) ? $dg_options['thumber']['thumbs'][$ID]['thumb_url'] : DG_Thumber::getDefaultThumbnail($ID);
1090
-
1091
- echo '<table id="ThumbsTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">'.
1092
- '<tbody><tr data-entry="'.$ID.'"><td class="column-icon media-icon"><img src="' .
1093
- $icon.'" />'.'</td><td class="column-thumbupload">' .
1094
- '<span class="manual-download">' .
1095
- '<span class="dashicons dashicons-upload"></span>' .
1096
- '<span class="html5dndmarker">Drop file here<span> or </span></span>' .
1097
- '<span class="buttons-area">' .
1098
- '<input id="upload-button'.$ID.'" type="file" />' .
1099
- '<input id="trigger-button'.$ID.'" type="button" value="Select File" class="button" />' .
1100
- '</span>' .
1101
- '</span>' .
1102
- '</td></tr></tbody></table>'.
1103
- (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;
1104
- }
1105
-
1106
- /**
1107
- * Save a Meta Box.
1108
- */
1109
- public static function saveMetaBox($post_id) {
1110
- // Check if our nonce is set.
1111
- // Verify that the nonce is valid.
1112
- // If this is an autosave, our form has not been submitted, so we don't want to do anything.
1113
- if ( !isset($_POST[DG_OPTION_NAME.'_meta_box_nonce']) || !wp_verify_nonce($_POST[DG_OPTION_NAME.'_meta_box_nonce'], DG_OPTION_NAME.'_meta_box') || (defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE) ) {
1114
- return;
1115
- }
1116
-
1117
- global $dg_options;
1118
- $responseArr = array('result' => false);
1119
- if (isset($_POST[DG_OPTION_NAME]['entry'])) {
1120
- $ID = intval($_POST[DG_OPTION_NAME]['entry']);
1121
- } else {
1122
- $ID = -1;
1123
- }
1124
- if ( isset($_POST[DG_OPTION_NAME]['upload']) && isset($_FILES['file']) && isset($dg_options['thumber']['thumbs'][$ID]) ) {
1125
- $old_path = $dg_options['thumber']['thumbs'][$ID]['thumb_path'];
1126
- $uploaded_filename = self::validateUploadedFile();
1127
- if ($uploaded_filename && DG_Thumber::setThumbnail($ID, $uploaded_filename)) {
1128
- if ($dg_options['thumber']['thumbs'][$ID]['thumb_path'] !== $old_path) {
1129
- @unlink($old_path);
1130
- }
1131
- $responseArr['result'] = true;
1132
- $responseArr['url'] = $dg_options['thumber']['thumbs'][$ID]['thumb_url'];
1133
- }
1134
- }
1135
- if (isset($_POST[DG_OPTION_NAME]['ajax'])) {
1136
- echo DG_Util::jsonEncode($responseArr);
1137
- wp_die();
1138
- }
1139
- }
1140
-
1141
- /**
1142
- * Render the Logging table.
1143
- */
1144
- public static function renderLoggingSection() {
1145
- $log_list = DG_Logger::readLog();
1146
- if ($log_list) {
1147
- $levels = array_map(array(__CLASS__, 'getLogLabelSpan'), array_keys(DG_LogLevel::getLogLevels()));
1148
-
1149
- $fmt =
1150
- '<tr>' .
1151
- '<th scope="col" class="manage-column column-date sorted desc"><a href="javascript:void(0);">' .
1152
- '<span>%s</span><span class="sorting-indicator"></span></a>' .
1153
- '</th>' .
1154
- '<th scope="col" class="manage-column column-level"><span>%s</span></th>' .
1155
- '<th scope="col" class="manage-column column-message"><span>%s</span></th>' .
1156
- '</tr>';
1157
-
1158
- $thead = sprintf($fmt,
1159
- __('Date', 'document-gallery'),
1160
- __('Level', 'document-gallery'),
1161
- __('Message', 'document-gallery'));
1162
-
1163
- ?>
1164
- <div class="log-list-wrapper">
1165
- <div>
1166
- <div class="tablenav top">
1167
- <div class="alignleft bulkactions">
1168
- <button class="action expandAll">
1169
- <?php echo __('Expand All', 'document-gallery'); ?>
1170
- </button>
1171
- <button class="action collapseAll">
1172
- <?php echo __('Collapse All', 'document-gallery'); ?>
1173
- </button>
1174
- </div>
1175
- <div class="levelSelector">
1176
- <input type="checkbox" id="allLevels" name="lswitch" value="all" checked />
1177
- <label for="allLevels" class="allLevels">ALL</label>
1178
- <?php
1179
- foreach (array_keys(DG_LogLevel::getLogLevels()) as $k) { ?>
1180
- <?php
1181
- $lower = strtolower($k);
1182
- $upper = strtoupper($k);
1183
- ?>
1184
- <input type="checkbox" id="<?php echo $lower; ?>Level" name="lswitch" value="<?php echo $lower; ?>" checked />
1185
- <label for="<?php echo $lower; ?>Level" class="<?php echo $lower; ?>Level"><?php echo $upper; ?></label>
1186
- <?php }
1187
- ?>
1188
- </div>
1189
- </div>
1190
- <table id="LogTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">
1191
- <thead>
1192
- <?php echo $thead; ?>
1193
- </thead>
1194
- <tfoot>
1195
- <?php echo $thead; ?>
1196
- </tfoot>
1197
- <tbody><?php
1198
- for ($i = count($log_list); $i > 0; $i--) {
1199
- $log_entry = $log_list[$i - 1];
1200
- $date = DocumentGallery::localDateTimeFromTimestamp($log_entry[0]);
1201
-
1202
- // convert attachment names to links
1203
- $log_entry[2] = preg_replace('/[ ^](attachment #)(\d+)[., ]/i', ' <a href="' . home_url() . '/?attachment_id=\2" target="_blank">\1<strong>\2</strong></a> ', $log_entry[2]);
1204
-
1205
- // bold the place where log entry was submitted
1206
- $log_entry[2] = preg_replace('/^(\(\w+::\w+\)) /', '<strong>\1</strong> ', $log_entry[2]);
1207
-
1208
- // italicize any function references within log entry
1209
- $log_entry[2] = preg_replace('/(\(?\w+::\w+\)?)/m', '<i>\1</i>', $log_entry[2]);
1210
-
1211
- echo '<tr><td class="date column-date" data-sort-value="'.$log_entry[0].'"><span class="logLabel date">'.$date.'</span></td>' .
1212
- '<td class="column-level">'.$levels[$log_entry[1]].'</td>' .
1213
- '<td class="column-entry">'.(empty($log_entry[3]) ? '<pre>'.$log_entry[2].'</pre>' : '<div class="expander" title="Click to Expand"><pre>'.$log_entry[2].'</pre><div><span class="dashicons dashicons-arrow-down-alt2"></span></div></div><div class="spoiler-body"><pre>'.$log_entry[3].'</pre></div>').'</td>' .
1214
- '</tr>'.PHP_EOL;
1215
- } ?>
1216
- </tbody>
1217
- </table>
1218
- <div class="tablenav bottom">
1219
- <div class="alignright bulkactions">
1220
- <button class="button action clearLog" name = '<?php echo DG_OPTION_NAME; ?>[clearLog]' value = 'true'>
1221
- <?php echo __('Clear Log', 'document-gallery'); ?>
1222
- </button>
1223
- </div>
1224
- </div>
1225
- </div>
1226
- </div>
1227
- <?php } else {
1228
- echo '<div class="noLog">'.__('There are no log entries at this time.', 'document-gallery').'<br />'.__('For Your information:', 'document-gallery').' <strong><i>'.__('Logging', 'document-gallery').'</i></strong> '.(DG_Logger::logEnabled()?'<span class="loggingON">'.__('is turned ON', 'document-gallery').'!</span>':'<span class="loggingOFF">'.__('is turned OFF', 'document-gallery').'!</span>').'</div>';
1229
- }
1230
- }
1231
-
1232
- /**
1233
- * Takes label name and returns SPAN tag.
1234
- * @param string $e label name.
1235
- * @return string SPAN tag
1236
- */
1237
- private static function getLogLabelSpan($e) {
1238
- return '<span class="logLabel ' . strtolower($e) . '">' . strtoupper($e) . '</span>';
1239
- }
1240
-
1241
- /**
1242
- * Render a checkbox field.
1243
- * @param array $args
1244
- */
1245
- public static function renderCheckboxField($args) {
1246
- $args['disabled'] = isset($args['disabled']) ? $args['disabled'] : false;
1247
- printf('<label><input type="checkbox" value="1" name="%1$s[%2$s]" id="%3$s" %4$s %5$s/> %6$s</label>',
1248
- $args['option_name'],
1249
- $args['name'],
1250
- $args['label_for'],
1251
- checked($args['value'], 1, false),
1252
- disabled($args['disabled'], true, false),
1253
- $args['description']);
1254
- }
1255
-
1256
- /**
1257
- * Render a text field.
1258
- * @param array $args
1259
- */
1260
- public static function renderTextField($args) {
1261
- printf('<input type="%1$s" value="%2$s" name="%3$s[%4$s]" id="%5$s" /> %6$s',
1262
- isset($args['type']) ? $args['type'] : 'text',
1263
- $args['value'],
1264
- $args['option_name'],
1265
- $args['name'],
1266
- $args['label_for'],
1267
- $args['description']);
1268
- }
1269
-
1270
- /**
1271
- * Accepts a two-dimensional array where each inner array consists of valid arguments for renderTextField.
1272
- * @param array $args
1273
- */
1274
- public static function renderMultiTextField($args) {
1275
- foreach ($args as $arg) {
1276
- self::renderTextField($arg);
1277
- }
1278
- }
1279
-
1280
- /**
1281
- * Render a select field.
1282
- * @param array $args
1283
- */
1284
- public static function renderSelectField($args) {
1285
- printf('<select name="%1$s[%2$s]" id="%3$s">',
1286
- $args['option_name'],
1287
- $args['name'],
1288
- $args['label_for']);
1289
-
1290
- foreach ($args['options'] as $val) {
1291
- printf('<option value="%1$s" %2$s>%3$s</option>',
1292
- $val,
1293
- selected($val, $args['value'], false),
1294
- $val,
1295
- $args['description']);
1296
- }
1297
-
1298
- print '</select> ' . $args['description'];
1299
- }
1300
-
1301
- /**
1302
- * Wraps the PHP exit language construct.
1303
- */
1304
- public static function _exit() {
1305
- exit;
1306
- }
1307
-
1308
- /**
1309
- * Blocks instantiation. All functions are static.
1310
- */
1311
- private function __construct() {
1312
-
1313
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1314
  }
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  class DG_Admin {
5
+ /**
6
+ * @var string The hook for the Document Gallery settings page.
7
+ */
8
+ private static $hook;
9
+
10
+ /**
11
+ * @var string The current tab being rendered.
12
+ */
13
+ private static $current;
14
+
15
+ /**
16
+ * NOTE: This should only ever be accessed through getTabs().
17
+ *
18
+ * @var multitype:string Associative array containing all tab names, keyed by tab slug.
19
+ */
20
+ private static $tabs;
21
+
22
+ /**
23
+ * Returns reference to tabs array, initializing if needed.
24
+ *
25
+ * NOTE: This cannot be done in a static constructor due to timing with i18n.
26
+ */
27
+ public static function &getTabs() {
28
+ if ( ! isset( self::$tabs ) ) {
29
+ self::$tabs = array(
30
+ 'General' => __( 'General', 'document-gallery' ),
31
+ 'Thumbnail' => __( 'Thumbnail Management', 'document-gallery' ),
32
+ 'Logging' => __( 'Logging', 'document-gallery' ),
33
+ 'Advanced' => __( 'Advanced', 'document-gallery' )
34
+ );
35
+ }
36
+
37
+ return self::$tabs;
38
+ }
39
+
40
+ /**
41
+ * Renders Document Gallery options page.
42
+ */
43
+ public static function renderOptions() { ?>
44
+ <div class="wrap">
45
+ <h2><?php echo __( 'Document Gallery Settings', 'document-gallery' ); ?></h2>
46
+
47
+ <h2 class="nav-tab-wrapper">
48
+ <?php foreach ( self::getTabs() as $tab => $name ) {
49
+ $class = ( $tab == self::$current ) ? ' nav-tab-active' : '';
50
+ echo '<a class="nav-tab ' . $tab . '-tab' . $class . '" href="?page=' . DG_OPTION_NAME . '&tab=' . $tab . '">' . $name . '</a>';
51
+ } ?>
52
+ </h2>
53
+
54
+ <form method="post" action="options.php" id="tab-<?php echo self::$current ?>">
55
+ <input type="hidden" name="<?php echo DG_OPTION_NAME; ?>[tab]" value="<?php echo self::$current; ?>"/>
56
+ <?php
57
+ settings_fields( DG_OPTION_NAME );
58
+ do_settings_sections( DG_OPTION_NAME );
59
+ if ( self::$current != 'Thumbnail' && self::$current != 'Logging' ) {
60
+ submit_button();
61
+ }
62
+ ?>
63
+ </form>
64
+
65
+ </div>
66
+ <?php }
67
+
68
+ /**
69
+ * Adds settings link to main plugin view.
70
+ */
71
+ public static function addSettingsLink( $links ) {
72
+ $settings = '<a href="options-general.php?page=' . DG_OPTION_NAME . '">' .
73
+ __( 'Settings', 'document-gallery' ) . '</a>';
74
+ array_unshift( $links, $settings );
75
+
76
+ return $links;
77
+ }
78
+
79
+ /**
80
+ * Adds donate link to main plugin view.
81
+ */
82
+ public static function addDonateLink( $links, $file ) {
83
+ if ( $file === DG_BASENAME ) {
84
+ global $dg_options;
85
+
86
+ $donate = '<strong><a href="' . $dg_options['meta']['donate_link'] . '">' .
87
+ __( 'Donate', 'document-gallery' ) . '</a></strong>';
88
+ $links[] = $donate;
89
+ }
90
+
91
+ return $links;
92
+ }
93
+
94
+ /**
95
+ * Adds Document Gallery settings page to admin navigation.
96
+ */
97
+ public static function addAdminPage() {
98
+ DG_Admin::$hook = add_options_page(
99
+ __( 'Document Gallery Settings', 'document-gallery' ),
100
+ __( 'Document Gallery', 'document-gallery' ),
101
+ 'manage_options', DG_OPTION_NAME, array( __CLASS__, 'renderOptions' ) );
102
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueueScriptsAndStyles' ) );
103
+ }
104
+
105
+ /**
106
+ * Enqueues styles and scripts for the admin settings page.
107
+ */
108
+ public static function enqueueScriptsAndStyles( $hook ) {
109
+ if ( in_array( $hook, array( DG_Admin::$hook, 'post.php', 'post-new.php' ), true ) ) {
110
+ // Settings Page
111
+ wp_enqueue_style( 'document-gallery-admin', DG_URL . 'assets/css/admin.css', null, DG_VERSION );
112
+
113
+ // gracefully degrade for older WP versions
114
+ if ( version_compare( get_bloginfo( 'version' ), '3.8', '<' ) ) {
115
+ echo '<style type="text/css">.dashicons, .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before, .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after, #ThumbsTable .title a:after, #LogTable>tbody a:after {display: none !important;}</style>' . PHP_EOL;
116
+ }
117
+
118
+ wp_enqueue_script( 'document-gallery-admin', DG_URL . 'assets/js/admin.js', array( 'jquery' ), DG_VERSION, true );
119
+ wp_localize_script( 'document-gallery-admin', 'dg_admin_vars', array( 'upload_limit' => wp_max_upload_size() ) );
120
+ if ( $hook !== self::$hook ) { //if $hook is 'post.php' or 'post-new.php'
121
+ wp_localize_script( 'document-gallery-admin', 'ajax_object', array( 'ajax_url' => admin_url( 'admin-ajax.php' ) ) );
122
+
123
+ // Media Manager
124
+ global $dg_options;
125
+ wp_enqueue_script( 'document-gallery-media-manager', DG_URL . 'assets/js/media_manager.js', array( 'media-views' ), DG_VERSION, true );
126
+ wp_localize_script( 'document-gallery-media-manager', 'DGl10n', array(
127
+ 'documentGalleryMenuTitle' => __( 'Create Document Gallery', 'document-gallery' ),
128
+ 'documentGalleryButton' => __( 'Create a new Document Gallery', 'document-gallery' ),
129
+ 'cancelDocumentGalleryTitle' => '&#8592; ' . __( 'Cancel Document Gallery', 'document-gallery' ),
130
+ 'updateDocumentGallery' => __( 'Update Document Gallery', 'document-gallery' ),
131
+ 'insertDocumentGallery' => __( 'Insert Document Gallery', 'document-gallery' ),
132
+ 'addToDocumentGallery' => __( 'Add to Document Gallery', 'document-gallery' ),
133
+ 'addToDocumentGalleryTitle' => __( 'Add to Document Gallery', 'document-gallery' ),
134
+ 'editDocumentGalleryTitle' => __( 'Edit Document Gallery', 'document-gallery' )
135
+ ) );
136
+ wp_localize_script( 'document-gallery-media-manager', 'documentGalleryDefaults', $dg_options['gallery'] );
137
+ }
138
+ }
139
+ }
140
+
141
+ /**
142
+ * Load Document Gallery Custom templates.
143
+ */
144
+ public static function loadCustomTemplates() {
145
+ include_once DG_PATH . 'admin/media-manager-template.php';
146
+ }
147
+
148
+ /**
149
+ * Registers settings for the Document Gallery options page.
150
+ */
151
+ public static function registerSettings() {
152
+ if ( empty( $_REQUEST['tab'] ) || ! array_key_exists( $_REQUEST['tab'], self::getTabs() ) ) {
153
+ reset( self::getTabs() );
154
+ self::$current = key( self::getTabs() );
155
+ } else {
156
+ self::$current = $_REQUEST['tab'];
157
+ }
158
+
159
+ register_setting( DG_OPTION_NAME, DG_OPTION_NAME, array( __CLASS__, 'validateSettings' ) );
160
+
161
+ $funct = 'register' . self::$current . 'Settings';
162
+ DG_Admin::$funct();
163
+ }
164
+
165
+ /**
166
+ * Registers settings for the general tab.
167
+ */
168
+ private static function registerGeneralSettings() {
169
+ global $dg_options;
170
+
171
+ include_once DG_PATH . 'inc/class-gallery.php';
172
+ include_once DG_PATH . 'inc/class-thumber.php';
173
+
174
+ $defaults = $dg_options['gallery'];
175
+ $active = $dg_options['thumber']['active'];
176
+
177
+ add_settings_section(
178
+ 'gallery_defaults', __( 'Default Settings', 'document-gallery' ),
179
+ array( __CLASS__, 'renderDefaultSettingsSection' ), DG_OPTION_NAME );
180
+
181
+ add_settings_section(
182
+ 'thumbnail_generation', __( 'Thumbnail Generation', 'document-gallery' ),
183
+ array( __CLASS__, 'renderThumberSection' ), DG_OPTION_NAME );
184
+
185
+ add_settings_section(
186
+ 'css', __( 'Custom CSS', 'document-gallery' ),
187
+ array( __CLASS__, 'renderCssSection' ), DG_OPTION_NAME );
188
+
189
+ add_settings_field(
190
+ 'gallery_defaults_attachment_pg', 'attachment_pg',
191
+ array( __CLASS__, 'renderCheckboxField' ),
192
+ DG_OPTION_NAME, 'gallery_defaults',
193
+ array(
194
+ 'label_for' => 'label_gallery_defaults_attachment_pg',
195
+ 'name' => 'gallery_defaults][attachment_pg',
196
+ 'value' => esc_attr( $defaults['attachment_pg'] ),
197
+ 'option_name' => DG_OPTION_NAME,
198
+ 'description' => __( 'Link to attachment page rather than to file', 'document-gallery' )
199
+ ) );
200
+
201
+ add_settings_field(
202
+ 'gallery_defaults_columns', 'columns',
203
+ array( __CLASS__, 'renderTextField' ),
204
+ DG_OPTION_NAME, 'gallery_defaults',
205
+ array(
206
+ 'label_for' => 'label_gallery_defaults_columns',
207
+ 'name' => 'gallery_defaults][columns',
208
+ 'value' => esc_attr( $defaults['columns'] ),
209
+ 'type' => 'number" min="1" step="1',
210
+ 'option_name' => DG_OPTION_NAME,
211
+ 'description' => __( 'The number of columns to display when not rendering descriptions.', 'document-gallery' )
212
+ ) );
213
+
214
+ add_settings_field(
215
+ 'gallery_defaults_descriptions', 'descriptions',
216
+ array( __CLASS__, 'renderCheckboxField' ),
217
+ DG_OPTION_NAME, 'gallery_defaults',
218
+ array(
219
+ 'label_for' => 'label_gallery_defaults_descriptions',
220
+ 'name' => 'gallery_defaults][descriptions',
221
+ 'value' => esc_attr( $defaults['descriptions'] ),
222
+ 'option_name' => DG_OPTION_NAME,
223
+ 'description' => __( 'Include document descriptions', 'document-gallery' )
224
+ ) );
225
+
226
+ add_settings_field(
227
+ 'gallery_defaults_fancy', 'fancy',
228
+ array( __CLASS__, 'renderCheckboxField' ),
229
+ DG_OPTION_NAME, 'gallery_defaults',
230
+ array(
231
+ 'label_for' => 'label_gallery_defaults_fancy',
232
+ 'name' => 'gallery_defaults][fancy',
233
+ 'value' => esc_attr( $defaults['fancy'] ),
234
+ 'option_name' => DG_OPTION_NAME,
235
+ 'description' => __( 'Use auto-generated document thumbnails', 'document-gallery' )
236
+ ) );
237
+
238
+ add_settings_field(
239
+ 'gallery_defaults_order', 'order',
240
+ array( __CLASS__, 'renderSelectField' ),
241
+ DG_OPTION_NAME, 'gallery_defaults',
242
+ array(
243
+ 'label_for' => 'label_gallery_defaults_order',
244
+ 'name' => 'gallery_defaults][order',
245
+ 'value' => esc_attr( $defaults['order'] ),
246
+ 'options' => DG_Gallery::getOrderOptions(),
247
+ 'option_name' => DG_OPTION_NAME,
248
+ 'description' => __( 'Ascending or descending sorting of documents', 'document-gallery' )
249
+ ) );
250
+
251
+ add_settings_field(
252
+ 'gallery_defaults_orderby', 'orderby',
253
+ array( __CLASS__, 'renderSelectField' ),
254
+ DG_OPTION_NAME, 'gallery_defaults',
255
+ array(
256
+ 'label_for' => 'label_gallery_defaults_orderby',
257
+ 'name' => 'gallery_defaults][orderby',
258
+ 'value' => esc_attr( $defaults['orderby'] ),
259
+ 'options' => DG_Gallery::getOrderbyOptions(),
260
+ 'option_name' => DG_OPTION_NAME,
261
+ 'description' => __( 'Which field to order documents by', 'document-gallery' )
262
+ ) );
263
+
264
+ add_settings_field(
265
+ 'gallery_defaults_relation', 'relation',
266
+ array( __CLASS__, 'renderSelectField' ),
267
+ DG_OPTION_NAME, 'gallery_defaults',
268
+ array(
269
+ 'label_for' => 'label_gallery_defaults_relation',
270
+ 'name' => 'gallery_defaults][relation',
271
+ 'value' => esc_attr( $defaults['relation'] ),
272
+ 'options' => DG_Gallery::getRelationOptions(),
273
+ 'option_name' => DG_OPTION_NAME,
274
+ 'description' => __( 'Whether matched documents must have all taxa_names (AND) or at least one (OR)', 'document-gallery' )
275
+ ) );
276
+
277
+ add_settings_field(
278
+ 'gallery_defaults_limit', 'limit',
279
+ array( __CLASS__, 'renderTextField' ),
280
+ DG_OPTION_NAME, 'gallery_defaults',
281
+ array(
282
+ 'label_for' => 'label_gallery_defaults_limit',
283
+ 'name' => 'gallery_defaults][limit',
284
+ 'value' => esc_attr( $defaults['limit'] ),
285
+ 'type' => 'number" min="-1" step="1',
286
+ 'option_name' => DG_OPTION_NAME,
287
+ 'description' => __( 'Limit the number of documents included. -1 means no limit.', 'document-gallery' )
288
+ ) );
289
+
290
+ add_settings_field(
291
+ 'gallery_defaults_mime_types', 'mime_types',
292
+ array( __CLASS__, 'renderTextField' ),
293
+ DG_OPTION_NAME, 'gallery_defaults',
294
+ array(
295
+ 'label_for' => 'label_gallery_defaults_mime_types',
296
+ 'name' => 'gallery_defaults][mime_types',
297
+ 'value' => esc_attr( $defaults['mime_types'] ),
298
+ 'type' => 'text',
299
+ 'option_name' => DG_OPTION_NAME,
300
+ 'description' => __( 'Comma-delimited list of <a href="http://en.wikipedia.org/wiki/Internet_media_type#List_of_common_media_types">MIME types</a>.', 'document-gallery' )
301
+ ) );
302
+
303
+ add_settings_field(
304
+ 'gallery_defaults_new_window', 'new_window',
305
+ array( __CLASS__, 'renderCheckboxField' ),
306
+ DG_OPTION_NAME, 'gallery_defaults',
307
+ array(
308
+ 'label_for' => 'label_gallery_defaults_new_window',
309
+ 'name' => 'gallery_defaults][new_window',
310
+ 'value' => esc_attr( $defaults['new_window'] ),
311
+ 'option_name' => DG_OPTION_NAME,
312
+ 'description' => __( 'Open thumbnail links in new window', 'document-gallery' ) . '.'
313
+ ) );
314
+
315
+ add_settings_field(
316
+ 'gallery_defaults_post_status', 'post_status',
317
+ array( __CLASS__, 'renderSelectField' ),
318
+ DG_OPTION_NAME, 'gallery_defaults',
319
+ array(
320
+ 'label_for' => 'label_gallery_defaults_post_status',
321
+ 'name' => 'gallery_defaults][post_status',
322
+ 'value' => esc_attr( $defaults['post_status'] ),
323
+ 'options' => DG_Gallery::getPostStatuses(),
324
+ 'option_name' => DG_OPTION_NAME,
325
+ 'description' => __( 'Which post status to look for when querying documents.', 'document-gallery' )
326
+ ) );
327
+
328
+ add_settings_field(
329
+ 'gallery_defaults_post_type', 'post_type',
330
+ array( __CLASS__, 'renderSelectField' ),
331
+ DG_OPTION_NAME, 'gallery_defaults',
332
+ array(
333
+ 'label_for' => 'label_gallery_defaults_post_type',
334
+ 'name' => 'gallery_defaults][post_type',
335
+ 'value' => esc_attr( $defaults['post_type'] ),
336
+ 'options' => DG_Gallery::getPostTypes(),
337
+ 'option_name' => DG_OPTION_NAME,
338
+ 'description' => __( 'Which post type to look for when querying documents.', 'document-gallery' )
339
+ ) );
340
+
341
+ add_settings_field(
342
+ 'thumbnail_generation_av', __( 'Audio/Video', 'document-gallery' ),
343
+ array( __CLASS__, 'renderCheckboxField' ),
344
+ DG_OPTION_NAME, 'thumbnail_generation',
345
+ array(
346
+ 'label_for' => 'label_thumbnail_generation_av',
347
+ 'name' => 'thumbnail_generation][av',
348
+ 'value' => esc_attr( $active['av'] ),
349
+ 'option_name' => DG_OPTION_NAME,
350
+ 'description' => esc_html__( 'Locally generate thumbnails for audio & video files.', 'document-gallery' )
351
+ ) );
352
+
353
+ add_settings_field(
354
+ 'thumbnail_generation_gs', 'Ghostscript',
355
+ array( __CLASS__, 'renderCheckboxField' ),
356
+ DG_OPTION_NAME, 'thumbnail_generation',
357
+ array(
358
+ 'label_for' => 'label_thumbnail_generation_gs',
359
+ 'name' => 'thumbnail_generation][gs',
360
+ 'value' => esc_attr( $active['gs'] ),
361
+ 'option_name' => DG_OPTION_NAME,
362
+ 'description' => DG_Thumber::isGhostscriptAvailable()
363
+ ? __( 'Use <a href="http://www.ghostscript.com/" target="_blank">Ghostscript</a> for faster local PDF processing (compared to Imagick).', 'document-gallery' )
364
+ : __( 'Your server is not configured to run <a href="http://www.ghostscript.com/" target="_blank">Ghostscript</a>.', 'document-gallery' ),
365
+ 'disabled' => ! DG_Thumber::isGhostscriptAvailable()
366
+ ) );
367
+
368
+ add_settings_field(
369
+ 'thumbnail_generation_imagick', 'Imagick',
370
+ array( __CLASS__, 'renderCheckboxField' ),
371
+ DG_OPTION_NAME, 'thumbnail_generation',
372
+ array(
373
+ 'label_for' => 'label_thumbnail_generation_imagick',
374
+ 'name' => 'thumbnail_generation][imagick',
375
+ 'value' => esc_attr( $active['imagick'] ),
376
+ 'option_name' => DG_OPTION_NAME,
377
+ 'description' => DG_Thumber::isImagickAvailable()
378
+ ? __( 'Use <a href="http://www.php.net/manual/en/book.imagick.php" target="_blank">Imagick</a> to handle lots of filetypes locally.', 'document-gallery' )
379
+ : __( 'Your server is not configured to run <a href="http://www.php.net/manual/en/book.imagick.php" target="_blank">Imagick</a>.', 'document-gallery' ),
380
+ 'disabled' => ! DG_Thumber::isImagickAvailable()
381
+ ) );
382
+
383
+ add_settings_field(
384
+ 'thumbnail_generation_width', __( 'Max Thumbnail Dimensions', 'document-gallery' ),
385
+ array( __CLASS__, 'renderMultiTextField' ),
386
+ DG_OPTION_NAME, 'thumbnail_generation',
387
+ array(
388
+ array(
389
+ 'label_for' => 'label_advanced_width',
390
+ 'name' => 'thumbnail_generation][width',
391
+ 'value' => esc_attr( $dg_options['thumber']['width'] ),
392
+ 'type' => 'number" min="1" step="1',
393
+ 'option_name' => DG_OPTION_NAME,
394
+ 'description' => ' x '
395
+ ),
396
+ array(
397
+ 'label_for' => 'label_advanced_height',
398
+ 'name' => 'thumbnail_generation][height',
399
+ 'value' => esc_attr( $dg_options['thumber']['height'] ),
400
+ 'type' => 'number" min="1" step="1',
401
+ 'option_name' => DG_OPTION_NAME,
402
+ 'description' => __( 'The max width and height (in pixels) that thumbnails will be generated.', 'document-gallery' )
403
+ )
404
+ ) );
405
+ }
406
+
407
+ /**
408
+ * Registers settings for the thumbnail management tab.
409
+ */
410
+ private static function registerThumbnailSettings() {
411
+ add_settings_section(
412
+ 'thumbnail_table', '',
413
+ array( __CLASS__, 'renderThumbnailSection' ), DG_OPTION_NAME );
414
+ }
415
+
416
+ /**
417
+ * Registers settings for the logging tab.
418
+ */
419
+ private static function registerLoggingSettings() {
420
+ add_settings_section(
421
+ 'logging_table', '',
422
+ array( __CLASS__, 'renderLoggingSection' ), DG_OPTION_NAME );
423
+ }
424
+
425
+ /**
426
+ * Registers settings for the advanced tab.
427
+ */
428
+ private static function registerAdvancedSettings() {
429
+ global $dg_options;
430
+
431
+ add_settings_section(
432
+ 'advanced', __( 'Advanced Thumbnail Generation', 'document-gallery' ),
433
+ array( __CLASS__, 'renderAdvancedSection' ), DG_OPTION_NAME );
434
+
435
+ add_settings_field(
436
+ 'advanced_logging_enabled', __( 'Logging Enabled', 'document-gallery' ),
437
+ array( __CLASS__, 'renderCheckboxField' ),
438
+ DG_OPTION_NAME, 'advanced',
439
+ array(
440
+ 'label_for' => 'label_advanced_logging_enabled',
441
+ 'name' => 'logging_enabled',
442
+ 'value' => esc_attr( $dg_options['logging']['enabled'] ),
443
+ 'option_name' => DG_OPTION_NAME,
444
+ 'description' => __( 'Whether to log debug and error information related to Document Gallery.', 'document-gallery' )
445
+ ) );
446
+
447
+ add_settings_field(
448
+ 'advanced_logging_purge_interval', __( 'Logging Purge Interval', 'document-gallery' ),
449
+ array( __CLASS__, 'renderTextField' ),
450
+ DG_OPTION_NAME, 'advanced',
451
+ array(
452
+ 'label_for' => 'label_advanced_logging_purge_interval',
453
+ 'name' => 'logging_purge_interval',
454
+ 'value' => esc_attr( $dg_options['logging']['purge_interval'] ),
455
+ 'type' => 'number" min="0" step="1',
456
+ 'option_name' => DG_OPTION_NAME,
457
+ 'description' => __( 'Number of days to keep old log entries (0 disables purging).', 'document-gallery' )
458
+ ) );
459
+
460
+ add_settings_field(
461
+ 'advanced_validation', __( 'Option Validation', 'document-gallery' ),
462
+ array( __CLASS__, 'renderCheckboxField' ),
463
+ DG_OPTION_NAME, 'advanced',
464
+ array(
465
+ 'label_for' => 'label_advanced_validation',
466
+ 'name' => 'validation',
467
+ 'value' => esc_attr( $dg_options['validation'] ),
468
+ 'option_name' => DG_OPTION_NAME,
469
+ 'description' => __( 'Whether option structure should be validated before save. This is not generally necessary.', 'document-gallery' )
470
+ ) );
471
+
472
+ add_settings_field(
473
+ 'advanced_thumb_timeout', __( 'Thumbnail Generation Timeout', 'document-gallery' ),
474
+ array( __CLASS__, 'renderTextField' ),
475
+ DG_OPTION_NAME, 'advanced',
476
+ array(
477
+ 'label_for' => 'label_advanced_thumb_timeout',
478
+ 'name' => 'timeout',
479
+ 'value' => esc_attr( $dg_options['thumber']['timeout'] ),
480
+ 'type' => 'number" min="1" step="1',
481
+ 'option_name' => DG_OPTION_NAME,
482
+ 'description' => __( 'Max number of seconds to wait for thumbnail generation before defaulting to filetype icons.', 'document-gallery' ) .
483
+ ' <em>' . __( 'Note that generation will continue where timeout happened next time the gallery is loaded.', 'document-gallery' ) . '</em>'
484
+ ) );
485
+
486
+ add_settings_field(
487
+ 'advanced_gs', __( 'Ghostscript Absolute Path', 'document-gallery' ),
488
+ array( __CLASS__, 'renderTextField' ),
489
+ DG_OPTION_NAME, 'advanced',
490
+ array(
491
+ 'label_for' => 'label_advanced_gs',
492
+ 'name' => 'gs',
493
+ 'value' => esc_attr( $dg_options['thumber']['gs'] ),
494
+ 'option_name' => DG_OPTION_NAME,
495
+ 'description' => $dg_options['thumber']['gs']
496
+ ? __( 'Successfully auto-detected the location of Ghostscript.', 'document-gallery' )
497
+ : __( 'Failed to auto-detect the location of Ghostscript.', 'document-gallery' )
498
+ ) );
499
+
500
+ add_settings_section(
501
+ 'advanced_options_dump', __( 'Options Array Dump', 'document-gallery' ),
502
+ array( __CLASS__, 'renderOptionsDumpSection' ), DG_OPTION_NAME );
503
+ }
504
+
505
+ /**
506
+ * Validates submitted options, sanitizing any invalid options.
507
+ *
508
+ * @param array $values User-submitted new options.
509
+ *
510
+ * @return array Sanitized new options.
511
+ */
512
+ public static function validateSettings( $values ) {
513
+ // NOTE: WP double-calls this function -- below logic prevents potential
514
+ // side effects by processing a maximum of one call to validate
515
+ // per page load, re-returning the previous result on any
516
+ // subsequent calls.
517
+ static $ret = null;
518
+ if ( is_null( $ret ) ) {
519
+ if ( empty( $values['tab'] ) || ! array_key_exists( $values['tab'], self::getTabs() ) ) {
520
+ reset( self::getTabs() );
521
+ $values['tab'] = key( self::getTabs() );
522
+ }
523
+ $funct = 'validate' . $values['tab'] . 'Settings';
524
+ unset( $values['tab'] );
525
+ $ret = DG_Admin::$funct( $values );
526
+ }
527
+
528
+ return $ret;
529
+ }
530
+
531
+ /**
532
+ * Validates general settings, sanitizing any invalid options.
533
+ *
534
+ * @param array $values User-submitted new options.
535
+ *
536
+ * @return array Sanitized new options.
537
+ */
538
+ private static function validateGeneralSettings( $values ) {
539
+ global $dg_options;
540
+ $ret = $dg_options;
541
+
542
+ include_once DG_PATH . 'inc/class-gallery.php';
543
+
544
+ $thumbs_cleared = false;
545
+
546
+ // handle gallery shortcode defaults
547
+ $errs = array();
548
+ $ret['gallery'] = DG_Gallery::sanitizeDefaults( null, $values['gallery_defaults'], $errs );
549
+
550
+ foreach ( $errs as $k => $v ) {
551
+ add_settings_error( DG_OPTION_NAME, str_replace( '_', '-', $k ), $v );
552
+ }
553
+
554
+ // handle setting width
555
+ if ( isset( $values['thumbnail_generation']['width'] ) ) {
556
+ $width = (int) $values['thumbnail_generation']['width'];
557
+ if ( $width > 0 ) {
558
+ $ret['thumber']['width'] = $width;
559
+ } else {
560
+ add_settings_error( DG_OPTION_NAME, 'thumber-width',
561
+ __( 'Invalid width given: ', 'document-gallery' ) . $values['thumbnail_generation']['width'] );
562
+ }
563
+
564
+ unset( $values['thumbnail_generation']['width'] );
565
+ }
566
+
567
+ // handle setting height
568
+ if ( isset( $values['thumbnail_generation']['height'] ) ) {
569
+ $height = (int) $values['thumbnail_generation']['height'];
570
+ if ( $height > 0 ) {
571
+ $ret['thumber']['height'] = $height;
572
+ } else {
573
+ add_settings_error( DG_OPTION_NAME, 'thumber-height',
574
+ __( 'Invalid height given: ', 'document-gallery' ) . $values['thumbnail_generation']['height'] );
575
+ }
576
+
577
+ unset( $values['thumbnail_generation']['width'] );
578
+ }
579
+
580
+ // delete thumb cache to force regeneration if max dimensions changed
581
+ if ( $ret['thumber']['width'] !== $dg_options['thumber']['width'] ||
582
+ $ret['thumber']['height'] !== $dg_options['thumber']['height']
583
+ ) {
584
+ foreach ( $ret['thumber']['thumbs'] as $v ) {
585
+ if ( isset( $v['thumber'] ) ) {
586
+ @unlink( $v['thumb_path'] );
587
+ }
588
+ }
589
+
590
+ $ret['thumber']['thumbs'] = array();
591
+ $thumbs_cleared = true;
592
+ }
593
+
594
+ // handle setting the active thumbers
595
+ foreach ( array_keys( $ret['thumber']['active'] ) as $k ) {
596
+ $ret['thumber']['active'][ $k ] = isset( $values['thumbnail_generation'][ $k ] );
597
+ }
598
+
599
+ // if new thumbers available, clear failed thumbnails for retry
600
+ if ( ! $thumbs_cleared ) {
601
+ foreach ( $dg_options['thumber']['active'] as $k => $v ) {
602
+ if ( ! $v && $ret['thumber']['active'][ $k ] ) {
603
+ foreach ( $dg_options['thumber']['thumbs'] as $k => $v ) {
604
+ if ( empty( $v['thumber'] ) ) {
605
+ unset( $ret['thumber']['thumbs'][ $k ] );
606
+ }
607
+ }
608
+ break;
609
+ }
610
+ }
611
+ }
612
+
613
+ // handle modified CSS
614
+ if ( trim( $ret['css']['text'] ) !== trim( $values['css'] ) ) {
615
+ $ret['css']['text'] = trim( $values['css'] );
616
+ }
617
+
618
+ return $ret;
619
+ }
620
+
621
+ /**
622
+ * Validates thumbnail management settings, sanitizing any invalid options.
623
+ *
624
+ * @param array $values User-submitted new options.
625
+ *
626
+ * @return array Sanitized new options.
627
+ */
628
+ private static function validateThumbnailSettings( $values ) {
629
+ global $dg_options;
630
+ $ret = $dg_options;
631
+ $responseArr = array( 'result' => false );
632
+
633
+ if ( isset( $values['entry'] ) ) {
634
+ $ID = intval( $values['entry'] );
635
+ } else {
636
+ $ID = - 1;
637
+ }
638
+
639
+ // Thumbnail(s) cleanup;
640
+ // cleanup value is a marker
641
+ if ( isset( $values['cleanup'] ) && isset( $values['ids'] ) ) {
642
+ $deleted = array_values( array_intersect( array_keys( $dg_options['thumber']['thumbs'] ), $values['ids'] ) );
643
+
644
+ foreach ( $deleted as $k ) {
645
+ if ( isset( $ret['thumber']['thumbs'][ $k ]['thumber'] ) ) {
646
+ @unlink( $ret['thumber']['thumbs'][ $k ]['thumb_path'] );
647
+ }
648
+
649
+ unset( $ret['thumber']['thumbs'][ $k ] );
650
+ }
651
+
652
+ $responseArr['result'] = true;
653
+ $responseArr['deleted'] = $deleted;
654
+ }
655
+
656
+ // Attachment title update
657
+ // title value is a marker
658
+ elseif ( isset( $values['title'] ) && $ID != - 1 ) {
659
+ $attachment = array(
660
+ 'ID' => $ID,
661
+ 'post_title' => rawurldecode( addslashes( $values['title'] ) )
662
+ );
663
+ if ( wp_update_post( $attachment ) ) {
664
+ $responseArr['result'] = true;
665
+ }
666
+ }
667
+
668
+ // Attachment description update
669
+ // description value is a marker
670
+ elseif ( isset( $values['description'] ) && $ID != - 1 ) {
671
+ $attachment = array(
672
+ 'ID' => $ID,
673
+ 'post_content' => rawurldecode( addslashes( $values['description'] ) )
674
+ );
675
+ if ( wp_update_post( $attachment ) ) {
676
+ $responseArr['result'] = true;
677
+ }
678
+ }
679
+
680
+ // Thumbnail file manual refresh (one at a time)
681
+ // upload value is a marker
682
+ elseif ( isset( $values['upload'] ) && isset( $_FILES['file'] ) && isset( $ret['thumber']['thumbs'][ $ID ] ) ) {
683
+ $old_path = $ret['thumber']['thumbs'][ $ID ]['thumb_path'];
684
+ $uploaded_filename = self::validateUploadedFile();
685
+ if ( $uploaded_filename && DG_Thumber::setThumbnail( $ID, $uploaded_filename ) ) {
686
+ if ( $dg_options['thumber']['thumbs'][ $ID ]['thumb_path'] !== $old_path ) {
687
+ @unlink( $old_path );
688
+ }
689
+ $responseArr['result'] = true;
690
+ $responseArr['url'] = $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'];
691
+ $ret['thumber']['thumbs'][ $ID ] = $dg_options['thumber']['thumbs'][ $ID ];
692
+ }
693
+ }
694
+
695
+ if ( isset( $values['ajax'] ) ) {
696
+ echo DG_Util::jsonEncode( $responseArr );
697
+ add_filter( 'wp_redirect', array( __CLASS__, '_exit' ), 1, 0 );
698
+ }
699
+
700
+ return $ret;
701
+ }
702
+
703
+ /**
704
+ * Validates uploaded file as a semi for potential thumbnail.
705
+ *
706
+ * @param string $var File field name.
707
+ *
708
+ * @return bool|string False on failure, path to temp file on success.
709
+ */
710
+ public static function validateUploadedFile( $var = 'file' ) {
711
+ // checking if any file was delivered
712
+ if ( ! isset( $_FILES[ $var ] ) ) {
713
+ return false;
714
+ }
715
+ // we gonna process only first one
716
+ if ( ! is_array( $_FILES[ $var ]['error'] ) ) {
717
+ $upload_err = $_FILES[ $var ]['error'];
718
+ $upload_path = $_FILES[ $var ]['tmp_name'];
719
+ $upload_size = $_FILES[ $var ]['size'];
720
+ $upload_type = $_FILES[ $var ]['type'];
721
+ $upload_name = $_FILES[ $var ]['name'];
722
+ } else {
723
+ $upload_err = $_FILES[ $var ]['error'][0];
724
+ $upload_path = $_FILES[ $var ]['tmp_name'][0];
725
+ $upload_size = $_FILES[ $var ]['size'][0];
726
+ $upload_type = $_FILES[ $var ]['type'][0];
727
+ $upload_name = $_FILES[ $var ]['name'][0];
728
+ }
729
+ $info = getimagesize( $upload_path );
730
+ if ( $info ) {
731
+ if ( $info['mime'] != $upload_type ) {// in DG_Thumber::getExt() we'll define and set appropriate extension
732
+ DG_Logger::writeLog(
733
+ DG_LogLevel::Warning,
734
+ __( 'File extension doesn\'t match the MIME type of the image: ', 'document-gallery' ) .
735
+ $upload_name . ' - ' . $info['mime'] );
736
+ }
737
+ if ( $upload_size > wp_max_upload_size() ) {
738
+ DG_Logger::writeLog(
739
+ DG_LogLevel::Warning,
740
+ __( 'Uploaded file size exceeds the allowable limit: ', 'document-gallery' ) .
741
+ $upload_name . ' - ' . $upload_size . 'b' );
742
+
743
+ return false;
744
+ }
745
+ } else {
746
+ DG_Logger::writeLog(
747
+ DG_LogLevel::Warning,
748
+ __( 'Uploaded file is not an image: ', 'document-gallery' ) .
749
+ $upload_name );
750
+
751
+ return false;
752
+ }
753
+ if ( $upload_err == UPLOAD_ERR_OK && $upload_size > 0 ) {
754
+ $temp_file = $upload_path;
755
+ } else {
756
+ DG_Logger::writeLog(
757
+ DG_LogLevel::Error,
758
+ __( 'Failed to get uploaded file: ', 'document-gallery' ) .
759
+ $upload_err );
760
+
761
+ return false;
762
+ }
763
+
764
+ return $temp_file;
765
+ }
766
+
767
+ /**
768
+ * Validates logging settings, sanitizing any invalid options.
769
+ *
770
+ * @param array $values User-submitted new options.
771
+ *
772
+ * @return array Sanitized new options.
773
+ */
774
+ private static function validateLoggingSettings( $values ) {
775
+ global $dg_options;
776
+ if ( isset( $values['clearLog'] ) ) {
777
+ DG_Logger::clearLog();
778
+ }
779
+
780
+ return $dg_options;
781
+ }
782
+
783
+ /**
784
+ * Validates advanced settings, sanitizing any invalid options.
785
+ *
786
+ * @param array $values User-submitted new options.
787
+ *
788
+ * @return array Sanitized new options.
789
+ */
790
+ private static function validateAdvancedSettings( $values ) {
791
+ global $dg_options;
792
+ $ret = $dg_options;
793
+
794
+ // handle setting the Ghostscript path
795
+ if ( isset( $values['gs'] ) &&
796
+ 0 != strcmp( $values['gs'], $ret['thumber']['gs'] )
797
+ ) {
798
+ if ( false === strpos( $values['gs'], ';' ) ) {
799
+ $ret['thumber']['gs'] = $values['gs'];
800
+ } else {
801
+ add_settings_error( DG_OPTION_NAME, 'thumber-gs',
802
+ __( 'Invalid Ghostscript path given: ', 'document-gallery' ) . $values['gs'] );
803
+ }
804
+ }
805
+
806
+ // handle setting timeout
807
+ if ( isset( $values['timeout'] ) ) {
808
+ $timeout = (int) $values['timeout'];
809
+ if ( $timeout > 0 ) {
810
+ $ret['thumber']['timeout'] = $timeout;
811
+ } else {
812
+ add_settings_error( DG_OPTION_NAME, 'thumber-timeout',
813
+ __( 'Invalid timeout given: ', 'document-gallery' ) . $values['timeout'] );
814
+ }
815
+ }
816
+
817
+ // validation checkbox
818
+ $ret['validation'] = isset( $values['validation'] );
819
+
820
+ // logging settings
821
+ $ret['logging']['enabled'] = isset( $values['logging_enabled'] );
822
+ if ( isset( $values['logging_purge_interval'] ) ) {
823
+ $purge_interval = (int) $values['logging_purge_interval'];
824
+ if ( $purge_interval >= 0 ) {
825
+ $ret['logging']['purge_interval'] = $purge_interval;
826
+ } else {
827
+ add_settings_error( DG_OPTION_NAME, 'thumber-logging-purge-interval',
828
+ __( 'Invalid logging purge interval given: ', 'document-gallery' ) . $values['logging_purge_interval'] );
829
+ }
830
+ }
831
+
832
+ return $ret;
833
+ }
834
+
835
+ /**
836
+ * @return bool Whether to register settings.
837
+ */
838
+ public static function doRegisterSettings() {
839
+ if ( ! is_multisite() ) {
840
+ $script = ! empty( $GLOBALS['pagenow'] ) ? $GLOBALS['pagenow'] : null;
841
+ } else {
842
+ $script = parse_url( $_SERVER['REQUEST_URI'] );
843
+ $script = basename( $script['path'] );
844
+ }
845
+
846
+ return ! empty( $script ) && ( 'options-general.php' === $script || 'options.php' === $script );
847
+ }
848
+
849
+ /**
850
+ * Render the Default Settings section.
851
+ */
852
+ public static function renderDefaultSettingsSection() { ?>
853
+ <p><?php _e( 'The following values will be used by default in the shortcode. You can still manually set each of these values in each individual shortcode.', 'document-gallery' ); ?></p>
854
+ <?php }
855
+
856
+ /**
857
+ * Render the Thumber section.
858
+ */
859
+ public static function renderThumberSection() { ?>
860
+ <p><?php _e( 'Select which tools to use when generating thumbnails.', 'document-gallery' ); ?></p>
861
+ <?php }
862
+
863
+ /**
864
+ * Renders a text field for use when modifying the CSS to be printed in addition to the default CSS.
865
+ */
866
+ public static function renderCssSection() {
867
+ global $dg_options; ?>
868
+ <p><?php printf(
869
+ __( 'Enter custom CSS styling for use with document galleries. To see which ids and classes you can style, take a look at <a href="%s" target="_blank">style.css</a>.' ),
870
+ DG_URL . 'assets/css/style.css' ); ?></p>
871
+ <table class="form-table">
872
+ <tbody>
873
+ <tr valign="top">
874
+ <td>
875
+ <textarea name="<?php echo DG_OPTION_NAME; ?>[css]" rows="10" cols="50"
876
+ class="large-text code"><?php echo $dg_options['css']['text']; ?></textarea>
877
+ </td>
878
+ </tr>
879
+ </tbody>
880
+ </table>
881
+ <?php }
882
+
883
+ /**
884
+ * Render the Thumber Advanced section.
885
+ */
886
+ public static function renderAdvancedSection() {
887
+ include_once DG_PATH . 'inc/class-thumber.php'; ?>
888
+ <p><?php _e( 'Unless you <em>really</em> know what you\'re doing, you should not touch these values.', 'document-gallery' ); ?></p>
889
+ <?php if ( ! DG_Thumber::isExecAvailable() ) : ?>
890
+ <p>
891
+ <em><?php _e( 'NOTE: <code>exec()</code> is not accessible. Ghostscript will not function.', 'document-gallery' ); ?></em>
892
+ </p>
893
+ <?php endif; ?>
894
+ <?php }
895
+
896
+ /**
897
+ * Renders a readonly textfield containing a dump of current DG options.
898
+ */
899
+ public static function renderOptionsDumpSection() {
900
+ global $dg_options; ?>
901
+ <p><?php
902
+ _e( 'The following <em>readonly text</em> should be provided when <a href="http://wordpress.org/support/plugin/document-gallery" target="_blank">reporting a bug</a>:', 'documet-gallery' );
903
+ ?></p>
904
+ <table class="form-table">
905
+ <tbody>
906
+ <tr valign="top">
907
+ <td>
908
+ <textarea readonly="true" rows="10" cols="50" id="options-dump"
909
+ class="large-text code"><?php print_r( $dg_options ); ?></textarea>
910
+ </td>
911
+ </tr>
912
+ </tbody>
913
+ </table>
914
+ <?php }
915
+
916
+ /**
917
+ * Render the Thumbnail table.
918
+ */
919
+ public static function renderThumbnailSection() {
920
+ include_once DG_PATH . 'inc/class-thumber.php';
921
+ $options = DG_Thumber::getOptions();
922
+
923
+ $URL_params = array( 'page' => DG_OPTION_NAME, 'tab' => 'Thumbnail' );
924
+ $att_ids = array();
925
+
926
+ if ( isset( $_REQUEST['orderby'] ) && in_array( strtolower( $_REQUEST['orderby'] ), array(
927
+ 'title',
928
+ 'date'
929
+ ) )
930
+ ) {
931
+ $orderby = strtolower( $_REQUEST['orderby'] );
932
+ $URL_params['orderby'] = $orderby;
933
+
934
+ switch ( $orderby ) {
935
+ case 'date':
936
+ foreach ( $options['thumbs'] as $key => $node ) {
937
+ $keyArray[ $key ] = $node['timestamp'];
938
+ $options['thumbs'][ $key ]['thumb_id'] = $att_ids[] = $key;
939
+ }
940
+ break;
941
+
942
+ case 'title':
943
+ foreach ( $options['thumbs'] as $key => $node ) {
944
+ $keyArray[ $key ] = basename( $node['thumb_path'] );
945
+ $options['thumbs'][ $key ]['thumb_id'] = $att_ids[] = $key;
946
+ }
947
+ break;
948
+ }
949
+
950
+ $order = strtolower( $_REQUEST['order'] );
951
+ if ( ! isset( $_REQUEST['order'] ) || ! in_array( $order, array( 'asc', 'desc' ) ) ) {
952
+ $order = 'asc';
953
+ }
954
+ $URL_params['order'] = $order;
955
+
956
+ if ( $order == 'asc' ) {
957
+ array_multisort( $keyArray, SORT_ASC, $options['thumbs'] );
958
+ } else {
959
+ array_multisort( $keyArray, SORT_DESC, $options['thumbs'] );
960
+ }
961
+ } else {
962
+ $orderby = '';
963
+ foreach ( $options['thumbs'] as $key => $node ) {
964
+ $options['thumbs'][ $key ]['thumb_id'] = $att_ids[] = $key;
965
+ }
966
+ }
967
+
968
+ static $limit_options = array( 10, 25, 75 );
969
+ if ( ! isset( $_REQUEST['limit'] ) || ! in_array( intval( $_REQUEST['limit'] ), $limit_options ) ) {
970
+ $limit = $limit_options[0];
971
+ } else {
972
+ $limit = intval( $_REQUEST['limit'] );
973
+ }
974
+
975
+ $URL_params['limit'] = $limit;
976
+ $select_limit = '';
977
+ foreach ( $limit_options as $l_o ) {
978
+ $select_limit .= '<option value="' . $l_o . '"' . selected( $limit, $l_o, false ) . '>' . $l_o . '</option>' . PHP_EOL;
979
+ }
980
+ $thumbs_number = count( $options['thumbs'] );
981
+ $lastsheet = ceil( $thumbs_number / $limit );
982
+ $sheet = isset( $_REQUEST['sheet'] ) ? intval( $_REQUEST['sheet'] ) : 1;
983
+ if ( $sheet <= 0 || $sheet > $lastsheet ) {
984
+ $sheet = 1;
985
+ }
986
+
987
+ $offset = ( $sheet - 1 ) * $limit;
988
+
989
+ $att_ids = array_slice( $att_ids, $offset, $limit );
990
+
991
+ // https://core.trac.wordpress.org/ticket/12212
992
+ $atts = array();
993
+ if ( ! empty( $att_ids ) ) {
994
+ $atts = get_posts(
995
+ array(
996
+ 'post_type' => 'any',
997
+ 'post_status' => 'any',
998
+ 'numberposts' => - 1,
999
+ 'post__in' => $att_ids,
1000
+ 'orderby' => 'post__in'
1001
+ ) );
1002
+ }
1003
+
1004
+ $titles = array();
1005
+ $contents = array();
1006
+ foreach ( $atts as $att ) {
1007
+ $path_parts = pathinfo( $att->guid );
1008
+ $titles[ $att->ID ] = $att->post_title;
1009
+ $types[ $att->ID ] = $path_parts['extension'];
1010
+ $contents[ $att->ID ] = $att->post_content;
1011
+ }
1012
+ unset( $atts );
1013
+
1014
+ $thead = '<tr>' .
1015
+ '<th scope="col" class="manage-column column-cb check-column">' .
1016
+ '<label class="screen-reader-text" for="cb-select-all-%1$d">' . __( 'Select All', 'document-gallery' ) . '</label>' .
1017
+ '<input id="cb-select-all-%1$d" type="checkbox">' .
1018
+ '</th>' .
1019
+ '<th scope="col" class="manage-column column-icon">' . __( 'Thumbnail', 'document-gallery' ) . '</th>' .
1020
+ '<th scope="col" class="manage-column column-title ' . ( ( $orderby != 'title' ) ? 'sortable desc' : 'sorted ' . $order ) . '"><a href="?' . http_build_query( array_merge( $URL_params, array(
1021
+ 'orderby' => 'title',
1022
+ 'order' => ( ( $orderby != 'title' ) ? 'asc' : ( ( $order == 'asc' ) ? 'desc' : 'asc' ) )
1023
+ ) ) ) . '"><span>' . __( 'File name', 'document-gallery' ) . '</span><span class="sorting-indicator"></span></th>' .
1024
+ '<th scope="col" class="manage-column column-description">' . __( 'Description', 'document-gallery' ) . '</th>' .
1025
+ '<th scope="col" class="manage-column column-thumbupload"></th>' .
1026
+ '<th scope="col" class="manage-column column-date ' . ( ( $orderby != 'date' ) ? 'sortable asc' : 'sorted ' . $order ) . '"><a href="?' . http_build_query( array_merge( $URL_params, array(
1027
+ 'orderby' => 'date',
1028
+ 'order' => ( ( $orderby != 'date' ) ? 'desc' : ( ( $order == 'asc' ) ? 'desc' : 'asc' ) )
1029
+ ) ) ) . '"><span>' . __( 'Date', 'document-gallery' ) . '</span><span class="sorting-indicator"></span></th>' .
1030
+ '</tr>';
1031
+
1032
+ $pagination = '<div class="alignleft bulkactions"><button class="button action deleteSelected">' . __( 'Delete Selected', 'document-gallery' ) . '</button></div><div class="tablenav-pages">' .
1033
+ '<span class="displaying-num">' .
1034
+ $thumbs_number . ' ' . _n( 'item', 'items', $thumbs_number ) .
1035
+ '</span>' . ( $lastsheet > 1 ?
1036
+ '<span class="pagination-links">' .
1037
+ '<a class="first-page' . ( $sheet == 1 ? ' disabled' : '' ) . '" title="' . __( 'Go to the first page', 'document-gallery' ) . '"' . ( $sheet == 1 ? '' : ' href="?' . http_build_query( $URL_params ) . '"' ) . '>«</a>' .
1038
+ '<a class="prev-page' . ( $sheet == 1 ? ' disabled' : '' ) . '" title="' . __( 'Go to the previous page', 'document-gallery' ) . '"' . ( $sheet == 1 ? '' : ' href="?' . http_build_query( array_merge( $URL_params, array( 'sheet' => $sheet - 1 ) ) ) . '"' ) . '>‹</a>' .
1039
+ '<span class="paging-input">' .
1040
+ '<input class="current-page" title="' . __( 'Current page', 'document-gallery' ) . '" type="text" name="paged" value="' . $sheet . '" size="' . strlen( $sheet ) . '" maxlength="' . strlen( $sheet ) . '"> ' . __( 'of', 'document-gallery' ) . ' <span class="total-pages">' . $lastsheet . '</span></span>' .
1041
+ '<a class="next-page' . ( $sheet == $lastsheet ? ' disabled' : '' ) . '" title="' . __( 'Go to the next page', 'document-gallery' ) . '"' . ( $sheet == $lastsheet ? '' : ' href="?' . http_build_query( array_merge( $URL_params, array( 'sheet' => $sheet + 1 ) ) ) . '"' ) . '>›</a>' .
1042
+ '<a class="last-page' . ( $sheet == $lastsheet ? ' disabled' : '' ) . '" title="' . __( 'Go to the last page', 'document-gallery' ) . '"' . ( $sheet == $lastsheet ? '' : ' href="?' . http_build_query( array_merge( $URL_params, array( 'sheet' => $lastsheet ) ) ) . '"' ) . '>»</a>' .
1043
+ '</span>' : ' <b>|</b> ' ) .
1044
+ '<span class="displaying-num"><select dir="rtl" class="limit_per_page">' . $select_limit . '</select> ' . __( 'items per page', 'document-gallery' ) . '</span>' .
1045
+ '</div>' .
1046
+ '<br class="clear" />';
1047
+ ?>
1048
+
1049
+ <script type="text/javascript">
1050
+ var URL_params = <?php echo DG_Util::jsonEncode($URL_params); ?>;
1051
+ </script>
1052
+ <div class="thumbs-list-wrapper">
1053
+ <div>
1054
+ <div class="tablenav top"><?php echo $pagination; ?></div>
1055
+ <table id="ThumbsTable" class="wp-list-table widefat fixed media"
1056
+ cellpadding="0" cellspacing="0">
1057
+ <thead>
1058
+ <?php printf( $thead, 1 ); ?>
1059
+ </thead>
1060
+ <tfoot>
1061
+ <?php printf( $thead, 2 ); ?>
1062
+ </tfoot>
1063
+ <tbody><?php
1064
+ $i = 0;
1065
+ foreach ( $options['thumbs'] as $v ) {
1066
+ if ( $i < $offset ) {
1067
+ $i ++;
1068
+ continue;
1069
+ }
1070
+ if ( ++ $i > $offset + $limit ) {
1071
+ break;
1072
+ }
1073
+
1074
+ $icon = isset( $v['thumb_url'] ) ? $v['thumb_url'] : DG_Thumber::getDefaultThumbnail( $v['thumb_id'] );
1075
+ $title = isset( $titles[ $v['thumb_id'] ] ) ? $titles[ $v['thumb_id'] ] : '';
1076
+ $type = $types[ $v['thumb_id'] ];
1077
+ $description = $contents[ $v['thumb_id'] ];
1078
+ $date = DocumentGallery::localDateTimeFromTimestamp( $v['timestamp'] );
1079
+
1080
+ echo '<tr data-entry="' . $v['thumb_id'] . '"><td scope="row" class="check-column"><input type="checkbox" class="cb-ids" name="' . DG_OPTION_NAME . '[ids][]" value="' .
1081
+ $v['thumb_id'] . '"></td><td class="column-icon media-icon"><img src="' .
1082
+ $icon . '" />' . '</td><td class="title column-title">' .
1083
+ ( $title ? '<strong><a href="' . home_url( '/?attachment_id=' . $v['thumb_id'] ) . '" target="_blank" title="' . sprintf( __( "View '%s' attachment page", 'document-gallery' ), $title ) . '"><span class="editable-title">' . $title . '</span> <sup>' . $type . '</sup></a></strong>' : __( 'Attachment not found', 'document-gallery' ) ) .
1084
+ '<span class="dashicons dashicons-edit"></span><span class="edit-controls"><span class="dashicons dashicons-yes"></span> <span class="dashicons dashicons-no"></span></span></td><td class="column-description"><div class="editable-description">' . $description . '</div><span class="dashicons dashicons-edit"></span><span class="edit-controls"><span class="dashicons dashicons-yes"></span> <span class="dashicons dashicons-no"></span><span class="dashicons dashicons-update"></span></span>' .
1085
+ '</td><td class="column-thumbupload">' .
1086
+ '<span class="manual-download">' .
1087
+ '<span class="dashicons dashicons-upload"></span>' .
1088
+ '<span class="html5dndmarker">Drop file here<span> or </span></span>' .
1089
+ '<span class="buttons-area">' .
1090
+ '<input id="upload-button' . $v['thumb_id'] . '" type="file" />' .
1091
+ '<input id="trigger-button' . $v['thumb_id'] . '" type="button" value="Select File" class="button" />' .
1092
+ '</span>' .
1093
+ '</span>' .
1094
+ '<div class="progress animate invis"><span><span></span></span></div>' .
1095
+ '</td><td class="date column-date">' . $date . '</td></tr>' . PHP_EOL;
1096
+ } ?>
1097
+ </tbody>
1098
+ </table>
1099
+ <div class="tablenav bottom"><?php echo $pagination; ?></div>
1100
+ </div>
1101
+ </div>
1102
+ <?php }
1103
+
1104
+ /**
1105
+ * Adds meta box to the attchements' edit pages.
1106
+ */
1107
+ public static function addMetaBox() {
1108
+ $screens = array( 'attachment' );
1109
+ foreach ( $screens as $screen ) {
1110
+ add_meta_box(
1111
+ DG_OPTION_NAME . '_gen_box',
1112
+ __( '<b>Thumbnail</b> for <i><b>Document Gallery</b></i>', 'document-gallery' ),
1113
+ array( __CLASS__, 'renderMetaBox' ),
1114
+ $screen,
1115
+ 'normal'
1116
+ );
1117
+ }
1118
+ add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueueScriptsAndStyles' ) );
1119
+ }
1120
+
1121
+ /**
1122
+ * Render a Meta Box.
1123
+ */
1124
+ public static function renderMetaBox( $post ) {
1125
+ global $dg_options;
1126
+ wp_nonce_field( DG_OPTION_NAME . '_meta_box', DG_OPTION_NAME . '_meta_box_nonce' );
1127
+ $ID = $post->ID;
1128
+ $icon = isset( $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'] ) ? $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'] : DG_Thumber::getDefaultThumbnail( $ID );
1129
+
1130
+ echo '<table id="ThumbsTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">' .
1131
+ '<tbody><tr data-entry="' . $ID . '"><td class="column-icon media-icon"><img src="' .
1132
+ $icon . '" />' . '</td><td class="column-thumbupload">' .
1133
+ '<span class="manual-download">' .
1134
+ '<span class="dashicons dashicons-upload"></span>' .
1135
+ '<span class="html5dndmarker">Drop file here<span> or </span></span>' .
1136
+ '<span class="buttons-area">' .
1137
+ '<input id="upload-button' . $ID . '" type="file" />' .
1138
+ '<input id="trigger-button' . $ID . '" type="button" value="Select File" class="button" />' .
1139
+ '</span>' .
1140
+ '</span>' .
1141
+ '</td></tr></tbody></table>' .
1142
+ ( 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;
1143
+ }
1144
+
1145
+ /**
1146
+ * Save a Meta Box.
1147
+ */
1148
+ public static function saveMetaBox( $post_id ) {
1149
+ // Check if our nonce is set.
1150
+ // Verify that the nonce is valid.
1151
+ // If this is an autosave, our form has not been submitted, so we don't want to do anything.
1152
+ if ( ! isset( $_POST[ DG_OPTION_NAME . '_meta_box_nonce' ] ) || ! wp_verify_nonce( $_POST[ DG_OPTION_NAME . '_meta_box_nonce' ], DG_OPTION_NAME . '_meta_box' ) || ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
1153
+ return;
1154
+ }
1155
+
1156
+ global $dg_options;
1157
+ $responseArr = array( 'result' => false );
1158
+ if ( isset( $_POST[ DG_OPTION_NAME ]['entry'] ) ) {
1159
+ $ID = intval( $_POST[ DG_OPTION_NAME ]['entry'] );
1160
+ } else {
1161
+ $ID = - 1;
1162
+ }
1163
+ if ( isset( $_POST[ DG_OPTION_NAME ]['upload'] ) && isset( $_FILES['file'] ) && isset( $dg_options['thumber']['thumbs'][ $ID ] ) ) {
1164
+ $old_path = $dg_options['thumber']['thumbs'][ $ID ]['thumb_path'];
1165
+ $uploaded_filename = self::validateUploadedFile();
1166
+ if ( $uploaded_filename && DG_Thumber::setThumbnail( $ID, $uploaded_filename ) ) {
1167
+ if ( $dg_options['thumber']['thumbs'][ $ID ]['thumb_path'] !== $old_path ) {
1168
+ @unlink( $old_path );
1169
+ }
1170
+ $responseArr['result'] = true;
1171
+ $responseArr['url'] = $dg_options['thumber']['thumbs'][ $ID ]['thumb_url'];
1172
+ }
1173
+ }
1174
+ if ( isset( $_POST[ DG_OPTION_NAME ]['ajax'] ) ) {
1175
+ echo DG_Util::jsonEncode( $responseArr );
1176
+ wp_die();
1177
+ }
1178
+ }
1179
+
1180
+ /**
1181
+ * Render the Logging table.
1182
+ */
1183
+ public static function renderLoggingSection() {
1184
+ $log_list = DG_Logger::readLog();
1185
+ if ( $log_list ) {
1186
+ $levels = array_map( array( __CLASS__, 'getLogLabelSpan' ), array_keys( DG_LogLevel::getLogLevels() ) );
1187
+
1188
+ $fmt =
1189
+ '<tr>' .
1190
+ '<th scope="col" class="manage-column column-date sorted desc"><a href="javascript:void(0);">' .
1191
+ '<span>%s</span><span class="sorting-indicator"></span></a>' .
1192
+ '</th>' .
1193
+ '<th scope="col" class="manage-column column-level"><span>%s</span></th>' .
1194
+ '<th scope="col" class="manage-column column-message"><span>%s</span></th>' .
1195
+ '</tr>';
1196
+
1197
+ $thead = sprintf( $fmt,
1198
+ __( 'Date', 'document-gallery' ),
1199
+ __( 'Level', 'document-gallery' ),
1200
+ __( 'Message', 'document-gallery' ) );
1201
+
1202
+ ?>
1203
+ <div class="log-list-wrapper">
1204
+ <div>
1205
+ <div class="tablenav top">
1206
+ <div class="alignleft bulkactions">
1207
+ <button class="action expandAll">
1208
+ <?php echo __( 'Expand All', 'document-gallery' ); ?>
1209
+ </button>
1210
+ <button class="action collapseAll">
1211
+ <?php echo __( 'Collapse All', 'document-gallery' ); ?>
1212
+ </button>
1213
+ </div>
1214
+ <div class="levelSelector">
1215
+ <input type="checkbox" id="allLevels" name="lswitch" value="all" checked/>
1216
+ <label for="allLevels" class="allLevels">ALL</label>
1217
+ <?php
1218
+ foreach ( array_keys( DG_LogLevel::getLogLevels() ) as $k ) { ?>
1219
+ <?php
1220
+ $lower = strtolower( $k );
1221
+ $upper = strtoupper( $k );
1222
+ ?>
1223
+ <input type="checkbox" id="<?php echo $lower; ?>Level" name="lswitch"
1224
+ value="<?php echo $lower; ?>" checked/>
1225
+ <label for="<?php echo $lower; ?>Level"
1226
+ class="<?php echo $lower; ?>Level"><?php echo $upper; ?></label>
1227
+ <?php }
1228
+ ?>
1229
+ </div>
1230
+ </div>
1231
+ <table id="LogTable" class="wp-list-table widefat fixed media" cellpadding="0" cellspacing="0">
1232
+ <thead>
1233
+ <?php echo $thead; ?>
1234
+ </thead>
1235
+ <tfoot>
1236
+ <?php echo $thead; ?>
1237
+ </tfoot>
1238
+ <tbody><?php
1239
+ for ( $i = count( $log_list ); $i > 0; $i -- ) {
1240
+ $log_entry = $log_list[ $i - 1 ];
1241
+ $date = DocumentGallery::localDateTimeFromTimestamp( $log_entry[0] );
1242
+
1243
+ // convert attachment names to links
1244
+ $log_entry[2] = preg_replace( '/[ ^](attachment #)(\d+)[., ]/i', ' <a href="' . home_url() . '/?attachment_id=\2" target="_blank">\1<strong>\2</strong></a> ', $log_entry[2] );
1245
+
1246
+ // bold the place where log entry was submitted
1247
+ $log_entry[2] = preg_replace( '/^(\(\w+::\w+\)) /', '<strong>\1</strong> ', $log_entry[2] );
1248
+
1249
+ // italicize any function references within log entry
1250
+ $log_entry[2] = preg_replace( '/(\(?\w+::\w+\)?)/m', '<i>\1</i>', $log_entry[2] );
1251
+
1252
+ echo '<tr><td class="date column-date" data-sort-value="' . $log_entry[0] . '"><span class="logLabel date">' . $date . '</span></td>' .
1253
+ '<td class="column-level">' . $levels[ $log_entry[1] ] . '</td>' .
1254
+ '<td class="column-entry">' . ( empty( $log_entry[3] ) ? '<pre>' . $log_entry[2] . '</pre>' : '<div class="expander" title="Click to Expand"><pre>' . $log_entry[2] . '</pre><div><span class="dashicons dashicons-arrow-down-alt2"></span></div></div><div class="spoiler-body"><pre>' . $log_entry[3] . '</pre></div>' ) . '</td>' .
1255
+ '</tr>' . PHP_EOL;
1256
+ } ?>
1257
+ </tbody>
1258
+ </table>
1259
+ <div class="tablenav bottom">
1260
+ <div class="alignright bulkactions">
1261
+ <button class="button action clearLog" name='<?php echo DG_OPTION_NAME; ?>[clearLog]'
1262
+ value='true'>
1263
+ <?php echo __( 'Clear Log', 'document-gallery' ); ?>
1264
+ </button>
1265
+ </div>
1266
+ </div>
1267
+ </div>
1268
+ </div>
1269
+ <?php } else {
1270
+ echo '<div class="noLog">' . __( 'There are no log entries at this time.', 'document-gallery' ) . '<br />' . __( 'For Your information:', 'document-gallery' ) . ' <strong><i>' . __( 'Logging', 'document-gallery' ) . '</i></strong> ' . ( DG_Logger::logEnabled() ? '<span class="loggingON">' . __( 'is turned ON', 'document-gallery' ) . '!</span>' : '<span class="loggingOFF">' . __( 'is turned OFF', 'document-gallery' ) . '!</span>' ) . '</div>';
1271
+ }
1272
+ }
1273
+
1274
+ /**
1275
+ * Takes label name and returns SPAN tag.
1276
+ *
1277
+ * @param string $e label name.
1278
+ *
1279
+ * @return string SPAN tag
1280
+ */
1281
+ private static function getLogLabelSpan( $e ) {
1282
+ return '<span class="logLabel ' . strtolower( $e ) . '">' . strtoupper( $e ) . '</span>';
1283
+ }
1284
+
1285
+ /**
1286
+ * Render a checkbox field.
1287
+ *
1288
+ * @param array $args
1289
+ */
1290
+ public static function renderCheckboxField( $args ) {
1291
+ $args['disabled'] = isset( $args['disabled'] ) ? $args['disabled'] : false;
1292
+ printf( '<label><input type="checkbox" value="1" name="%1$s[%2$s]" id="%3$s" %4$s %5$s/> %6$s</label>',
1293
+ $args['option_name'],
1294
+ $args['name'],
1295
+ $args['label_for'],
1296
+ checked( $args['value'], 1, false ),
1297
+ disabled( $args['disabled'], true, false ),
1298
+ $args['description'] );
1299
+ }
1300
+
1301
+ /**
1302
+ * Render a text field.
1303
+ *
1304
+ * @param array $args
1305
+ */
1306
+ public static function renderTextField( $args ) {
1307
+ printf( '<input type="%1$s" value="%2$s" name="%3$s[%4$s]" id="%5$s" /> %6$s',
1308
+ isset( $args['type'] ) ? $args['type'] : 'text',
1309
+ $args['value'],
1310
+ $args['option_name'],
1311
+ $args['name'],
1312
+ $args['label_for'],
1313
+ $args['description'] );
1314
+ }
1315
+
1316
+ /**
1317
+ * Accepts a two-dimensional array where each inner array consists of valid arguments for renderTextField.
1318
+ *
1319
+ * @param array $args
1320
+ */
1321
+ public static function renderMultiTextField( $args ) {
1322
+ foreach ( $args as $arg ) {
1323
+ self::renderTextField( $arg );
1324
+ }
1325
+ }
1326
+
1327
+ /**
1328
+ * Render a select field.
1329
+ *
1330
+ * @param array $args
1331
+ */
1332
+ public static function renderSelectField( $args ) {
1333
+ printf( '<select name="%1$s[%2$s]" id="%3$s">',
1334
+ $args['option_name'],
1335
+ $args['name'],
1336
+ $args['label_for'] );
1337
+
1338
+ foreach ( $args['options'] as $val ) {
1339
+ printf( '<option value="%1$s" %2$s>%3$s</option>',
1340
+ $val,
1341
+ selected( $val, $args['value'], false ),
1342
+ $val,
1343
+ $args['description'] );
1344
+ }
1345
+
1346
+ print '</select> ' . $args['description'];
1347
+ }
1348
+
1349
+ /**
1350
+ * Wraps the PHP exit language construct.
1351
+ */
1352
+ public static function _exit() {
1353
+ exit;
1354
+ }
1355
+
1356
+ /**
1357
+ * Blocks instantiation. All functions are static.
1358
+ */
1359
+ private function __construct() {
1360
+
1361
+ }
1362
  }
admin/media-manager-template.php CHANGED
@@ -1,84 +1,91 @@
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'); ?></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>
20
 
21
- <label class="setting">
22
- <table><tr>
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>
39
- </label>
40
 
41
- <label class="setting">
42
- <table><tr>
43
- <td><span><?php _e('Include document descriptions', 'document-gallery'); ?></span></td>
44
- <td><input type="checkbox" data-setting="descriptions" <# if ( documentGalleryDefaults.descriptions ) { #>checked="checked"<# } #>/></td>
45
- </tr></table>
46
- </label>
47
 
48
- <label class="setting">
49
- <table><tr>
50
- <td><span><?php _e('Use auto-generated document thumbnails', 'document-gallery'); ?></span></td>
51
- <td><input type="checkbox" data-setting="fancy" <# if ( documentGalleryDefaults.fancy ) { #>checked="checked"<# } #>/></td>
52
- </tr></table>
53
- </label>
54
 
55
- <label class="setting">
56
- <table><tr>
57
- <td><span><?php _e('Which field to order documents by', 'document-gallery'); ?></span></td>
58
- <td><select class="DGorderby" name="DGorderby" data-setting="DGorderby">
59
- <?php foreach ( DG_Gallery::getOrderbyOptions() as $i ) : ?>
60
- <option value="<?php echo esc_attr( $i ); ?>" <#
61
- if ( '<?php echo $i ?>' == documentGalleryDefaults.orderby ) { #>selected="selected"<# }
62
- #>>
63
- <?php echo esc_html( $i ); ?>
64
- </option>
65
- <?php endforeach; ?>
66
- </select></td>
67
- </tr></table>
68
- </label>
69
 
70
- <label class="setting">
71
- <table><tr>
72
- <td><span><?php _e('Ascending or descending sorting of documents', 'document-gallery'); ?></span></td>
73
- <td><select class="order" name="order" data-setting="order">
74
- <?php foreach ( DG_Gallery::getOrderOptions() as $i ) : ?>
75
- <option value="<?php echo esc_attr( $i ); ?>" <#
76
- if ( '<?php echo $i ?>' == documentGalleryDefaults.order ) { #>selected="selected"<# }
77
- #>>
78
- <?php echo esc_html( $i ); ?>
79
- </option>
80
- <?php endforeach; ?>
81
- </select></td>
82
- </tr></table>
83
- </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
84
  </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-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>
20
 
21
+ <label class="setting">
22
+ <table><tr>
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>
39
+ </label>
40
 
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>
75
+ </label>
76
+
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>
assets/css/admin.css CHANGED
@@ -1,358 +1,431 @@
1
  @media screen and (max-width: 782px) {
2
- .column-title {
3
- width: 150px;
4
- }
5
- .column-date {
6
- display: table-cell !important;
7
- width: auto !important;
8
- }
9
- .nav-tab-wrapper {
10
- padding-left: 5px !important;
11
- padding-right: 5px !important;
12
- }
13
- .top .tablenav-pages {
14
- display: none;
15
- }
16
- .bottom .tablenav-pages {
17
- width: auto !important;
18
- margin-top: 0px !important;
19
- }
20
- .bottom .displaying-num {
21
- position: inherit !important;
22
- }
 
 
 
 
 
23
  }
 
24
  @media screen and (max-width: 979px) {
25
- .column-thumbupload {
26
- display: none;
27
- }
28
- .thumbs-list-wrapper {
29
- margin-top: 0px !important;
30
- }
31
- }
32
-
33
- div.thumbs-list-wrapper, div.log-list-wrapper{
34
- text-align:center;
35
- margin-top: 1.5em;
36
- }
37
- div.thumbs-list-wrapper>div, div.log-list-wrapper>div{
38
- margin: 0 auto;
39
- display: inline-block;
40
- }
41
- #ThumbsTable, #LogTable{
42
- border: none;
43
- box-shadow: none;
44
- -webkit-box-shadow: none;
45
- -moz-box-shadow: none;
46
- border-radius: 10px;
47
- -webkit-border-radius: 10px;
48
- -moz-border-radius: 10px;
49
- background: #B9C9FE;
50
- color: #039;
51
- width: 100%;
52
- margin: 10px auto;
53
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  #ThumbsTable tbody, #LogTable tbody {
55
- background: #e8edff;
56
- color: #669;
57
  }
58
- #ThumbsTable>tbody>tr:hover, #LogTable>tbody>tr:hover {
59
- background: #d0dafd;
 
60
  }
61
- #ThumbsTable>tbody>tr:not(:last-child) td, #LogTable>tbody>tr:not(:last-child) td {
62
- border-bottom-color: #b9c9fe;
63
- border-bottom-style: solid;
64
- border-bottom-width: 1px;
65
  }
66
- #ThumbsTable td, #ThumbsTable th, #LogTable td, #LogTable th{
67
- text-align: center;
68
- vertical-align: middle;
69
- margin: 0;
70
- padding: 4px;
 
71
  }
 
72
  #LogTable td {
73
- text-align: left;
74
  }
 
75
  td.title.column-title, .column-thumbupload {
76
- text-align: left !important;
77
  }
78
- #ThumbsTable img{
79
- display: block;
80
- margin: 5px auto;
 
81
  }
 
82
  #LogTable td > pre, .spoiler-body > pre, .expander pre, .collapser pre {
83
- margin: 0;
84
- display: inline-block;
85
- white-space: pre-line;
86
  }
87
- tr.selected{
88
- background: #B6ADCE;
 
89
  }
90
- tr.selected:hover{
91
- background: #D8D3E5 !important;
 
92
  }
 
93
  .check-column, .column-icon {
94
- white-space: nowrap;
95
- width: 1%;
96
  }
 
97
  .column-entry, .column-title {
98
- /*width: 100%;*/
99
  }
 
100
  .column-thumbupload {
101
- white-space: nowrap;
102
- width: 35%;
103
  }
 
104
  #document_gallery_gen_box .column-thumbupload {
105
- width: auto;
106
- padding-left: 7em;
107
  }
108
- .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before, .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after{
109
- display: inline-block;
110
- -webkit-font-smoothing: antialiased;
111
- font: normal 20px/1 'dashicons';
112
- vertical-align: text-bottom;
113
- padding-right: 5px;
 
114
  }
115
- .General-tab:before{
116
- content: '\f108';
 
117
  }
118
- .Thumbnail-tab:before{
119
- content: '\f233';
 
120
  }
121
- .Logging-tab:before{
122
- content: '\f163';
 
123
  }
124
- .Advanced-tab:before{
125
- content: '\f332';
 
126
  }
127
- .deleteSelected:before, .clearLog:before{
128
- content: '\f182';
 
129
  }
 
130
  .expandAll:before {
131
- content: '\f211';
132
  }
 
133
  .collapseAll:before {
134
- content: '\f506';
135
  }
 
136
  .expandAll, .collapseAll {
137
- display: none;
138
- }
139
- #ThumbsTable .title a:after, #LogTable>tbody a:after{
140
- content: '\f504';
141
- display: inline-block;
142
- -webkit-font-smoothing: antialiased;
143
- font-family: 'dashicons';
144
- font-size: inherit;
145
- font-style: normal;
146
- font-variant: normal;
147
- font-weight: normal;
148
- line-height: 1;
149
- vertical-align: inherit;
150
- padding-left: 5px;
151
- }
152
- #LogTable>tbody a {
153
- -webkit-transition: none;
154
- transition: none;
155
- text-decoration: none;
156
- outline: 0;
157
- }
158
- #LogTable>tbody pre strong {
159
- font-weight: bolder;
160
- }
161
- #LogTable>tbody a:active, #LogTable>tbody a:hover {
162
- color: #2ea2cc;
163
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  .levelSelector {
165
- float: right;
166
  }
 
167
  .logLabel {
168
- padding: 0 10px;
169
- color: #fff !important;
170
- text-decoration: none;
171
- font-weight: bolder;
172
- border: none !important;
173
- float:left;
174
- margin-left: 1px;
175
- margin-top: 1px;
176
- -webkit-border-radius: 100px;
177
- -moz-border-radius: 100px;
178
- border-radius: 100px;
179
- cursor: context-menu;
180
  }
 
181
  .logLabel.warning {
182
- background: #F89406;
183
  }
 
184
  .logLabel.detail {
185
- background: #3A87AD;
186
  }
 
187
  .logLabel.error {
188
- background: #CC0000;
189
  }
 
190
  .logLabel.date {
191
- background: #999999;
192
- /*white-space: nowrap;*/
193
- font-weight: inherit;
194
  }
195
- .logLabel.date:before{
196
- font-size: inherit;
197
- vertical-align: middle;
198
- padding-bottom: 0.2em;
199
- content: '\f469';
 
200
  }
 
201
  .spoiler-body {
202
- padding: 1px 6px 2px;
203
- display: none;
204
- border-top: 1px solid #C3CBD1;
205
- background: #F5F5F5;
206
  }
 
207
  .column-entry {
208
- text-align: left !important;
209
  }
 
210
  .expander pre, .collapser pre {
211
- vertical-align: middle;
212
- float: left;
213
- white-space: pre-wrap;
214
  }
 
215
  .expander, .collapser {
216
- display: table;
217
- vertical-align: middle;
218
- width: 100%;
219
- cursor: pointer;
220
  }
 
221
  .expander > div, .collapser > div {
222
- display: table-cell;
223
- vertical-align: middle;
224
- height: 100%;
225
  }
 
226
  .dashicons.dashicons-arrow-down-alt2, .dashicons.dashicons-arrow-up-alt2 {
227
- float: right;
228
- padding-right: 15px;
229
  }
 
230
  .levelSelector > *, .dashicons {
231
- -webkit-touch-callout: none;
232
- -webkit-user-select: none;
233
- -khtml-user-select: none;
234
- -moz-user-select: none;
235
- -ms-user-select: none;
236
- user-select: none;
237
  }
 
238
  .levelSelector > input[type=checkbox] {
239
- display:none;
240
  }
 
241
  .levelSelector > input[type=checkbox] + label {
242
- color: #6B6B6B;
243
- font-weight: bolder;
244
- margin: 4px 0px;
245
- overflow: auto;
246
- text-align: center;
247
- padding: 3px 8px;
248
- display: table-cell;
249
- background-color: #f5f5f5;
250
- background-image: -moz-linear-gradient(top,#fff,#e6e6e6);
251
- background-image: -webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));
252
- background-image: -webkit-linear-gradient(top,#fff,#e6e6e6);
253
- background-image: -o-linear-gradient(top,#fff,#e6e6e6);
254
- background-image: linear-gradient(to bottom,#fff,#e6e6e6);
255
- background-repeat: repeat-x;
256
- border: 1px solid #ccc;
257
- border-color: #e6e6e6 #e6e6e6 #bfbfbf;
258
- border-color: rgba(0,0,0,0.1) rgba(0,0,0,0.1) rgba(0,0,0,0.25);
259
- border-bottom-color: #b3b3b3;
260
- filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff',endColorstr='#ffe6e6e6',GradientType=0);
261
- filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
262
- -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
263
- -moz-box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
264
- box-shadow: inset 0 1px 0 rgba(255,255,255,0.2),0 1px 2px rgba(0,0,0,0.05);
265
  }
 
266
  .levelSelector > input[type=checkbox] + label:first-of-type {
267
- border-top-left-radius: 4px;
268
- border-bottom-left-radius: 4px;
269
  }
 
270
  .levelSelector > input[type=checkbox] + label:last-of-type {
271
- border-top-right-radius: 4px;
272
- border-bottom-right-radius: 4px;
273
  }
 
274
  .levelSelector > input[type=checkbox]:checked + label {
275
- background-image: none;
276
- outline: 0;
277
- -webkit-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
278
- -moz-box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
279
- box-shadow: inset 0 2px 4px rgba(0,0,0,0.15),0 1px 2px rgba(0,0,0,0.05);
280
  }
 
281
  .levelSelector > input[type=checkbox] + label.errorLevel {
282
- color: #CC0000;
283
  }
 
284
  .levelSelector > input[type=checkbox]:checked + label.errorLevel {
285
- background-color: #CC0000;
286
- color: #fff;
287
  }
 
288
  .levelSelector > input[type=checkbox] + label.warningLevel {
289
- color: #F89406;
290
  }
 
291
  .levelSelector > input[type=checkbox]:checked + label.warningLevel {
292
- background-color: #F89406;
293
- color: #fff;
294
  }
 
295
  .levelSelector > input[type=checkbox] + label.detailLevel {
296
- color: #3A87AD;
297
  }
 
298
  .levelSelector > input[type=checkbox]:checked + label.detailLevel {
299
- background-color: #3A87AD;
300
- color: #fff;
301
  }
 
302
  .noLog {
303
- font-size: x-large;
304
- display: block;
305
- width: 100%;
306
- text-align: center;
307
- margin-top: 20ex;
308
- line-height: 150%;
309
  }
 
310
  .loggingON, .loggingOFF {
311
- font-weight: bolder;
312
  }
 
313
  .loggingON {
314
- color: green;
315
  }
 
316
  .loggingOFF {
317
- color: red;
318
  }
319
- th input{
320
- margin-left: 0 !important;
321
- margin-top: 1px !important;
 
322
  }
323
- td input{
324
- margin-right: 0 !important;
 
325
  }
 
326
  textarea[readonly], input[readonly], select[readonly] {
327
- background-color: #dcdcdc;
328
  }
 
329
  .nowrap {
330
- white-space: nowrap;
331
  }
 
332
  .column-level {
333
- white-space: nowrap;
334
- width: 6em;
335
  }
 
336
  .column-date {
337
- width: 16em !important;
338
  }
 
339
  .column-thumbupload {
340
- -webkit-user-select: none;
341
- -moz-user-select: none;
342
- -ms-user-select: none;
343
- user-select: none;
344
- cursor: context-menu;
345
  }
 
346
  .column-thumbupload > * {
347
- visibility: hidden;
348
  }
 
349
  .column-thumbupload .dashicons {
350
- font-size: x-large;
351
- padding-top: 2px;
352
- padding-left: 16px;
353
- padding-right: 10px;
354
- vertical-align: text-bottom;
355
  }
 
356
  /*Attempt to add fancy shadows*/
357
  /*#ThumbsTable tr:hover {
358
  z-index: 3;
@@ -363,127 +436,387 @@ textarea[readonly], input[readonly], select[readonly] {
363
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
364
  }*/
365
  #ThumbsTable tr:hover .column-thumbupload > *, #document_gallery_gen_box #ThumbsTable tr .column-thumbupload > * {
366
- visibility: visible;
367
  }
 
368
  .dragover {
369
- outline: 3px dashed #83b4d8;
370
  }
 
371
  .html5dndmarker span {
372
- padding: 0px 10px;
373
  }
374
- .buttons-area input:first-child {
375
- display: none !important;
 
376
  }
 
377
  #document_gallery_gen_box h3 span {
378
- font-weight: 100;
379
  }
 
380
  #document_gallery_gen_box h3 span i b {
381
- font-weight: 500;
382
  }
 
383
  th.column-description {
384
- text-align: left !important;
385
  }
 
386
  .editable-description {
387
- max-height: 70px;
388
- overflow-y: auto;
389
- text-align: justify;
390
  }
 
391
  .column-description textarea {
392
- height: 65px;
393
- width: 100%;
394
  }
 
395
  td.column-title, td.column-description {
396
- position: relative;
397
  }
 
398
  td.column-title.trans, td.column-description.trans {
399
- background-color: inherit;
400
- transition: background-color 1s linear;
401
- -moz-transition: background-color 1s linear;
402
- -o-transition: background-color 1s linear;
403
- -webkit-transition: background-color 1s linear;
404
  }
 
405
  td .dashicons-edit, td .edit-controls {
406
- display: none;
407
- position: absolute;
408
- top: 5px;
409
- right: 12px;
410
  }
411
- td .dashicons-edit, .edit-controls .dashicons-yes, .edit-controls .dashicons-no {
412
- z-index: 100;
413
- font-size: x-large;
414
- cursor: pointer;
 
 
 
415
  }
416
- td .dashicons-edit {
417
- color: #0074A2;
 
418
  }
419
- .edit-controls:hover {
420
- opacity: 1;
 
421
  }
 
422
  .edit-controls {
423
- opacity: 0.1;
424
  }
 
425
  .edit-controls .dashicons-yes {
426
- color: green;
427
  }
 
428
  .edit-controls .dashicons-no {
429
- color: red;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
430
  }
 
431
  .responseSuccess {
432
- background-color: greenyellow !important;
433
  }
 
434
  .responseFail {
435
- background-color: crimson !important;
436
  }
 
437
  td.column-title .dashicons-edit, td.column-description .dashicons-edit {
438
- display: block;
439
- overflow: hidden;
440
- height: 0;
441
- opacity: 0;
442
- transition: opacity 1s ease-out;
443
- -moz-transition: opacity 1s ease-out;
444
- -o-transition: opacity 1s ease-out;
445
- -webkit-transition: opacity 1s ease-out;
446
  }
 
447
  td.column-title:hover .dashicons-edit, td.column-description:hover .dashicons-edit {
448
- opacity: 1;
449
- height: auto;
450
  }
 
451
  td.column-title input {
452
- width: 75%;
453
  }
 
454
  .manual-download {
455
- display: block;
456
- text-align: center;
457
  }
 
458
  .editable-description::-webkit-scrollbar, .column-description textarea::-webkit-scrollbar {
459
- width: 5px;
460
- height: 5px;
461
  }
 
462
  .editable-description::-webkit-scrollbar-track-piece, .column-description textarea::-webkit-scrollbar-track-piece {
463
- background-color: #ffffff;
464
- -webkit-border-radius: 5px;
 
 
465
  }
 
466
  .editable-description::-webkit-scrollbar-thumb:vertical, .column-description textarea::-webkit-scrollbar-thumb:vertical {
467
- height: 5px;
468
- background-color: #9b9b9b;;
469
- -webkit-border-radius: 5px;
470
  }
 
471
  .document-gallery-settings label.setting {
472
- margin: 2px 0;
473
  }
 
474
  .document-gallery-settings label.setting table {
475
- width: 100%;
476
- padding-right: 8px;
477
  }
 
478
  .document-gallery-settings label.setting table tr td:last-of-type, .document-gallery-settings label.setting table tr td:last-of-type * {
479
- text-align: right !important;
480
- float: none !important;
481
- margin: auto 0 !important;
482
  }
 
483
  .document-gallery-settings label.setting table tr td:last-of-type select {
484
- max-width: none !important;
485
  }
 
486
  .document-gallery-settings label.setting table tr td span {
487
- text-align: left !important;
488
- float: none !important;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
  }
1
  @media screen and (max-width: 782px) {
2
+ .column-title {
3
+ width: 150px;
4
+ }
5
+
6
+ .column-date {
7
+ display: table-cell !important;
8
+ width: auto !important;
9
+ }
10
+
11
+ .nav-tab-wrapper {
12
+ padding-left: 5px !important;
13
+ padding-right: 5px !important;
14
+ }
15
+
16
+ .top .tablenav-pages {
17
+ display: none;
18
+ }
19
+
20
+ .bottom .tablenav-pages {
21
+ width: auto !important;
22
+ margin-top: 0 !important;
23
+ }
24
+
25
+ .bottom .displaying-num {
26
+ position: inherit !important;
27
+ }
28
  }
29
+
30
  @media screen and (max-width: 979px) {
31
+ .column-thumbupload {
32
+ display: none;
33
+ }
34
+
35
+ .thumbs-list-wrapper {
36
+ margin-top: 0 !important;
37
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  }
39
+
40
+ div.thumbs-list-wrapper, div.log-list-wrapper {
41
+ text-align: center;
42
+ margin-top: 1.5em;
43
+ }
44
+
45
+ div.thumbs-list-wrapper > div, div.log-list-wrapper > div {
46
+ margin: 0 auto;
47
+ display: inline-block;
48
+ }
49
+
50
+ #ThumbsTable, #LogTable {
51
+ border: none;
52
+ box-shadow: none;
53
+ -webkit-box-shadow: none;
54
+ -moz-box-shadow: none;
55
+ border-radius: 10px;
56
+ -webkit-border-radius: 10px;
57
+ -moz-border-radius: 10px;
58
+ background: #B9C9FE;
59
+ color: #039;
60
+ width: 100%;
61
+ margin: 10px auto;
62
+ }
63
+
64
  #ThumbsTable tbody, #LogTable tbody {
65
+ background: #E8EdFF;
66
+ color: #669;
67
  }
68
+
69
+ #ThumbsTable > tbody > tr:hover, #LogTable > tbody > tr:hover {
70
+ background: #D0DAFD;
71
  }
72
+
73
+ #ThumbsTable > tbody > tr:not(:last-child) td, #LogTable > tbody > tr:not(:last-child) td {
74
+ border-bottom: 1px solid #B9C9FE;
 
75
  }
76
+
77
+ #ThumbsTable td, #ThumbsTable th, #LogTable td, #LogTable th {
78
+ text-align: center;
79
+ vertical-align: middle;
80
+ margin: 0;
81
+ padding: 4px;
82
  }
83
+
84
  #LogTable td {
85
+ text-align: left;
86
  }
87
+
88
  td.title.column-title, .column-thumbupload {
89
+ text-align: left !important;
90
  }
91
+
92
+ #ThumbsTable img {
93
+ display: block;
94
+ margin: 5px auto;
95
  }
96
+
97
  #LogTable td > pre, .spoiler-body > pre, .expander pre, .collapser pre {
98
+ margin: 0;
99
+ display: inline-block;
100
+ white-space: pre-line;
101
  }
102
+
103
+ tr.selected {
104
+ background: #B6ADCE;
105
  }
106
+
107
+ tr.selected:hover {
108
+ background: #D8D3E5 !important;
109
  }
110
+
111
  .check-column, .column-icon {
112
+ white-space: nowrap;
113
+ width: 1%;
114
  }
115
+
116
  .column-entry, .column-title {
117
+ /*width: 100%;*/
118
  }
119
+
120
  .column-thumbupload {
121
+ white-space: nowrap;
122
+ width: 35%;
123
  }
124
+
125
  #document_gallery_gen_box .column-thumbupload {
126
+ width: auto;
127
+ padding-left: 7em;
128
  }
129
+
130
+ .nav-tab:before, .deleteSelected:before, .clearLog:before, .expandAll:before, .collapseAll:before, .logLabel.date:before, .collapser:after, .expander:after {
131
+ display: inline-block;
132
+ -webkit-font-smoothing: antialiased;
133
+ font: normal 20px/1 'dashicons';
134
+ vertical-align: text-bottom;
135
+ padding-right: 5px;
136
  }
137
+
138
+ .General-tab:before {
139
+ content: '\f108';
140
  }
141
+
142
+ .Thumbnail-tab:before {
143
+ content: '\f233';
144
  }
145
+
146
+ .Logging-tab:before {
147
+ content: '\f163';
148
  }
149
+
150
+ .Advanced-tab:before {
151
+ content: '\f332';
152
  }
153
+
154
+ .deleteSelected:before, .clearLog:before {
155
+ content: '\f182';
156
  }
157
+
158
  .expandAll:before {
159
+ content: '\f211';
160
  }
161
+
162
  .collapseAll:before {
163
+ content: '\f506';
164
  }
165
+
166
  .expandAll, .collapseAll {
167
+ display: none;
168
+ }
169
+
170
+ #ThumbsTable .title a:after, #LogTable > tbody a:after {
171
+ content: '\f504';
172
+ display: inline-block;
173
+ -webkit-font-smoothing: antialiased;
174
+ font-family: 'dashicons';
175
+ font-size: inherit;
176
+ font-style: normal;
177
+ font-variant: normal;
178
+ font-weight: normal;
179
+ line-height: 1;
180
+ vertical-align: inherit;
181
+ padding-left: 5px;
 
 
 
 
 
 
 
 
 
 
 
182
  }
183
+
184
+ #LogTable > tbody a {
185
+ -webkit-transition: none;
186
+ transition: none;
187
+ text-decoration: none;
188
+ outline: 0;
189
+ }
190
+
191
+ #LogTable > tbody pre strong {
192
+ font-weight: bolder;
193
+ }
194
+
195
+ #LogTable > tbody a:active, #LogTable > tbody a:hover {
196
+ color: #2ea2cc;
197
+ }
198
+
199
  .levelSelector {
200
+ float: right;
201
  }
202
+
203
  .logLabel {
204
+ padding: 0 10px;
205
+ color: #fff !important;
206
+ text-decoration: none;
207
+ font-weight: bolder;
208
+ border: none !important;
209
+ float: left;
210
+ margin-left: 1px;
211
+ margin-top: 1px;
212
+ -webkit-border-radius: 100px;
213
+ -moz-border-radius: 100px;
214
+ border-radius: 100px;
215
+ cursor: context-menu;
216
  }
217
+
218
  .logLabel.warning {
219
+ background: #F89406;
220
  }
221
+
222
  .logLabel.detail {
223
+ background: #3A87AD;
224
  }
225
+
226
  .logLabel.error {
227
+ background: #CC0000;
228
  }
229
+
230
  .logLabel.date {
231
+ background: #999999;
232
+ /*white-space: nowrap;*/
233
+ font-weight: inherit;
234
  }
235
+
236
+ .logLabel.date:before {
237
+ font-size: inherit;
238
+ vertical-align: middle;
239
+ padding-bottom: 0.2em;
240
+ content: '\f469';
241
  }
242
+
243
  .spoiler-body {
244
+ padding: 1px 6px 2px;
245
+ display: none;
246
+ border-top: 1px solid #C3CBD1;
247
+ background: #F5F5F5;
248
  }
249
+
250
  .column-entry {
251
+ text-align: left !important;
252
  }
253
+
254
  .expander pre, .collapser pre {
255
+ vertical-align: middle;
256
+ float: left;
257
+ white-space: pre-wrap;
258
  }
259
+
260
  .expander, .collapser {
261
+ display: table;
262
+ vertical-align: middle;
263
+ width: 100%;
264
+ cursor: pointer;
265
  }
266
+
267
  .expander > div, .collapser > div {
268
+ display: table-cell;
269
+ vertical-align: middle;
270
+ height: 100%;
271
  }
272
+
273
  .dashicons.dashicons-arrow-down-alt2, .dashicons.dashicons-arrow-up-alt2 {
274
+ float: right;
275
+ padding-right: 15px;
276
  }
277
+
278
  .levelSelector > *, .dashicons {
279
+ -webkit-touch-callout: none;
280
+ -webkit-user-select: none;
281
+ -khtml-user-select: none;
282
+ -moz-user-select: none;
283
+ -ms-user-select: none;
284
+ user-select: none;
285
  }
286
+
287
  .levelSelector > input[type=checkbox] {
288
+ display: none;
289
  }
290
+
291
  .levelSelector > input[type=checkbox] + label {
292
+ color: #6B6B6B;
293
+ font-weight: bolder;
294
+ margin: 4px 0;
295
+ overflow: auto;
296
+ text-align: center;
297
+ padding: 3px 8px;
298
+ display: table-cell;
299
+ background-color: #F5F5F5;
300
+ background-image: -moz-linear-gradient(top, #FFF, #E6E6E6);
301
+ background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#FFF), to(#E6E6E6));
302
+ background-image: -webkit-linear-gradient(top, #FFF, #E6E6E6);
303
+ background-image: -o-linear-gradient(top, #FFF, #E6E6E6);
304
+ background-image: linear-gradient(to bottom, #FFF, #E6E6E6);
305
+ background-repeat: repeat-x;
306
+ border: 1px solid #CCC;
307
+ border-color: #E6E6E6 #E6E6E6 #BFBFBF;
308
+ border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
309
+ border-bottom-color: #B3B3B3;
310
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFE6E6E6', GradientType=0);
311
+ filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
312
+ -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
313
+ -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
314
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
315
  }
316
+
317
  .levelSelector > input[type=checkbox] + label:first-of-type {
318
+ border-top-left-radius: 4px;
319
+ border-bottom-left-radius: 4px;
320
  }
321
+
322
  .levelSelector > input[type=checkbox] + label:last-of-type {
323
+ border-top-right-radius: 4px;
324
+ border-bottom-right-radius: 4px;
325
  }
326
+
327
  .levelSelector > input[type=checkbox]:checked + label {
328
+ background-image: none;
329
+ outline: 0;
330
+ -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
331
+ -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
332
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
333
  }
334
+
335
  .levelSelector > input[type=checkbox] + label.errorLevel {
336
+ color: #CC0000;
337
  }
338
+
339
  .levelSelector > input[type=checkbox]:checked + label.errorLevel {
340
+ background-color: #CC0000;
341
+ color: #FFF;
342
  }
343
+
344
  .levelSelector > input[type=checkbox] + label.warningLevel {
345
+ color: #F89406;
346
  }
347
+
348
  .levelSelector > input[type=checkbox]:checked + label.warningLevel {
349
+ background-color: #F89406;
350
+ color: #FFF;
351
  }
352
+
353
  .levelSelector > input[type=checkbox] + label.detailLevel {
354
+ color: #3A87AD;
355
  }
356
+
357
  .levelSelector > input[type=checkbox]:checked + label.detailLevel {
358
+ background-color: #3A87AD;
359
+ color: #FFF;
360
  }
361
+
362
  .noLog {
363
+ font-size: x-large;
364
+ display: block;
365
+ width: 100%;
366
+ text-align: center;
367
+ margin-top: 20ex;
368
+ line-height: 150%;
369
  }
370
+
371
  .loggingON, .loggingOFF {
372
+ font-weight: bolder;
373
  }
374
+
375
  .loggingON {
376
+ color: green;
377
  }
378
+
379
  .loggingOFF {
380
+ color: red;
381
  }
382
+
383
+ th input {
384
+ margin-left: 0 !important;
385
+ margin-top: 1px !important;
386
  }
387
+
388
+ td input {
389
+ margin-right: 0 !important;
390
  }
391
+
392
  textarea[readonly], input[readonly], select[readonly] {
393
+ background-color: #DCDCDC;
394
  }
395
+
396
  .nowrap {
397
+ white-space: nowrap;
398
  }
399
+
400
  .column-level {
401
+ white-space: nowrap;
402
+ width: 6em;
403
  }
404
+
405
  .column-date {
406
+ width: 16em !important;
407
  }
408
+
409
  .column-thumbupload {
410
+ -webkit-user-select: none;
411
+ -moz-user-select: none;
412
+ -ms-user-select: none;
413
+ user-select: none;
414
+ cursor: default;
415
  }
416
+
417
  .column-thumbupload > * {
418
+ visibility: hidden;
419
  }
420
+
421
  .column-thumbupload .dashicons {
422
+ font-size: x-large;
423
+ padding-top: 2px;
424
+ padding-left: 16px;
425
+ padding-right: 10px;
426
+ vertical-align: text-bottom;
427
  }
428
+
429
  /*Attempt to add fancy shadows*/
430
  /*#ThumbsTable tr:hover {
431
  z-index: 3;
436
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
437
  }*/
438
  #ThumbsTable tr:hover .column-thumbupload > *, #document_gallery_gen_box #ThumbsTable tr .column-thumbupload > * {
439
+ visibility: visible;
440
  }
441
+
442
  .dragover {
443
+ outline: 3px dashed #83b4d8;
444
  }
445
+
446
  .html5dndmarker span {
447
+ padding: 0 10px;
448
  }
449
+
450
+ .buttons-area input:first-child, .invis {
451
+ display: none !important;
452
  }
453
+
454
  #document_gallery_gen_box h3 span {
455
+ font-weight: 100;
456
  }
457
+
458
  #document_gallery_gen_box h3 span i b {
459
+ font-weight: 500;
460
  }
461
+
462
  th.column-description {
463
+ text-align: left !important;
464
  }
465
+
466
  .editable-description {
467
+ max-height: 70px;
468
+ overflow-y: auto;
469
+ text-align: justify;
470
  }
471
+
472
  .column-description textarea {
473
+ height: 65px;
474
+ width: 100%;
475
  }
476
+
477
  td.column-title, td.column-description {
478
+ position: relative;
479
  }
480
+
481
  td.column-title.trans, td.column-description.trans {
482
+ background-color: inherit;
483
+ transition: background-color 1s linear;
484
+ -o-transition: background-color 1s linear;
485
+ -moz-transition: background-color 1s linear;
486
+ -webkit-transition: background-color 1s linear;
487
  }
488
+
489
  td .dashicons-edit, td .edit-controls {
490
+ display: none;
491
+ position: absolute;
492
+ top: 5px;
493
+ right: 12px;
494
  }
495
+
496
+ td .dashicons-edit, .edit-controls .dashicons-yes, .edit-controls .dashicons-no, .edit-controls .dashicons-update {
497
+ z-index: 100;
498
+ font-size: x-large;
499
+ cursor: pointer;
500
+ width: auto;
501
+ height: auto;
502
  }
503
+
504
+ td .dashicons-edit, .edit-controls .dashicons-update {
505
+ color: #0074A2;
506
  }
507
+
508
+ .edit-controls:hover, .edit-controls.waiting {
509
+ opacity: 1;
510
  }
511
+
512
  .edit-controls {
513
+ opacity: 0.1;
514
  }
515
+
516
  .edit-controls .dashicons-yes {
517
+ color: green;
518
  }
519
+
520
  .edit-controls .dashicons-no {
521
+ color: red;
522
+ }
523
+
524
+ .edit-controls.waiting .dashicons-yes, .edit-controls.waiting .dashicons-no {
525
+ display: none;
526
+ }
527
+
528
+ .edit-controls .dashicons-update {
529
+ display: none;
530
+ cursor: default;
531
+ }
532
+
533
+ .edit-controls.waiting .dashicons-update, .deleteSelected.waiting:before {
534
+ display: block;
535
+ -webkit-animation: spin 1s linear infinite;
536
+ -moz-animation: spin 1s linear infinite;
537
+ animation: spin 1s linear infinite;
538
+ }
539
+
540
+ .deleteSelected.waiting:before {
541
+ content: '\f463';
542
+ width: auto;
543
+ height: auto;
544
+ display: inline-block;
545
+ padding: 0;
546
+ }
547
+
548
+ @-moz-keyframes spin {
549
+ 100% {
550
+ -moz-transform: rotate(360deg);
551
+ }
552
+ }
553
+
554
+ @-webkit-keyframes spin {
555
+ 100% {
556
+ -webkit-transform: rotate(360deg);
557
+ }
558
+ }
559
+
560
+ @keyframes spin {
561
+ 100% {
562
+ -webkit-transform: rotate(360deg);
563
+ transform: rotate(360deg);
564
+ }
565
  }
566
+
567
  .responseSuccess {
568
+ background-color: greenyellow !important;
569
  }
570
+
571
  .responseFail {
572
+ background-color: crimson !important;
573
  }
574
+
575
  td.column-title .dashicons-edit, td.column-description .dashicons-edit {
576
+ height: 0;
577
+ opacity: 0;
578
+ display: block;
579
+ overflow: hidden;
580
+ transition: opacity 1s ease-out;
581
+ -o-transition: opacity 1s ease-out;
582
+ -moz-transition: opacity 1s ease-out;
583
+ -webkit-transition: opacity 1s ease-out;
584
  }
585
+
586
  td.column-title:hover .dashicons-edit, td.column-description:hover .dashicons-edit {
587
+ opacity: 1;
588
+ height: auto;
589
  }
590
+
591
  td.column-title input {
592
+ width: 75%;
593
  }
594
+
595
  .manual-download {
596
+ display: block;
597
+ text-align: center;
598
  }
599
+
600
  .editable-description::-webkit-scrollbar, .column-description textarea::-webkit-scrollbar {
601
+ width: 5px;
602
+ height: 5px;
603
  }
604
+
605
  .editable-description::-webkit-scrollbar-track-piece, .column-description textarea::-webkit-scrollbar-track-piece {
606
+ background-color: #FFFFFF;
607
+ -webkit-border-radius: 5px;
608
+ -moz-border-radius: 5px;
609
+ border-radius: 5px;
610
  }
611
+
612
  .editable-description::-webkit-scrollbar-thumb:vertical, .column-description textarea::-webkit-scrollbar-thumb:vertical {
613
+ height: 5px;
614
+ background-color: #9B9B9B;;
615
+ -webkit-border-radius: 5px;
616
  }
617
+
618
  .document-gallery-settings label.setting {
619
+ margin: 2px 0;
620
  }
621
+
622
  .document-gallery-settings label.setting table {
623
+ width: 100%;
624
+ padding-right: 8px;
625
  }
626
+
627
  .document-gallery-settings label.setting table tr td:last-of-type, .document-gallery-settings label.setting table tr td:last-of-type * {
628
+ text-align: right !important;
629
+ float: none !important;
630
+ margin: auto 0 !important;
631
  }
632
+
633
  .document-gallery-settings label.setting table tr td:last-of-type select {
634
+ max-width: none !important;
635
  }
636
+
637
  .document-gallery-settings label.setting table tr td span {
638
+ text-align: left !important;
639
+ float: none !important;
640
+ }
641
+
642
+ .progress {
643
+ border: 0;
644
+ width: 100%;
645
+ height: 18px;
646
+ position: relative;
647
+ background-color: #F1F1F1; /*#F3F3F3*/
648
+ -webkit-border-radius: 9px;
649
+ -moz-border-radius: 9px;
650
+ border-radius: 9px;
651
+ -webkit-box-shadow: inset 0 -1px 1px rgba(255, 255, 255, 0.3);
652
+ -moz-box-shadow: inset 0 -1px 1px rgba(255, 255, 255, 0.3);
653
+ box-shadow: inset 0 -1px 1px rgba(255, 255, 255, 0.3);
654
+ }
655
+
656
+ .column-thumbupload .progress {
657
+ visibility: visible !important;
658
+ }
659
+
660
+ .progress > span {
661
+ border: 0;
662
+ height: 100%;
663
+ display: block;
664
+ overflow: hidden;
665
+ position: relative;
666
+ -webkit-transition: width 0.5s linear;
667
+ -moz-transition: width 0.5s linear;
668
+ -o-transition: width 0.5s linear;
669
+ transition: width 0.5s linear;
670
+ background-color: #7AD03A; /*#0074A2*/
671
+ -webkit-border-radius: 9px;
672
+ -moz-border-radius: 9px;
673
+ border-radius: 9px;
674
+ background-image: linear-gradient(
675
+ 90deg,
676
+ rgb(43, 194, 83) 37%,
677
+ rgb(84, 240, 84) 69%
678
+ );
679
+ background-image: -moz-linear-gradient(
680
+ 90deg,
681
+ rgb(43, 194, 83) 37%,
682
+ rgb(84, 240, 84) 69%
683
+ );
684
+ background-image: -o-linear-gradient(
685
+ 90deg,
686
+ rgb(43, 194, 83) 37%,
687
+ rgb(84, 240, 84) 69%
688
+ );
689
+ background-image: -webkit-gradient(
690
+ linear,
691
+ left bottom,
692
+ left top,
693
+ color-stop(0, rgb(43, 194, 83)),
694
+ color-stop(1, rgb(84, 240, 84))
695
+ );
696
+ -webkit-box-shadow: inset 0 2px 9px rgba(255, 255, 255, 0.3),
697
+ inset 0 -2px 6px rgba(0, 0, 0, 0.4);
698
+ -moz-box-shadow: inset 0 2px 9px rgba(255, 255, 255, 0.3),
699
+ inset 0 -2px 6px rgba(0, 0, 0, 0.4);
700
+ box-shadow: inset 0 2px 9px rgba(255, 255, 255, 0.3),
701
+ inset 0 -2px 6px rgba(0, 0, 0, 0.4);
702
+ }
703
+
704
+ .progress > span:after, .progress.animate > span > span {
705
+ top: 0;
706
+ left: 0;
707
+ right: 0;
708
+ bottom: 0;
709
+ z-index: 1;
710
+ content: '';
711
+ overflow: hidden;
712
+ position: absolute;
713
+ -webkit-border-radius: 9px;
714
+ -moz-border-radius: 9px;
715
+ border-radius: 9px;
716
+ background-image: -webkit-gradient(
717
+ linear, 0 0, 100% 100%,
718
+ color-stop(.25, rgba(255, 255, 255, .2)),
719
+ color-stop(.25, transparent), color-stop(.5, transparent),
720
+ color-stop(.5, rgba(255, 255, 255, .2)),
721
+ color-stop(.75, rgba(255, 255, 255, .2)),
722
+ color-stop(.75, transparent), to(transparent)
723
+ );
724
+ background-image: -moz-linear-gradient(
725
+ 315deg,
726
+ rgba(255, 255, 255, .2) 25%,
727
+ transparent 25%,
728
+ transparent 50%,
729
+ rgba(255, 255, 255, .2) 50%,
730
+ rgba(255, 255, 255, .2) 75%,
731
+ transparent 75%,
732
+ transparent
733
+ );
734
+ background-image: -o-linear-gradient(
735
+ 315deg,
736
+ rgba(255, 255, 255, .2) 25%,
737
+ transparent 25%,
738
+ transparent 50%,
739
+ rgba(255, 255, 255, .2) 50%,
740
+ rgba(255, 255, 255, .2) 75%,
741
+ transparent 75%,
742
+ transparent
743
+ );
744
+ background-image: linear-gradient(
745
+ 315deg,
746
+ rgba(255, 255, 255, .2) 25%,
747
+ transparent 25%,
748
+ transparent 50%,
749
+ rgba(255, 255, 255, .2) 50%,
750
+ rgba(255, 255, 255, .2) 75%,
751
+ transparent 75%,
752
+ transparent
753
+ );
754
+ -webkit-background-size: 50px 50px;
755
+ -moz-background-size: 50px 50px;
756
+ background-size: 50px 50px;
757
+ -webkit-animation: move 2s linear infinite;
758
+ -moz-animation: move 2s linear infinite;
759
+ -ms-animation: move 2s linear infinite;
760
+ animation: move 2s linear infinite;
761
+ }
762
+
763
+ @-webkit-keyframes move {
764
+ 0% {
765
+ background-position: 0 0;
766
+ }
767
+ 100% {
768
+ background-position: 50px 50px;
769
+ }
770
+ }
771
+
772
+ @-moz-keyframes move {
773
+ 0% {
774
+ background-position: 0 0;
775
+ }
776
+ 100% {
777
+ background-position: 50px 50px;
778
+ }
779
+ }
780
+
781
+ @-ms-keyframes move {
782
+ 0% {
783
+ background-position: 0 0;
784
+ }
785
+ 100% {
786
+ background-position: 50px 50px;
787
+ }
788
+ }
789
+
790
+ @keyframes move {
791
+ 0% {
792
+ background-position: 0 0;
793
+ }
794
+ 100% {
795
+ background-position: 50px 50px;
796
+ }
797
+ }
798
+
799
+ .progress.animate > span:after {
800
+ display: none;
801
+ }
802
+
803
+ .progress.success > span {
804
+ background-color: green;
805
+ width: 100% !important;
806
+ background-image: none !important;
807
+ }
808
+
809
+ .progress.fail > span {
810
+ background-color: red;
811
+ background-image: none !important;
812
+ }
813
+
814
+ .progress.nostripes > span > span, .progress.nostripes > span:after,
815
+ .progress.success > span > span, .progress.success > span:after,
816
+ .progress.fail > span > span, .progress.fail > span:after {
817
+ -webkit-animation: none;
818
+ -moz-animation: none;
819
+ -ms-animation: none;
820
+ animation: none;
821
+ background-image: none;
822
  }
assets/css/style.css CHANGED
@@ -1,62 +1,69 @@
1
- div.document-icon{ text-align: center; }
 
 
2
 
3
- div.document-icon img{
4
- width: 89px;
5
- max-width: 100%;
6
- border: none;
7
  }
8
 
9
- div.document-icon a{
10
- font-size: 10px;
11
- line-height: 12px;
12
  }
13
 
14
- div.document-icon{ margin: 5px 0 0; }
 
 
15
 
16
  /* WITHOUT DESCRIPTION */
17
- div.document-icon{
18
- display: inline-block;
19
- vertical-align: top;
20
- overflow: hidden;
21
  }
22
- div.document-icon-wrapper{
23
- width: 100%;
24
- padding: 0;
25
- text-align: left;
 
26
  }
 
27
  /* END WITHOUT DESCRIPTION */
28
 
29
  /* WITH DESCRIPTION */
30
- div.descriptions.document-icon-wrapper div.document-icon{
31
- max-width: 115px;
32
- padding: 0;
33
- padding-right: 3px;
34
- float: left;
35
  }
36
 
37
- div.descriptions.document-icon-wrapper{
38
- vertical-align: middle;
39
- text-align: inherit;
40
  }
41
 
42
- div.descriptions.document-icon-wrapper img{
43
- width: 65px;
44
- max-width: 100%;
45
  }
46
 
47
  /* clearfix */
48
  /* can't depend on theme having a clearfix class,
49
  so build it into dg css */
50
  div.descriptions.document-icon-wrapper:before,
51
- div.descriptions.document-icon-wrapper:after{
52
- content: "";
53
- display: table;
54
  }
55
 
56
- div.descriptions.document-icon-wrapper:after{
57
  clear: both;
58
  }
59
- div.descriptions.document-icon-wrapper{
 
60
  zoom: 1; /* For IE 6/7 (trigger hasLayout) */
61
  }
 
62
  /* END WITH DESCRIPTION */
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;
31
  }
32
+
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
  }
51
 
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
+
69
  /* END WITH DESCRIPTION */
assets/js/admin.js CHANGED
@@ -1,362 +1,416 @@
1
- jQuery(document).ready(function(){
2
- jQuery('.cb-ids').change(function() {
3
- if(jQuery(this).is(':checked')) {
4
- jQuery(this).closest('tr').addClass('selected');
5
- }
6
- else {
7
- jQuery(this).closest('tr').removeClass('selected');
8
- }
9
- });
10
- jQuery('th input:checkbox').change(function() {
11
- if(jQuery(this).is(':checked')) {
12
- jQuery('#ThumbsTable tbody tr').addClass('selected');
13
- }
14
- else {
15
- jQuery('#ThumbsTable tbody tr').removeClass('selected');
16
- }
17
- });
18
- jQuery('input.current-page').bind('keypress', {}, function(e) {
19
- var code = (e.keyCode ? e.keyCode : e.which);
20
- if (code == 13) {//Enter keycode
21
- e.preventDefault();
22
- jQuery(location).attr('href','?'+jQuery.param(jQuery.extend(URL_params,{ sheet: this.value })));
23
- }
24
- });
25
- jQuery('select.limit_per_page').change(function() {
26
- jQuery(location).attr('href','?'+jQuery.param(jQuery.extend(URL_params,{ limit: this.value })));
27
- });
28
- jQuery('#tab-Thumbnail').submit( function (event) {
29
- event.preventDefault();
30
- if (jQuery('.cb-ids:checked').length > 0) {
31
- var a = jQuery(this).attr('action');
32
- var b = jQuery(this).serialize() +
33
- '&document_gallery%5Bajax%5D=true' +
34
- '&document_gallery%5Bcleanup%5D=true';
35
- jQuery.post(a, b, function(response) {
36
- if (response.indexOf("\n") == -1) {
37
- eval('var reply = ' + response + ';');
38
- if (reply.result) {
39
- var result = reply.deleted;
40
- for (var index in result) {
41
- jQuery('input[type=checkbox][value='+result[index]+']').closest('tr').fadeOut('slow', 0.00, function() {jQuery(this).slideUp('slow', function() {jQuery(this).remove();});});
42
- }
43
- }
44
- } else {
45
- console.log('Invalid response from server:');
46
- console.log(response);
47
- }
48
- } ).fail(function() {
49
- console.log( 'Problem in reaching the server' );
50
- });
51
- }
52
- return false;
53
- });
54
-
55
- jQuery('#tab-Advanced #options-dump').click(function() {
56
- jQuery(this).select();
57
- });
58
-
59
- function toggleSpoiler() {
60
- var sel = getSelection().toString();
61
- if(!sel){
62
- jQuery(this).next().slideToggle('slow');
63
- jQuery(this).find('.dashicons').toggleClass('dashicons-arrow-down-alt2 dashicons-arrow-up-alt2');
64
- jQuery(this).toggleClass('expander collapser');
65
- jQuery(this).attr('title', (jQuery(this).hasClass('expander') ? 'Click to Expand' : 'Click to Collapse'));
66
- }
67
- }
 
 
 
 
 
 
 
68
 
69
- jQuery('.expander').click(toggleSpoiler);
70
 
71
- if (jQuery('.spoiler-body').length) {
72
- jQuery('.expandAll, .collapseAll').addClass('button');
73
- jQuery('.expandAll').click(function(e) {
74
- e.preventDefault();
75
- jQuery('.expander').trigger('click');
76
- });
77
- jQuery('.collapseAll').click(function(e) {
78
- e.preventDefault();
79
- jQuery('.collapser').trigger('click');
80
- });
81
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
- jQuery('.levelSelector input').change(function() {
84
- if (jQuery(this).val() == 'all') {
85
- jQuery('.levelSelector input').not("[value='all']").prop('checked', jQuery(this).is(':checked'));
86
- jQuery(this).is(':checked') ? jQuery('#LogTable tbody tr').show() : jQuery('#LogTable tbody tr').hide();
87
- }
88
- else {
89
- jQuery(this).is(':checked') ? jQuery('#LogTable tbody tr:has(span.'+jQuery(this).val()+')').show() : jQuery('#LogTable tbody tr:has(span.'+jQuery(this).val()+')').hide();
90
- if ( (jQuery('.levelSelector input:checked').not("[value='all']").length + 1) == jQuery('.levelSelector input[type="checkbox"]').length ) {
91
- jQuery('.levelSelector input[value="all"]').prop('checked', true);
92
- } else {
93
- jQuery('.levelSelector input[value="all"]').prop('checked', false);
94
- }
95
- }
96
- });
97
-
98
- jQuery('#LogTable .manage-column.column-date').click(function(){
99
- jQuery(this).toggleClass('asc desc');
100
  var table = jQuery('#LogTable > tbody');
101
  var rows = table.children('tr');
102
- table.append(rows.get().reverse());
103
- });
104
 
105
- function DragDropFilesStop(e) {
106
- e = e || event;
107
- if (e.dataTransfer.types) {
108
- //Testing if dragenter/dragover Event Contains Files - http://css-tricks.com/snippets/javascript/test-if-dragenterdragover-event-contains-files/
109
- for (var i = 0; i < e.dataTransfer.types.length; i++) {
110
- if (e.dataTransfer.types[i] == 'Files') {
111
- //Or maybe it is better just to test e.dataTransfer.files[0].name - http://stackoverflow.com/a/12622904/3951387
112
- //NO: before drop the list is not accessible
113
- e.stopPropagation();
114
- e.preventDefault();
115
- e.dataTransfer.dropEffect = 'none';
116
- break;
 
117
  }
118
- }
119
- //jQuery way - Not working
120
- /*if (jQuery.inArray('Files', e.dataTransfer.types)) {
121
- e.stopPropagation();
122
- e.preventDefault();
123
- e.dataTransfer.dropEffect = 'none';
124
- }*/
125
- }
126
- }
127
 
128
- //Preventing browser from acting on drag&dropped files beside the dedicated areas
129
- window.addEventListener('dragover', DragDropFilesStop, false);
130
- window.addEventListener('drop', DragDropFilesStop, false);
131
 
132
- function handleDragOver(e) {
133
- e = e || event;
134
- if (e.originalEvent.dataTransfer.types) {
135
- for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
136
- if (e.originalEvent.dataTransfer.types[i] == 'Files') {
137
- e.stopPropagation();
138
- e.preventDefault();
139
- //Have to exploit broker to access standart properties while using jQuery to bind handlers - http://stackoverflow.com/a/14792183/3951387
140
- e.originalEvent.dataTransfer.dropEffect = 'move';
141
- return false;
 
142
  }
143
- }
144
- }
145
- }
 
 
 
146
 
147
- //Firing HTML5 DragLeave only when all the DragEnter'ed child elements were DragLeave'd - http://stackoverflow.com/a/21002544
148
- var counter = {};
149
- function handleDragEnter(e) {
150
- // this / e.target is the current hover target.
151
- e = e || event;
152
- if (e.originalEvent.dataTransfer.types) {
153
- for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
154
- if (e.originalEvent.dataTransfer.types[i] == 'Files') {
155
- this.classList.add('dragover');
156
- counter[jQuery(this).data('entry')]++;//or without jQuery: this.getAttribute('data-entry')
157
- break;
158
  }
159
- }
160
- }
161
- }
162
 
163
- function handleDragLeave(e) {
164
- e = e || event;
165
- if (e.originalEvent.dataTransfer.types) {
166
- for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
167
- if (e.originalEvent.dataTransfer.types[i] == 'Files') {
168
- counter[jQuery(this).data('entry')]--;
169
- if (counter[jQuery(this).data('entry')] === 0) {
170
- this.classList.remove('dragover');// this / e.target is previous target element.
171
- }
172
- break;
 
173
  }
174
- }
175
- }
176
- }
177
 
178
- function handleDrop(e) {
179
- e = e || event;
180
- if (e.originalEvent.dataTransfer.types) {
181
- for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
182
- if (e.originalEvent.dataTransfer.types[i] == 'Files') {
183
 
184
- e.stopPropagation();// Stops some browsers from redirecting.
185
- e.preventDefault();
186
 
187
- processFiles(e.originalEvent.dataTransfer.files,jQuery(this).data('entry'));
188
- counter[jQuery(this).data('entry')] = 0;
189
- this.classList.remove('dragover');
190
- break;
 
191
  }
192
- }
193
- }
194
- }
195
 
196
- function handleBrowseButton(e) {
197
- e = e || event;
198
- processFiles(e.target.files,jQuery(this).closest('tr').data('entry'));
199
- //Was thinking about purging input:file control - http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery
200
- //Decided just to get rid of name properties thus such controls wouldn't be taken into consideration during form submit or processed with FormData
201
- }
202
 
203
- function processFiles(files,entry) {
204
- for (var i = 0, f; f = files[i]; i++) {
205
- //Processing only first qualifying file
206
- if (f.type.indexOf('image/') == 0 && typeof dg_admin_vars.upload_limit != 'undefined' && f.size <= parseInt(dg_admin_vars.upload_limit)) {
207
- var target;
208
- var formData= new FormData(jQuery('[data-entry='+entry+']').closest('form')[0]);
209
- if (typeof ajax_object != 'undefined' && typeof ajax_object.ajax_url != 'undefined') {
210
- target = ajax_object.ajax_url;
211
- formData.append('action', 'dg_upload_thumb');
212
- } else {
213
- target = jQuery('#tab-Thumbnail').attr('action');
214
- }
215
- formData.append('document_gallery[entry]', entry);
216
- formData.append('document_gallery[ajax]', 'true');
217
- formData.append('document_gallery[upload]', 'true');
218
- formData.append('file', f);
219
- var xhr = new XMLHttpRequest();
220
- xhr.open('POST', target);
221
- var theImg = jQuery('[data-entry='+entry+']').find('.column-icon img');
222
- xhr.onreadystatechange = function() {
223
- if (xhr.readyState == 4) {
224
- if (xhr.responseText.indexOf("\n") == -1) {
225
- eval('var response = ' + xhr.responseText + ';');
226
- if (response.result) {
227
- // check if generated thumbnail has the same url
228
- if (response.url === theImg.attr('src')) {
229
- theImg.attr('src', theImg.attr('src') + '?' + new Date().getTime());
 
 
 
 
 
 
230
  } else {
231
- theImg.attr('src', response.url);
 
 
232
  }
233
- }
234
- } else {
235
- console.log('Invalid response from server:');
236
- console.log(xhr.responseText);
237
- }
238
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
  }
240
- xhr.send(formData);
241
- break;
242
- }
243
- }
244
- }
245
 
246
- // Prepairing all the drop-zones on page load
247
- jQuery('#ThumbsTable tbody tr').each(function() {
248
- jQuery(this)
249
- .on('dragenter', handleDragEnter)
250
- .on('dragover', handleDragOver)
251
- .on('dragleave', handleDragLeave)
252
- .on('drop', handleDrop);
253
- counter[jQuery(this).data('entry')] = 0;
254
- jQuery(this).find('input:button').on('click', function() {
255
  jQuery(this).prevAll('input:file').click();
256
- });
257
- jQuery(this).find('input:file').on('change', handleBrowseButton);
258
- });
 
 
 
 
 
 
 
 
 
259
 
260
- //Checking Drag&Drop support
261
- //Structure is Not supported in Chrome's WebKit
262
- /*if (!('files' in DataTransfer.prototype)) {//Determine presence of HTML5 drag'n'drop file upload API - http://stackoverflow.com/a/2312859/3951387
263
- jQuery('.html5dndmarker').hide();
264
- }*/
265
- if (!('draggable' in document.createElement('span'))) {
266
- jQuery('.html5dndmarker').hide();
267
- }
268
-
269
- jQuery( 'td .dashicons-edit' ).click(function() {
270
- var cell = jQuery( this ).closest('td');
271
- if (cell.hasClass('column-title')) {
272
- if (cell.find('a').css('display') == 'none') {
 
 
273
  return;
274
- }
275
- cell.find('a').hide().after('<input type="text" value="'+cell.find('.editable-title').text()+'">');
276
- cell.find('input').focus();
277
- } else if (cell.hasClass('column-description')) {
278
- if (cell.find('.editable-description').css('display') == 'none') {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  return;
280
- }
281
- cell.find('.editable-description').hide().after('<textarea>'+cell.find('.editable-description').text()+'</textarea>');
282
- cell.find('textarea').focus();
283
- } else {
284
- return;
285
- }
286
- jQuery( this ).css('visibility', 'hidden');
287
- cell.find('.edit-controls').show();
288
- });
289
- jQuery( '.edit-controls .dashicons-no' ).click(function() {
290
- var cell = jQuery( this ).closest('td');
291
- if (cell.hasClass('column-title')) {
292
- if (cell.find('a').css('display') != 'none') {
 
 
 
 
 
 
 
 
293
  return;
294
- }
295
- cell.find('input').fadeOut('fast', function(){jQuery(this).remove();cell.find('a').fadeIn('fast');});
296
- } else if (cell.hasClass('column-description')) {
297
- if (cell.find('.editable-description').css('display') != 'none') {
298
  return;
299
- }
300
- cell.find('textarea').fadeOut('fast', function(){jQuery(this).remove();cell.find('.editable-description').fadeIn('fast');});
301
- } else {
302
- return;
303
- }
304
- jQuery( this ).closest('.edit-controls').hide();
305
- cell.find('.dashicons-edit').css('visibility', 'visible');
306
- });
307
- jQuery( '.edit-controls .dashicons-yes' ).click(function() {
308
- var cell = jQuery( this ).closest('td');
309
- var entry = jQuery(this).closest('tr').data('entry')
310
- var target = jQuery('#tab-Thumbnail').attr('action');
311
- var formData= new FormData(jQuery('[data-entry='+entry+']').closest('form')[0]);
312
- formData.append('document_gallery[entry]', entry);
313
- formData.append('document_gallery[ajax]', 'true');
314
- var newContent, updateGoal;
315
- if (cell.hasClass('column-title')) {
316
- newContent = cell.find('input');
317
- updateGoal = cell.find('.editable-title');
318
- formData.append('document_gallery[title]', encodeURIComponent(newContent.val()));
319
- } else if (cell.hasClass('column-description')) {
320
- newContent = cell.find('textarea');
321
- updateGoal = cell.find('.editable-description');
322
- formData.append('document_gallery[description]', encodeURIComponent(newContent.val()));
323
- } else {
324
- return;
325
- }
326
- if (newContent.val()==updateGoal.text()) {
327
- jQuery( this ).next('.dashicons-no').click();
328
- return;
329
- }
330
- var xhr = new XMLHttpRequest();
331
- xhr.open('POST', target);
332
- //var theImg = jQuery('[data-entry='+entry+']').find('.column-icon img');
333
- xhr.onreadystatechange = function() {
334
- if (xhr.readyState == 4) {
335
- cell.addClass('trans');
336
- if (xhr.responseText.indexOf("\n") == -1) {
337
- eval('var response = ' + xhr.responseText + ';');
338
- if (response.result) {
339
- updateGoal.text(newContent.val());
340
- cell.find('.dashicons-no').click();
341
- cell.addClass('responseSuccess').delay(2000).queue(function(){ jQuery(this).removeClass('responseSuccess').dequeue();}).delay(1100).queue(function(){ jQuery(this).removeClass('trans').dequeue();});
342
- } else {
343
- shake(newContent);
344
- cell.addClass('responseFail').delay(1100).queue(function(){ jQuery(this).removeClass('responseFail').dequeue();}).delay(1100).queue(function(){ jQuery(this).removeClass('trans').dequeue();});
345
- }
346
- } else {
347
- shake(newContent);
348
- cell.addClass('responseFail').delay(1100).queue(function(){ jQuery(this).removeClass('responseFail').dequeue();}).delay(1100).queue(function(){ jQuery(this).removeClass('trans').dequeue();});
349
- console.log('Invalid response from server:');
350
- console.log(xhr.responseText);
351
  }
352
- }
353
- }
354
- xhr.send(formData);
355
- });
356
- function shake(target) {
357
- var offset = 4;
358
- for( var i = 0; i < 6; i++ ) {
359
- target.animate( { 'margin-left': "+=" + ( offset = -offset ) + 'px' }, 50);
360
- }
361
- }
362
  });
1
+ jQuery(document).ready(function () {
2
+ jQuery('.cb-ids').change(function () {
3
+ if (jQuery(this).is(':checked')) {
4
+ jQuery(this).closest('tr').addClass('selected');
5
+ }
6
+ else {
7
+ jQuery(this).closest('tr').removeClass('selected');
8
+ }
9
+ });
10
+ jQuery('th input:checkbox').change(function () {
11
+ if (jQuery(this).is(':checked')) {
12
+ jQuery('#ThumbsTable tbody tr').addClass('selected');
13
+ }
14
+ else {
15
+ jQuery('#ThumbsTable tbody tr').removeClass('selected');
16
+ }
17
+ });
18
+ jQuery('input.current-page').bind('keypress', {}, function (e) {
19
+ var code = (e.keyCode ? e.keyCode : e.which);
20
+ if (code == 13) {//Enter keycode
21
+ e.preventDefault();
22
+ jQuery(location).attr('href', '?' + jQuery.param(jQuery.extend(URL_params, {sheet: this.value})));
23
+ }
24
+ });
25
+ jQuery('select.limit_per_page').change(function () {
26
+ jQuery(location).attr('href', '?' + jQuery.param(jQuery.extend(URL_params, {limit: this.value})));
27
+ });
28
+ jQuery('#tab-Thumbnail').submit(function (event) {
29
+ event.preventDefault();
30
+ if (jQuery('.cb-ids:checked').length > 0) {
31
+ var a = jQuery(this).attr('action');
32
+ var b = jQuery(this).serialize() +
33
+ '&document_gallery%5Bajax%5D=true' +
34
+ '&document_gallery%5Bcleanup%5D=true';
35
+ jQuery('.deleteSelected').addClass('waiting').attr('disabled', 'disabled');
36
+ jQuery.post(a, b, function (response) {
37
+ if (response.indexOf("\n") == -1) {
38
+ eval('var reply = ' + response + ';');
39
+ if (reply.result) {
40
+ var result = reply.deleted;
41
+ for (var index in result) {
42
+ jQuery('input[type=checkbox][value=' + result[index] + ']').closest('tr').fadeOut('slow', 0.00, function () {
43
+ jQuery(this).slideUp('slow', function () {
44
+ jQuery(this).remove();
45
+ });
46
+ });
47
+ }
48
+ }
49
+ } else {
50
+ console.log('Invalid response from server:');
51
+ console.log(response);
52
+ }
53
+ jQuery('.deleteSelected').removeClass('waiting').removeAttr('disabled');
54
+ }).fail(function () {
55
+ console.log('Problem in reaching the server');
56
+ jQuery('.deleteSelected').removeClass('waiting').removeAttr('disabled');
57
+ });
58
+ }
59
+ return false;
60
+ });
61
+
62
+ jQuery('#tab-Advanced #options-dump').click(function () {
63
+ jQuery(this).select();
64
+ });
65
+
66
+ function toggleSpoiler() {
67
+ var sel = getSelection().toString();
68
+ if (!sel) {
69
+ jQuery(this).next().slideToggle('slow');
70
+ jQuery(this).find('.dashicons').toggleClass('dashicons-arrow-down-alt2 dashicons-arrow-up-alt2');
71
+ jQuery(this).toggleClass('expander collapser');
72
+ jQuery(this).attr('title', (jQuery(this).hasClass('expander') ? 'Click to Expand' : 'Click to Collapse'));
73
+ }
74
+ }
75
 
76
+ jQuery('.expander').click(toggleSpoiler);
77
 
78
+ if (jQuery('.spoiler-body').length) {
79
+ jQuery('.expandAll, .collapseAll').addClass('button');
80
+ jQuery('.expandAll').click(function (e) {
81
+ e.preventDefault();
82
+ jQuery('.expander').trigger('click');
83
+ });
84
+ jQuery('.collapseAll').click(function (e) {
85
+ e.preventDefault();
86
+ jQuery('.collapser').trigger('click');
87
+ });
88
+ }
89
+
90
+ jQuery('.levelSelector input').change(function () {
91
+ if (jQuery(this).val() == 'all') {
92
+ jQuery('.levelSelector input').not("[value='all']").prop('checked', jQuery(this).is(':checked'));
93
+ jQuery(this).is(':checked') ? jQuery('#LogTable tbody tr').show() : jQuery('#LogTable tbody tr').hide();
94
+ }
95
+ else {
96
+ jQuery(this).is(':checked') ? jQuery('#LogTable tbody tr:has(span.' + jQuery(this).val() + ')').show() : jQuery('#LogTable tbody tr:has(span.' + jQuery(this).val() + ')').hide();
97
+ if ((jQuery('.levelSelector input:checked').not("[value='all']").length + 1) == jQuery('.levelSelector input[type="checkbox"]').length) {
98
+ jQuery('.levelSelector input[value="all"]').prop('checked', true);
99
+ } else {
100
+ jQuery('.levelSelector input[value="all"]').prop('checked', false);
101
+ }
102
+ }
103
+ });
104
 
105
+ jQuery('#LogTable .manage-column.column-date').click(function () {
106
+ jQuery(this).toggleClass('asc desc');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  var table = jQuery('#LogTable > tbody');
108
  var rows = table.children('tr');
109
+ table.append(rows.get().reverse());
110
+ });
111
 
112
+ function DragDropFilesStop(e) {
113
+ e = e || event;
114
+ if (e.dataTransfer.types) {
115
+ //Testing if dragenter/dragover Event Contains Files - http://css-tricks.com/snippets/javascript/test-if-dragenterdragover-event-contains-files/
116
+ for (var i = 0; i < e.dataTransfer.types.length; i++) {
117
+ if (e.dataTransfer.types[i] == 'Files') {
118
+ //Or maybe it is better just to test e.dataTransfer.files[0].name - http://stackoverflow.com/a/12622904/3951387
119
+ //NO: before drop the list is not accessible
120
+ e.stopPropagation();
121
+ e.preventDefault();
122
+ e.dataTransfer.dropEffect = 'none';
123
+ break;
124
+ }
125
  }
126
+ //jQuery way - Not working
127
+ /*if (jQuery.inArray('Files', e.dataTransfer.types)) {
128
+ e.stopPropagation();
129
+ e.preventDefault();
130
+ e.dataTransfer.dropEffect = 'none';
131
+ }*/
132
+ }
133
+ }
 
134
 
135
+ //Preventing browser from acting on drag&dropped files beside the dedicated areas
136
+ window.addEventListener('dragover', DragDropFilesStop, false);
137
+ window.addEventListener('drop', DragDropFilesStop, false);
138
 
139
+ function handleDragOver(e) {
140
+ e = e || event;
141
+ if (e.originalEvent.dataTransfer.types) {
142
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
143
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
144
+ e.stopPropagation();
145
+ e.preventDefault();
146
+ //Have to exploit broker to access standart properties while using jQuery to bind handlers - http://stackoverflow.com/a/14792183/3951387
147
+ e.originalEvent.dataTransfer.dropEffect = 'move';
148
+ return false;
149
+ }
150
  }
151
+ }
152
+ }
153
+
154
+ //Firing HTML5 DragLeave only when all the DragEnter'ed child elements were DragLeave'd - http://stackoverflow.com/a/21002544
155
+ //Had to change in favour of http://stackoverflow.com/questions/10253663/ because of issues with FireFox
156
+ var counter = {};
157
 
158
+ function handleDragEnter(e) {
159
+ // this / e.target is the current hover target.
160
+ e = e || event;
161
+ if (e.originalEvent.dataTransfer.types) {
162
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
163
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
164
+ this.classList.add('dragover');
165
+ counter[jQuery(this).data('entry')] = counter[jQuery(this).data('entry')].add(jQuery(e.target));
166
+ break;
167
+ }
 
168
  }
169
+ }
170
+ }
 
171
 
172
+ function handleDragLeave(e) {
173
+ e = e || event;
174
+ if (e.originalEvent.dataTransfer.types) {
175
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
176
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
177
+ counter[jQuery(this).data('entry')] = counter[jQuery(this).data('entry')].not(e.target);
178
+ if (counter[jQuery(this).data('entry')].length === 0) {
179
+ this.classList.remove('dragover');// this / e.target is previous target element.
180
+ }
181
+ break;
182
+ }
183
  }
184
+ }
185
+ }
 
186
 
187
+ function handleDrop(e) {
188
+ e = e || event;
189
+ if (e.originalEvent.dataTransfer.types) {
190
+ for (var i = 0; i < e.originalEvent.dataTransfer.types.length; i++) {
191
+ if (e.originalEvent.dataTransfer.types[i] == 'Files') {
192
 
193
+ e.stopPropagation();// Stops some browsers from redirecting.
194
+ e.preventDefault();
195
 
196
+ processFiles(e.originalEvent.dataTransfer.files, jQuery(this).data('entry'));
197
+ counter[jQuery(this).data('entry')] = jQuery();
198
+ this.classList.remove('dragover');
199
+ break;
200
+ }
201
  }
202
+ }
203
+ }
 
204
 
205
+ function handleBrowseButton(e) {
206
+ e = e || event;
207
+ processFiles(e.target.files, jQuery(this).closest('tr').data('entry'));
208
+ //Was thinking about purging input:file control - http://stackoverflow.com/questions/1043957/clearing-input-type-file-using-jquery
209
+ //Decided just to get rid of name properties thus such controls wouldn't be taken into consideration during form submit or processed with FormData
210
+ }
211
 
212
+ function processFiles(files, entry) {
213
+ for (var i = 0, f; f = files[i]; i++) {
214
+ //Processing only first qualifying file
215
+ if (f.type.indexOf('image/') == 0 && typeof dg_admin_vars.upload_limit != 'undefined' && f.size <= parseInt(dg_admin_vars.upload_limit)) {
216
+ var target, theRow = jQuery('[data-entry=' + entry + ']');
217
+ var formData = new FormData(theRow.closest('form')[0]);
218
+ if (typeof ajax_object != 'undefined' && typeof ajax_object.ajax_url != 'undefined') {
219
+ target = ajax_object.ajax_url;
220
+ formData.append('action', 'dg_upload_thumb');
221
+ } else {
222
+ target = jQuery('#tab-Thumbnail').attr('action');
223
+ }
224
+ formData.append('document_gallery[entry]', entry);
225
+ formData.append('document_gallery[ajax]', 'true');
226
+ formData.append('document_gallery[upload]', 'true');
227
+ formData.append('file', f);
228
+ var xhr = new XMLHttpRequest();
229
+ xhr.open('POST', target);
230
+ var theImg = theRow.find('.column-icon img');
231
+ var progressBarValue = theRow.find('.progress > *:first');
232
+ xhr.onreadystatechange = function () {
233
+ if (xhr.readyState == 4) {
234
+ if (xhr.status == 200 && xhr.responseText.indexOf("\n") == -1) {
235
+ jQuery(progressBarValue).parent().addClass('success');
236
+ eval('var response = ' + xhr.responseText + ';');
237
+ if (response.result) {
238
+ // check if generated thumbnail has the same url
239
+ if (response.url === theImg.attr('src')) {
240
+ theImg.attr('src', theImg.attr('src') + '?' + new Date().getTime());
241
+ } else {
242
+ theImg.attr('src', response.url);
243
+ }
244
+ }
245
  } else {
246
+ jQuery(progressBarValue).parent().addClass('fail');
247
+ console.log('Invalid response from server:');
248
+ console.log(xhr.responseText);
249
  }
250
+ setTimeout(function () {
251
+ theRow.find('.column-thumbupload > *').toggleClass('invis');
252
+ jQuery(progressBarValue).parent().removeClass('success fail');
253
+ }, 5000);
254
+ }
255
+ };
256
+ xhr.onload = function () {
257
+ progressBarValue.width('100%');
258
+ };
259
+ xhr.upload.onprogress = function (event) {
260
+ if (event.lengthComputable) {
261
+ var complete = (event.loaded / event.total * 100 | 0);
262
+ progressBarValue.width(complete + '%');
263
+ }
264
+ };
265
+ progressBarValue.width(0);
266
+ theRow.find('.column-thumbupload > *').toggleClass('invis');
267
+ xhr.send(formData);
268
+ break;
269
+ } else {
270
+ console.log('Attempt to upload improper file %c' + f.name + ':', 'font-weight:bold;');
271
+ if (f.type.indexOf('image/') != 0) {
272
+ console.log('\tIs not an image - ' + f.type);
273
+ }
274
+ if (typeof dg_admin_vars.upload_limit != 'undefined' && f.size > parseInt(dg_admin_vars.upload_limit)) {
275
+ console.log('\tIs too big - ' + f.size + 'b; (Limit is ' + dg_admin_vars.upload_limit + 'b)');
276
+ }
277
  }
278
+ }
279
+ }
 
 
 
280
 
281
+ // Prepairing all the drop-zones on page load
282
+ jQuery('#ThumbsTable tbody tr').each(function () {
283
+ jQuery(this)
284
+ .on('dragenter', handleDragEnter)
285
+ .on('dragover', handleDragOver)
286
+ .on('dragleave', handleDragLeave)
287
+ .on('drop', handleDrop);
288
+ counter[jQuery(this).data('entry')] = jQuery();
289
+ jQuery(this).find('input:button').on('click', function () {
290
  jQuery(this).prevAll('input:file').click();
291
+ });
292
+ jQuery(this).find('input:file').on('change', handleBrowseButton);
293
+ });
294
+
295
+ //Checking Drag&Drop support
296
+ //Structure is Not supported in Chrome's WebKit
297
+ /*if (!('files' in DataTransfer.prototype)) {//Determine presence of HTML5 drag'n'drop file upload API - http://stackoverflow.com/a/2312859/3951387
298
+ jQuery('.html5dndmarker').hide();
299
+ }*/
300
+ if (!('draggable' in document.createElement('span'))) {
301
+ jQuery('.html5dndmarker').hide();
302
+ }
303
 
304
+ jQuery('td .dashicons-edit').click(function () {
305
+ var cell = jQuery(this).closest('td');
306
+ if (cell.hasClass('column-title')) {
307
+ if (cell.find('a').css('display') == 'none') {
308
+ return;
309
+ }
310
+ cell.find('a').hide().after('<input type="text" value="' + cell.find('.editable-title').text() + '">');
311
+ cell.find('input').focus();
312
+ } else if (cell.hasClass('column-description')) {
313
+ if (cell.find('.editable-description').css('display') == 'none') {
314
+ return;
315
+ }
316
+ cell.find('.editable-description').hide().after('<textarea>' + cell.find('.editable-description').text() + '</textarea>');
317
+ cell.find('textarea').focus();
318
+ } else {
319
  return;
320
+ }
321
+ jQuery(this).css('visibility', 'hidden');
322
+ cell.find('.edit-controls').show();
323
+ });
324
+ jQuery('.edit-controls .dashicons-no').click(function () {
325
+ var cell = jQuery(this).closest('td');
326
+ if (cell.hasClass('column-title')) {
327
+ if (cell.find('a').css('display') != 'none') {
328
+ return;
329
+ }
330
+ cell.find('input').fadeOut('fast', function () {
331
+ jQuery(this).remove();
332
+ cell.find('a').fadeIn('fast');
333
+ });
334
+ } else if (cell.hasClass('column-description')) {
335
+ if (cell.find('.editable-description').css('display') != 'none') {
336
+ return;
337
+ }
338
+ cell.find('textarea').fadeOut('fast', function () {
339
+ jQuery(this).remove();
340
+ cell.find('.editable-description').fadeIn('fast');
341
+ });
342
+ } else {
343
  return;
344
+ }
345
+ jQuery(this).closest('.edit-controls').hide();
346
+ cell.find('.dashicons-edit').css('visibility', 'visible');
347
+ });
348
+ jQuery('.edit-controls .dashicons-yes').click(function () {
349
+ var cell = jQuery(this).closest('td');
350
+ var entry = jQuery(this).closest('tr').data('entry');
351
+ var target = jQuery('#tab-Thumbnail').attr('action');
352
+ var formData = new FormData(jQuery('[data-entry=' + entry + ']').closest('form')[0]);
353
+ formData.append('document_gallery[entry]', entry);
354
+ formData.append('document_gallery[ajax]', 'true');
355
+ var newContent, updateGoal;
356
+ if (cell.hasClass('column-title')) {
357
+ newContent = cell.find('input');
358
+ updateGoal = cell.find('.editable-title');
359
+ formData.append('document_gallery[title]', encodeURIComponent(newContent.val()));
360
+ } else if (cell.hasClass('column-description')) {
361
+ newContent = cell.find('textarea');
362
+ updateGoal = cell.find('.editable-description');
363
+ formData.append('document_gallery[description]', encodeURIComponent(newContent.val()));
364
+ } else {
365
  return;
366
+ }
367
+ if (newContent.val() == updateGoal.text()) {
368
+ jQuery(this).next('.dashicons-no').click();
 
369
  return;
370
+ }
371
+ var xhr = new XMLHttpRequest();
372
+ xhr.open('POST', target);
373
+ xhr.onreadystatechange = function () {
374
+ if (xhr.readyState == 4) {
375
+ cell.addClass('trans');
376
+ cell.find('.edit-controls').removeClass('waiting');
377
+ if (xhr.responseText.indexOf("\n") == -1) {
378
+ eval('var response = ' + xhr.responseText + ';');
379
+ if (response.result) {
380
+ updateGoal.text(newContent.val());
381
+ cell.find('.dashicons-no').click();
382
+ cell.addClass('responseSuccess').delay(2000).queue(function () {
383
+ jQuery(this).removeClass('responseSuccess').dequeue();
384
+ }).delay(1100).queue(function () {
385
+ jQuery(this).removeClass('trans').dequeue();
386
+ });
387
+ } else {
388
+ shake(newContent);
389
+ cell.addClass('responseFail').delay(1100).queue(function () {
390
+ jQuery(this).removeClass('responseFail').dequeue();
391
+ }).delay(1100).queue(function () {
392
+ jQuery(this).removeClass('trans').dequeue();
393
+ });
394
+ }
395
+ } else {
396
+ shake(newContent);
397
+ cell.addClass('responseFail').delay(1100).queue(function () {
398
+ jQuery(this).removeClass('responseFail').dequeue();
399
+ }).delay(1100).queue(function () {
400
+ jQuery(this).removeClass('trans').dequeue();
401
+ });
402
+ console.log('Invalid response from server:');
403
+ console.log(xhr.responseText);
404
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
405
  }
406
+ };
407
+ jQuery(this).closest('.edit-controls').addClass('waiting');
408
+ xhr.send(formData);
409
+ });
410
+ function shake(target) {
411
+ var offset = 4;
412
+ for (var i = 0; i < 6; i++) {
413
+ target.animate({'margin-left': "+=" + ( offset = -offset ) + 'px'}, 50);
414
+ }
415
+ }
416
  });
assets/js/media_manager.js CHANGED
@@ -1,385 +1,385 @@
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
15
- * @augments wp.media.controller.State
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,
52
- idealColumnWidth: 170,
53
- editing: false,
54
- priority: 60,
55
- syncSelection: false
56
- },
57
-
58
- /**
59
- * @since 3.5.0
60
- */
61
- initialize: function() {
62
- // If we haven't been provided a `library`, create a `Selection`.
63
- if ( ! this.get('library') )
64
- this.set( 'library', new media.model.Selection() );
65
-
66
- // The single `Attachment` view to be used in the `Attachments` view.
67
- if ( ! this.get('AttachmentView') )
68
- this.set( 'AttachmentView', media.view.Attachment.EditLibrary );
69
- media.controller.Library.prototype.initialize.apply( this, arguments );
70
- },
71
-
72
- /**
73
- * @since 3.5.0
74
- */
75
- activate: function() {
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
- },
88
-
89
- /**
90
- * @since 3.5.0
91
- */
92
- deactivate: function() {
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
- },
100
-
101
- /**
102
- * @since 3.5.0
103
- *
104
- * @param browser
105
- */
106
- gallerySettings: function( browser ) {
107
- if ( ! this.get('displaySettings') ) {
108
- return;
109
- }
110
-
111
- var library = this.get('library');
112
-
113
- if ( ! library || ! browser ) {
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
- });
126
-
127
- browser.toolbar.set( 'reverse', {
128
- text: l10n.reverseOrder,
129
- priority: 80,
130
-
131
- click: function() {
132
- library.reset( library.toArray().reverse() );
133
- }
134
- });
135
- }
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
143
- * @augments wp.media.controller.State
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 ),
178
-
179
- /**
180
- * @since 3.5.0
181
- */
182
- initialize: function() {
183
- // If a library wasn't supplied, create a library of images.
184
- if ( ! this.get('library') )
185
- this.set( 'library', media.query() );
186
-
187
- media.controller.Library.prototype.initialize.apply( this, arguments );
188
- },
189
-
190
- /**
191
- * @since 3.5.0
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
- };
205
-
206
- // Reset the library to ensure that all attachments are re-added
207
- // to the collection. Do so silently, as calling `observe` will
208
- // trigger the `reset` event.
209
- library.reset( library.mirroring.models, { silent: true });
210
- library.observe( edit );
211
- this.editLibrary = edit;
212
-
213
- media.controller.Library.prototype.activate.apply( this, arguments );
214
- }
215
- });
216
-
217
- /**
218
- * wp.media.view.Settings.DocumentGallery
219
- *
220
- * @class
221
- * @augments wp.media.view.Settings
222
- * @augments wp.media.View
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
- {
235
- initialize: function() {
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,
246
-
247
- library: media.query( this.options.library )
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 ) {
280
- frame.setState( previous );
281
- } else {
282
- frame.close();
283
- }
284
-
285
- // Keep focus inside media modal
286
- // after canceling a gallery
287
- this.controller.modal.focusManager.focus();
288
- }
289
- },
290
- separateCancel: new media.View({
291
- className: 'separator',
292
- priority: 40
293
- })
294
- });
295
- },
296
-
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
-
338
- /**
339
- * @fires wp.media.controller.State#update
340
- */
341
- click: function() {
342
- var controller = this.controller,
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
-
368
- /**
369
- * @fires wp.media.controller.State#reset
370
- */
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
+ ( 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
15
+ * @augments wp.media.controller.State
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,
52
+ idealColumnWidth: 170,
53
+ editing: false,
54
+ priority: 60,
55
+ syncSelection: false
56
+ },
57
+
58
+ /**
59
+ * @since 3.5.0
60
+ */
61
+ initialize: function () {
62
+ // If we haven't been provided a `library`, create a `Selection`.
63
+ if (!this.get('library'))
64
+ this.set('library', new media.model.Selection());
65
+
66
+ // The single `Attachment` view to be used in the `Attachments` view.
67
+ if (!this.get('AttachmentView'))
68
+ this.set('AttachmentView', media.view.Attachment.EditLibrary);
69
+ media.controller.Library.prototype.initialize.apply(this, arguments);
70
+ },
71
+
72
+ /**
73
+ * @since 3.5.0
74
+ */
75
+ activate: function () {
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
+ },
88
+
89
+ /**
90
+ * @since 3.5.0
91
+ */
92
+ deactivate: function () {
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
+ },
100
+
101
+ /**
102
+ * @since 3.5.0
103
+ *
104
+ * @param browser
105
+ */
106
+ gallerySettings: function (browser) {
107
+ if (!this.get('displaySettings')) {
108
+ return;
109
+ }
110
+
111
+ var library = this.get('library');
112
+
113
+ if (!library || !browser) {
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
+ });
126
+
127
+ browser.toolbar.set('reverse', {
128
+ text: l10n.reverseOrder,
129
+ priority: 80,
130
+
131
+ click: function () {
132
+ library.reset(library.toArray().reverse());
133
+ }
134
+ });
135
+ }
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
143
+ * @augments wp.media.controller.State
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),
178
+
179
+ /**
180
+ * @since 3.5.0
181
+ */
182
+ initialize: function () {
183
+ // If a library wasn't supplied, create a library of images.
184
+ if (!this.get('library'))
185
+ this.set('library', media.query());
186
+
187
+ media.controller.Library.prototype.initialize.apply(this, arguments);
188
+ },
189
+
190
+ /**
191
+ * @since 3.5.0
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
+ };
205
+
206
+ // Reset the library to ensure that all attachments are re-added
207
+ // to the collection. Do so silently, as calling `observe` will
208
+ // trigger the `reset` event.
209
+ library.reset(library.mirroring.models, {silent: true});
210
+ library.observe(edit);
211
+ this.editLibrary = edit;
212
+
213
+ media.controller.Library.prototype.activate.apply(this, arguments);
214
+ }
215
+ });
216
+
217
+ /**
218
+ * wp.media.view.Settings.DocumentGallery
219
+ *
220
+ * @class
221
+ * @augments wp.media.view.Settings
222
+ * @augments wp.media.View
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
+ {
235
+ initialize: function () {
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,
246
+
247
+ library: media.query(this.options.library)
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) {
280
+ frame.setState(previous);
281
+ } else {
282
+ frame.close();
283
+ }
284
+
285
+ // Keep focus inside media modal
286
+ // after canceling a gallery
287
+ this.controller.modal.focusManager.focus();
288
+ }
289
+ },
290
+ separateCancel: new media.View({
291
+ className: 'separator',
292
+ priority: 40
293
+ })
294
+ });
295
+ },
296
+
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
+
338
+ /**
339
+ * @fires wp.media.controller.State#update
340
+ */
341
+ click: function () {
342
+ var controller = this.controller,
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
+
368
+ /**
369
+ * @fires wp.media.controller.State#reset
370
+ */
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, _));
document-gallery.php CHANGED
@@ -1,30 +1,30 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /*
5
  Plugin Name: Document Gallery
6
  Plugin URI: http://wordpress.org/extend/plugins/document-gallery/
7
  Description: Display non-images (and images) in gallery format on a page or post with the [dg] shortcode.
8
- Version: 3.2
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.2');
16
 
17
  // define helper paths & URLs
18
- define('DG_BASENAME', plugin_basename(__FILE__));
19
- define('DG_URL', plugin_dir_url(__FILE__));
20
- define('DG_PATH', plugin_dir_path(__FILE__));
21
- define('DG_WPINC_PATH', ABSPATH . WPINC . '/');
22
- define('DG_WPADMIN_PATH', ABSPATH . 'wp-admin/');
23
 
24
  // init DG options for use throughout plugin
25
  global $dg_options;
26
- define('DG_OPTION_NAME', 'document_gallery');
27
- $dg_options = get_option(DG_OPTION_NAME, null);
28
 
29
  // core functionality
30
  include_once DG_PATH . 'inc/class-document-gallery.php';
@@ -34,58 +34,61 @@ include_once DG_PATH . 'inc/class-util.php';
34
 
35
  // logging functionality
36
  include_once DG_PATH . 'inc/class-logger.php';
37
- add_action(DG_Logger::PurgeLogsAction, array('DG_Logger', 'purgeExpiredEntries'));
38
 
39
  // handle activation, updates, and uninstallation
40
  include_once DG_PATH . 'inc/class-setup.php';
41
- register_activation_hook(__FILE__, array('DG_Setup', 'activate'));
42
- add_action('wpmu_new_blog', array('DG_Setup','activateNewBlog'));
43
- register_uninstall_hook(__FILE__, array('DG_Setup', 'uninstall'));
44
  DG_Setup::maybeUpdate();
45
 
46
  // validate options if desired
47
- if ($dg_options['validation']) {
48
- add_action('init', array('DocumentGallery', 'addValidation'));
49
  }
50
 
51
  // I18n
52
- add_action('plugins_loaded', array('DocumentGallery', 'loadTextDomain'));
53
 
54
  // cleanup cached data when thumbed attachment deleted
55
  include_once DG_PATH . 'inc/class-thumber.php';
56
- add_action('delete_attachment', array('DG_Thumber', 'deleteThumbMeta'));
57
-
58
- if (is_admin()) {
59
- // admin house keeping
60
- include_once DG_PATH . 'admin/class-admin.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);
65
-
66
- // build options page
67
- add_action('admin_menu', array('DG_Admin', 'addAdminPage'));
68
-
69
- // add meta box for managing thumbnail generation to attachment Edit Media page
70
- add_action('add_meta_boxes', array('DG_Admin', 'addMetaBox'));
71
- add_action('wp_ajax_dg_upload_thumb', array('DG_Admin', 'saveMetaBox'));
72
-
73
- // Media Manager integration
74
- add_action('admin_print_footer_scripts', array('DG_Admin', 'loadCustomTemplates')); //wp_print_scripts || wp_footer
75
-
76
- if (DG_Admin::doRegisterSettings()) {
77
- add_action('admin_init', array('DG_Admin', 'registerSettings'));
78
- }
 
 
 
79
  } else {
80
- // styling for gallery
81
- if (apply_filters('dg_use_default_gallery_style', true )) {
82
- add_action('wp_enqueue_scripts', array('DocumentGallery', 'enqueueGalleryStyle'));
83
- }
84
- add_action('wp_print_scripts', array('DocumentGallery', 'printCustomStyle'));
85
  }
86
 
87
  // adds 'dg' shortcode
88
- add_shortcode('dg', array('DocumentGallery', 'doShortcode'));
89
 
90
  // public API for developers
91
  include_once DG_PATH . 'inc/class-api.php';
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /*
5
  Plugin Name: Document Gallery
6
  Plugin URI: http://wordpress.org/extend/plugins/document-gallery/
7
  Description: Display non-images (and images) in gallery format on a page or post with the [dg] shortcode.
8
+ Version: 3.3
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.3' );
16
 
17
  // define helper paths & URLs
18
+ define( 'DG_BASENAME', plugin_basename( __FILE__ ) );
19
+ define( 'DG_URL', plugin_dir_url( __FILE__ ) );
20
+ define( 'DG_PATH', plugin_dir_path( __FILE__ ) );
21
+ define( 'DG_WPINC_PATH', ABSPATH . WPINC . '/' );
22
+ define( 'DG_WPADMIN_PATH', ABSPATH . 'wp-admin/' );
23
 
24
  // init DG options for use throughout plugin
25
  global $dg_options;
26
+ define( 'DG_OPTION_NAME', 'document_gallery' );
27
+ $dg_options = get_option( DG_OPTION_NAME, null );
28
 
29
  // core functionality
30
  include_once DG_PATH . 'inc/class-document-gallery.php';
34
 
35
  // logging functionality
36
  include_once DG_PATH . 'inc/class-logger.php';
37
+ add_action( DG_Logger::PurgeLogsAction, array( 'DG_Logger', 'purgeExpiredEntries' ) );
38
 
39
  // handle activation, updates, and uninstallation
40
  include_once DG_PATH . 'inc/class-setup.php';
41
+ register_activation_hook( __FILE__, array( 'DG_Setup', 'activate' ) );
42
+ add_action( 'wpmu_new_blog', array( 'DG_Setup', 'activateNewBlog' ) );
43
+ register_uninstall_hook( __FILE__, array( 'DG_Setup', 'uninstall' ) );
44
  DG_Setup::maybeUpdate();
45
 
46
  // validate options if desired
47
+ if ( $dg_options['validation'] ) {
48
+ add_action( 'init', array( 'DocumentGallery', 'addValidation' ) );
49
  }
50
 
51
  // I18n
52
+ add_action( 'plugins_loaded', array( 'DocumentGallery', 'loadTextDomain' ) );
53
 
54
  // cleanup cached data when thumbed attachment deleted
55
  include_once DG_PATH . 'inc/class-thumber.php';
56
+ add_action( 'delete_attachment', array( 'DG_Thumber', 'deleteThumbMeta' ) );
57
+
58
+ if ( is_admin() ) {
59
+ // admin house keeping
60
+ include_once DG_PATH . 'admin/class-admin.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 );
65
+
66
+ // build options page
67
+ add_action( 'admin_menu', array( 'DG_Admin', 'addAdminPage' ) );
68
+
69
+ // add meta box for managing thumbnail generation to attachment Edit Media page
70
+ add_action( 'add_meta_boxes', array( 'DG_Admin', 'addMetaBox' ) );
71
+ add_action( 'wp_ajax_dg_upload_thumb', array( 'DG_Admin', 'saveMetaBox' ) );
72
+
73
+ // Media Manager integration
74
+ add_action( 'admin_print_footer_scripts', array(
75
+ 'DG_Admin',
76
+ 'loadCustomTemplates'
77
+ ) ); //wp_print_scripts || wp_footer
78
+
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 ) ) {
85
+ add_action( 'wp_enqueue_scripts', array( 'DocumentGallery', 'enqueueGalleryStyle' ) );
86
+ }
87
+ add_action( 'wp_print_scripts', array( 'DocumentGallery', 'printCustomStyle' ) );
88
  }
89
 
90
  // adds 'dg' shortcode
91
+ add_shortcode( 'dg', array( 'DocumentGallery', 'doShortcode' ) );
92
 
93
  // public API for developers
94
  include_once DG_PATH . 'inc/class-api.php';
inc/class-api.php CHANGED
@@ -1,17 +1,17 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /**
5
  * All methods within this class may be considered "documented" and
6
  * developers may rely on their existence moving forward. If there is
7
  * a need to remove a method in this class, it will go through a
8
  * deprecation period before being removed.
9
- *
10
  * This guarantee is only provided for this file and class-logger. All
11
  * other methods throughout Document Gallery may be removed or
12
  * significantly changed from version-to-version and should not be relied
13
  * on in any way for external development.
14
- *
15
  * NOTE: If you are performing actions related to Document Gallery, you
16
  * you are encouraged to log important events through the Document Gallery
17
  * logging interface. This interface is available under the DG_Logger class.
@@ -19,55 +19,61 @@ defined('WPINC') OR exit;
19
  * @author drossiter
20
  */
21
  class DG_API {
22
- /**
23
- * Sets the thumbnail for the given attachment ID.
24
- *
25
- * @param int $ID Document ID.
26
- * @param string $path System path to thumbnail.
27
- * @param unknown $generator Descriptor for generation method -- usually method name.
28
- * @return bool Whether set was successful.
29
- */
30
- public static function setThumbnail($ID, $path, $generator = 'unknown') {
31
- include_once DG_PATH . 'inc/class-thumber.php';
32
- return DG_Thumber::setThumbnail($ID, $path, $generator);
33
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
- /**
36
- * Sets the thumbnail for the given attachment ID to a failed state.
37
- * This prevents the plugin attempting to generate a thumbnail for this
38
- * plugin in the future.
39
- *
40
- * @param int $ID The attachment ID.
41
- */
42
- public static function setThumbnailFailed($ID) {
43
- include_once DG_PATH . 'inc/class-thumber.php';
44
- return DG_Thumber::setThumbnailFailed($ID);
45
- }
46
-
47
- /**
48
- *
49
- * @param int $ID The attachment ID.
50
- * @param number $pg The page number to use (1-based numbering).
51
- * @param string $generate_if_missing Whether to generate the thumbnail if it has not
52
- * yet been generated.
53
- * @return string The URL for the thumbnail NULL. Note that if generate_if_missing
54
- * is true then you will never get NULL -- you will get a default icon if generation fails.
55
- */
56
- public static function getThumbnail($ID, $pg = 1, $generate_if_missing = false) {
57
- include_once DG_PATH . 'inc/class-thumber.php';
58
- return DG_Thumber::getThumbnail($ID, $pg, $generate_if_missing);
59
- }
60
-
61
- /**
62
- * Removes all metadata related to a thumbnail for the given attachment ID(s). This allows
63
- * the plugin to attempt to re-generate the thumbnail for this attachment next time it
64
- * is requested in a gallery or through some other means.
65
- *
66
- * @param int|array $ids Which thumbnails to delete.
67
- * @return array All IDs that were deleted -- some subset of IDs requested to be deleted.
68
- */
69
- public static function deleteThumbnails($ids) {
70
- include_once DG_PATH . 'inc/class-thumber.php';
71
- return DG_Thumber::deleteThumbMeta($ids);
72
- }
73
  }
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /**
5
  * All methods within this class may be considered "documented" and
6
  * developers may rely on their existence moving forward. If there is
7
  * a need to remove a method in this class, it will go through a
8
  * deprecation period before being removed.
9
+ *
10
  * This guarantee is only provided for this file and class-logger. All
11
  * other methods throughout Document Gallery may be removed or
12
  * significantly changed from version-to-version and should not be relied
13
  * on in any way for external development.
14
+ *
15
  * NOTE: If you are performing actions related to Document Gallery, you
16
  * you are encouraged to log important events through the Document Gallery
17
  * logging interface. This interface is available under the DG_Logger class.
19
  * @author drossiter
20
  */
21
  class DG_API {
22
+ /**
23
+ * Sets the thumbnail for the given attachment ID.
24
+ *
25
+ * @param int $ID Document ID.
26
+ * @param string $path System path to thumbnail.
27
+ * @param string $generator Descriptor for generation method -- usually method name.
28
+ *
29
+ * @return bool Whether set was successful.
30
+ */
31
+ public static function setThumbnail( $ID, $path, $generator = 'unknown' ) {
32
+ include_once DG_PATH . 'inc/class-thumber.php';
33
+
34
+ return DG_Thumber::setThumbnail( $ID, $path, $generator );
35
+ }
36
+
37
+ /**
38
+ * Sets the thumbnail for the given attachment ID to a failed state.
39
+ * This prevents the plugin attempting to generate a thumbnail for this
40
+ * plugin in the future.
41
+ *
42
+ * @param int $ID The attachment ID.
43
+ */
44
+ public static function setThumbnailFailed( $ID ) {
45
+ include_once DG_PATH . 'inc/class-thumber.php';
46
+
47
+ return DG_Thumber::setThumbnailFailed( $ID );
48
+ }
49
+
50
+ /**
51
+ *
52
+ * @param int $ID The attachment ID.
53
+ * @param int $pg The page number to use (1-based numbering).
54
+ * @param bool $generate_if_missing Whether to generate the thumbnail if it has not yet been generated.
55
+ *
56
+ * @return string The URL for the thumbnail NULL. Note that if generate_if_missing
57
+ * is true then you will never get NULL -- you will get a default icon if generation fails.
58
+ */
59
+ public static function getThumbnail( $ID, $pg = 1, $generate_if_missing = false ) {
60
+ include_once DG_PATH . 'inc/class-thumber.php';
61
+
62
+ return DG_Thumber::getThumbnail( $ID, $pg, $generate_if_missing );
63
+ }
64
+
65
+ /**
66
+ * Removes all metadata related to a thumbnail for the given attachment ID(s). This allows
67
+ * the plugin to attempt to re-generate the thumbnail for this attachment next time it
68
+ * is requested in a gallery or through some other means.
69
+ *
70
+ * @param int|array $ids Which thumbnails to delete.
71
+ *
72
+ * @return array All IDs that were deleted -- some subset of IDs requested to be deleted.
73
+ */
74
+ public static function deleteThumbnails( $ids ) {
75
+ include_once DG_PATH . 'inc/class-thumber.php';
76
 
77
+ return DG_Thumber::deleteThumbMeta( $ids );
78
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  }
inc/class-document-gallery.php CHANGED
@@ -6,246 +6,184 @@
6
  * @author drossiter
7
  */
8
  class DocumentGallery {
9
-
10
- /*==========================================================================
11
- * THE SHORTCODE
12
- *=========================================================================*/
13
-
14
- /**
15
- * Takes values passed from attributes and returns sutable HTML to represent
16
- * all valid attachments requested.
17
- *
18
- * @param multitype:string $atts Arguments from the user.
19
- * @return string HTML for the Document Gallery.
20
- */
21
- public static function doShortcode($atts) {
22
- include_once DG_PATH . 'inc/class-gallery.php';
23
-
24
- $start = microtime(true);
25
- $gallery = (string)new DG_Gallery($atts);
26
- DG_Logger::writeLog(DG_LogLevel::Detail, 'Generation Time: ' . sprintf('%.2f', (microtime(true) - $start)) . ' s');
27
-
28
- return $gallery;
29
- }
30
-
31
- /**
32
- * Enqueue standard DG CSS.
33
- */
34
- public static function enqueueGalleryStyle() {
35
- wp_enqueue_style('document-gallery', DG_URL . 'assets/css/style.css', null, DG_VERSION);
36
- }
37
-
38
- /**
39
- * Prints user's custom CSS.
40
- */
41
- public static function printCustomStyle() {
42
- global $dg_options;
43
-
44
- if (!empty($dg_options['css']['minified'])) {
45
- echo "<style type='text/css'>{$dg_options['css']['minified']}</style>" . PHP_EOL;
46
- }
47
- }
48
-
49
- /*==========================================================================
50
- * I18n
51
- *=========================================================================*/
52
-
53
- /**
54
- * Loads language files into WP core.
55
- */
56
- public static function loadTextDomain() {
57
- load_plugin_textdomain('document-gallery', false, dirname(DG_BASENAME) . '/languages/');
58
- }
59
-
60
- /*==========================================================================
61
- * HELPER FUNCTIONS
62
- *=========================================================================*/
63
-
64
- /**
65
- * @param int $blog ID of the blog to be retrieved in multisite env.
66
- * @return multitype:unknown Options for the blog.
67
- */
68
- public static function getOptions($blog = null) {
69
- global $dg_options;
70
- return is_null($blog)
71
- ? $dg_options
72
- : get_blog_option($blog, DG_OPTION_NAME, null);
73
- }
74
-
75
- /**
76
- * @param multitype:unknown $options
77
- * @param int $blog ID of the blog to be set in multisite env.
78
- */
79
- public static function setOptions($options, $blog = null) {
80
- if (is_null($blog)) {
81
- global $dg_options;
82
- update_option(DG_OPTION_NAME, $options);
83
- $dg_options = $options;
84
- } else {
85
- update_blog_option($blog, DG_OPTION_NAME, $options);
86
- }
87
- }
88
-
89
- /**
90
- * @param int $blog ID of the blog to be deleted in multisite env.
91
- */
92
- public static function deleteOptions($blog = null) {
93
- if (is_null($blog)) {
94
- delete_option(DG_OPTION_NAME);
95
- } else {
96
- delete_blog_option($blog, DG_OPTION_NAME);
97
- }
98
- }
99
-
100
- /**
101
- * Adds hook to validate DG options every time save is attempted.
102
- */
103
- public static function addValidation() {
104
- add_filter('pre_update_option_' . DG_OPTION_NAME, array('DocumentGallery', 'validateOptionsStructure'), 10, 2);
105
- }
106
-
107
- /**
108
- * Checks whether the given options match the option schema.
109
- * @param multivar $new The new options to be validated.
110
- * @param multivar $old The old options.
111
- * @return array The options to be saved.
112
- */
113
- public static function validateOptionsStructure($new, $old) {
114
- if (self::isValidOptionsStructure($new)) {
115
- $ret = $new;
116
- } else {
117
- $ret = $old;
118
- DG_Logger::writeLog(DG_LogLevel::Error, 'Attempted to save invalid options.' . PHP_EOL . print_r($new, true), true, true);
119
- }
120
-
121
- return $ret;
122
- }
123
-
124
- /**
125
- * @param multivar|unknown $o The options structure to validate.
126
- * @param multivar $schema The schema to validate against (note that only keys matter -- non-array values are ignored).
127
- * @return bool Whether the given options structure matches the schema.
128
- */
129
- private static function isValidOptionsStructure($o, $schema = null) {
130
- if (is_null($schema)) {
131
- $schema = DG_Setup::getDefaultOptions(true);
132
- }
133
-
134
- // simple checks first
135
- $valid = is_array($o) && (count($schema) === count($o));
136
-
137
- if ($valid) {
138
- foreach ($schema as $sk => $sv) {
139
- $valid = array_key_exists($sk, $o);
140
- if (is_array($sv) && !empty($sv)) {
141
- $valid = $valid && self::isValidOptionsStructure($o[$sk], $sv);
142
- }
143
-
144
- if (!$valid) {
145
- break;
146
- }
147
- }
148
- }
149
-
150
- return $valid;
151
- }
152
-
153
- /**
154
- * Function takes a GMT timestamp and returns a date/time string in the
155
- * current timezone and WP format.
156
- * @param int $timestamp The GMT timestamp to translate.
157
- * @return string The local time in the WP date/time format.
158
- */
159
- public static function localDateTimeFromTimestamp($timestamp) {
160
- static $gmt_offet = null;
161
- static $wp_date_format = null;
162
- static $wp_time_format = null;
163
- if (is_null($gmt_offet)) {
164
- $gmt_offet = get_option('gmt_offset');
165
- $wp_date_format = get_option('date_format');
166
- $wp_time_format = get_option('time_format');
167
- }
168
-
169
- 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>';
170
- }
171
-
172
- /**
173
- * Compiles any custom CSS, including minification and escaping HTML.
174
- * @param string $custom The custom CSS to compile.
175
- * @return string Compiled CSS.
176
- */
177
- public static function compileCustomCss($custom) {
178
- $css = str_replace('&gt;', '>', esc_html($custom));
179
- return self::minifyCss($css);
180
- }
181
-
182
- /**
183
- * Minifies CSS string.
184
- * Source: http://stackoverflow.com/a/15195752/866618
185
- */
186
- private static function minifyCss($css) {
187
- # remove comments first (simplifies the other regex)
188
- $re1 = <<<EOS
189
- (?sx)
190
- # quotes
191
- (
192
- "(?:[^"\\\\]++|\\.)*+"
193
- | '(?:[^'\\\\]++|\\.)*+'
194
- )
195
- |
196
- # comments
197
- /\* (?> .*? \*/ )
198
- EOS;
199
-
200
- $re2 = <<<EOS
201
- (?six)
202
- # quotes
203
- (
204
- "(?:[^"\\\\]++|\\.)*+"
205
- | '(?:[^'\\\\]++|\\.)*+'
206
- )
207
- |
208
- # ; before } (and the spaces after it while we're here)
209
- \s*+ ; \s*+ ( } ) \s*+
210
- |
211
- # all spaces around meta chars/operators
212
- \s*+ ( [*$~^|]?+= | [{};,>~+-] | !important\b ) \s*+
213
- |
214
- # spaces right of ( [ :
215
- ( [[(:] ) \s++
216
- |
217
- # spaces left of ) ]
218
- \s++ ( [])] )
219
- |
220
- # spaces left (and right) of :
221
- \s++ ( : ) \s*+
222
- # but not in selectors: not followed by a {
223
- (?!
224
- (?>
225
- [^{}"']++
226
- | "(?:[^"\\\\]++|\\.)*+"
227
- | '(?:[^'\\\\]++|\\.)*+'
228
- )*+
229
- {
230
- )
231
- |
232
- # spaces at beginning/end of string
233
- ^ \s++ | \s++ \z
234
- |
235
- # double spaces to single
236
- (\s)\s+
237
- EOS;
238
-
239
- $css = preg_replace("%$re1%", '$1', $css);
240
- return preg_replace("%$re2%", '$1$2$3$4$5$6$7', $css);
241
- }
242
-
243
- /**
244
- * Blocks instantiation. All functions are static.
245
- */
246
- private function __construct() {
247
-
248
- }
249
- }
250
-
251
- ?>
6
  * @author drossiter
7
  */
8
  class DocumentGallery {
9
+
10
+ /*==========================================================================
11
+ * THE SHORTCODE
12
+ *=========================================================================*/
13
+
14
+ /**
15
+ * Takes values passed from attributes and returns suitable HTML to represent
16
+ * all valid attachments requested.
17
+ *
18
+ * @param array $atts Arguments from the user.
19
+ *
20
+ * @return string HTML for the Document Gallery.
21
+ */
22
+ public static function doShortcode( $atts ) {
23
+ include_once DG_PATH . 'inc/class-gallery.php';
24
+
25
+ $start = microtime( true );
26
+ $gallery = (string) new DG_Gallery( $atts );
27
+ DG_Logger::writeLog( DG_LogLevel::Detail, 'Generation Time: ' . sprintf( '%.2f', ( microtime( true ) - $start ) ) . ' s' );
28
+
29
+ return $gallery;
30
+ }
31
+
32
+ /**
33
+ * Enqueue standard DG CSS.
34
+ */
35
+ public static function enqueueGalleryStyle() {
36
+ wp_enqueue_style( 'document-gallery', DG_URL . 'assets/css/style.css', null, DG_VERSION );
37
+ }
38
+
39
+ /**
40
+ * Prints user's custom CSS.
41
+ */
42
+ public static function printCustomStyle() {
43
+ global $dg_options;
44
+
45
+ if ( ! empty( $dg_options['css']['text'] ) ) {
46
+ echo '<style type="text/css">' . $dg_options['css']['text'] . '</style>' . PHP_EOL;
47
+ }
48
+ }
49
+
50
+ /*==========================================================================
51
+ * I18n
52
+ *=========================================================================*/
53
+
54
+ /**
55
+ * Loads language files into WP core.
56
+ */
57
+ public static function loadTextDomain() {
58
+ load_plugin_textdomain( 'document-gallery', false, dirname( DG_BASENAME ) . '/languages/' );
59
+ }
60
+
61
+ /*==========================================================================
62
+ * HELPER FUNCTIONS
63
+ *=========================================================================*/
64
+
65
+ /**
66
+ * @param int $blog ID of the blog to be retrieved in multisite env.
67
+ *
68
+ * @return array Options for the blog.
69
+ */
70
+ public static function getOptions( $blog = null ) {
71
+ global $dg_options;
72
+
73
+ return is_null( $blog )
74
+ ? $dg_options
75
+ : get_blog_option( $blog, DG_OPTION_NAME, null );
76
+ }
77
+
78
+ /**
79
+ * @param array $options
80
+ * @param int $blog ID of the blog to be set in multisite env.
81
+ */
82
+ public static function setOptions( $options, $blog = null ) {
83
+ if ( is_null( $blog ) ) {
84
+ global $dg_options;
85
+ update_option( DG_OPTION_NAME, $options );
86
+ $dg_options = $options;
87
+ } else {
88
+ update_blog_option( $blog, DG_OPTION_NAME, $options );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * @param int $blog ID of the blog to be deleted in multisite env.
94
+ */
95
+ public static function deleteOptions( $blog = null ) {
96
+ if ( is_null( $blog ) ) {
97
+ delete_option( DG_OPTION_NAME );
98
+ } else {
99
+ delete_blog_option( $blog, DG_OPTION_NAME );
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Adds hook to validate DG options every time save is attempted.
105
+ */
106
+ public static function addValidation() {
107
+ add_filter( 'pre_update_option_' . DG_OPTION_NAME, array(
108
+ 'DocumentGallery',
109
+ 'validateOptionsStructure'
110
+ ), 10, 2 );
111
+ }
112
+
113
+ /**
114
+ * Checks whether the given options match the option schema.
115
+ *
116
+ * @param array $new The new options to be validated.
117
+ * @param array $old The old options.
118
+ *
119
+ * @return array The options to be saved.
120
+ */
121
+ public static function validateOptionsStructure( $new, $old ) {
122
+ if ( self::isValidOptionsStructure( $new ) ) {
123
+ $ret = $new;
124
+ } else {
125
+ $ret = $old;
126
+ DG_Logger::writeLog( DG_LogLevel::Error, 'Attempted to save invalid options.' . PHP_EOL . print_r( $new, true ), true, true );
127
+ }
128
+
129
+ return $ret;
130
+ }
131
+
132
+ /**
133
+ * @param array|unknown $o The options structure to validate.
134
+ * @param array $schema The schema to validate against (note that only keys matter -- non-array values are ignored).
135
+ *
136
+ * @return bool Whether the given options structure matches the schema.
137
+ */
138
+ private static function isValidOptionsStructure( $o, $schema = null ) {
139
+ if ( is_null( $schema ) ) {
140
+ $schema = DG_Setup::getDefaultOptions( true );
141
+ }
142
+
143
+ // simple checks first
144
+ $valid = is_array( $o ) && ( count( $schema ) === count( $o ) );
145
+
146
+ if ( $valid ) {
147
+ foreach ( $schema as $sk => $sv ) {
148
+ $valid = array_key_exists( $sk, $o );
149
+ if ( is_array( $sv ) && ! empty( $sv ) ) {
150
+ $valid = $valid && self::isValidOptionsStructure( $o[ $sk ], $sv );
151
+ }
152
+
153
+ if ( ! $valid ) {
154
+ break;
155
+ }
156
+ }
157
+ }
158
+
159
+ return $valid;
160
+ }
161
+
162
+ /**
163
+ * Function takes a GMT timestamp and returns a date/time string in the
164
+ * current timezone and WP format.
165
+ *
166
+ * @param int $timestamp The GMT timestamp to translate.
167
+ *
168
+ * @return string The local time in the WP date/time format.
169
+ */
170
+ public static function localDateTimeFromTimestamp( $timestamp ) {
171
+ static $gmt_offet = null;
172
+ static $wp_date_format = null;
173
+ static $wp_time_format = null;
174
+ if ( is_null( $gmt_offet ) ) {
175
+ $gmt_offet = get_option( 'gmt_offset' );
176
+ $wp_date_format = get_option( 'date_format' );
177
+ $wp_time_format = get_option( 'time_format' );
178
+ }
179
+
180
+ 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>';
181
+ }
182
+
183
+ /**
184
+ * Blocks instantiation. All functions are static.
185
+ */
186
+ private function __construct() {
187
+
188
+ }
189
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
inc/class-document.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /**
5
  * Holds data specific to a given document.
@@ -8,78 +8,82 @@ defined('WPINC') OR exit;
8
  */
9
  class DG_Document {
10
 
11
- /*==========================================================================
12
- * PRIVATE FIELDS
13
- *=========================================================================*/
14
 
15
- // general document data
16
- private $description, $gallery, $ID, $link, $title, $title_attribute;
17
 
18
- /*==========================================================================
19
- * INIT GALLERY
20
- *=========================================================================*/
21
 
22
- /**
23
- * Constructs instance of Document.
24
- * @param type $attachment Attachment object used to initalize fields.
25
- * @param type $gallery Instance of Gallery class.
26
- */
27
- public function __construct($attachment, $gallery) {
28
- include_once DG_PATH . 'inc/class-thumber.php';
29
-
30
- // init general document data
31
- $this->gallery = $gallery;
32
- $this->description = wptexturize($attachment->post_content);
33
- $this->ID = $attachment->ID;
34
- $this->link = $gallery->linkToAttachmentPg()
35
- ? get_attachment_link($attachment->ID)
36
- : wp_get_attachment_url($attachment->ID);
37
- $this->title = wptexturize($attachment->post_title);
38
- $this->title_attribute = esc_attr(strip_tags($this->title));
39
- }
40
 
41
- /*==========================================================================
42
- * OUTPUT HTML STRING
43
- *=========================================================================*/
 
 
44
 
45
- /**
46
- * Returns HTML representing this Document.
47
- * @filter dg_icon_template Filters the DG icon HTML. Passes a single
48
- * bool value indicating whether the gallery is using descriptions or not.
49
- * @return string
50
- */
51
- public function __toString() {
52
- $thumb = $this->gallery->useFancyThumbs()
53
- ? DG_Thumber::getThumbnail($this->ID)
54
- : DG_Thumber::getDefaultThumbnail($this->ID);
55
 
56
- $repl = array($this->link, $thumb, $this->title_attribute, $this->title);
57
- $find = array('%link%', '%img%', '%title_attribute%', '%title%');
58
- $description = '';
59
-
60
- // if descriptions then add filterable tag and value to replaced tag
61
- if ($this->gallery->useDescriptions()) {
62
- $repl[] = $this->description;
63
- $find[] = '%description%';
64
- $description = ' <p>%description%</p>';
65
- }
66
-
67
- $target = $this->gallery->openLinkInNewWindow() ? '_blank' : '_self';
68
- $doc_icon =
69
- ' <div class="document-icon">' . PHP_EOL .
70
- " <a href=\"%link%\" target=\"$target\"><img src=\"%img%\" title=\"%title_attribute%\" alt=\"%title_attribute%\" /><br>%title%</a>" . PHP_EOL .
71
- ' </div>' . PHP_EOL .
72
- $description;
73
-
74
- // allow developers to filter icon output
75
- $doc_icon = apply_filters(
76
- 'dg_icon_template',
77
- $doc_icon,
78
- $this->gallery->useDescriptions(),
79
- $this->ID);
80
 
81
- return str_replace($find, $repl, $doc_icon);
82
- }
83
- }
 
84
 
85
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /**
5
  * Holds data specific to a given document.
8
  */
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.
24
+ *
25
+ * @param WP_Post $attachment Attachment object used to initalize fields.
26
+ * @param DG_Gallery $gallery Instance of Gallery class.
27
+ */
28
+ public function __construct( $attachment, $gallery ) {
29
+ // init general document data
30
+ $this->gallery = $gallery;
31
+ $this->description = wptexturize( $attachment->post_content );
32
+ $this->ID = $attachment->ID;
33
+ $this->link = $gallery->linkToAttachmentPg()
34
+ ? get_attachment_link( $attachment->ID )
35
+ : wp_get_attachment_url( $attachment->ID );
36
+ $this->title = wptexturize( $attachment->post_title );
37
+ $this->title_attribute = esc_attr( strip_tags( $this->title ) );
 
 
38
 
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.
51
+ * @filter dg_icon_template Filters the DG icon HTML. Passes a single
52
+ * bool value indicating whether the gallery is using descriptions or not.
53
+ * @return string
54
+ */
55
+ public function __toString() {
56
+ include_once DG_PATH . 'inc/class-thumber.php';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ $thumb = $this->gallery->useFancyThumbs()
59
+ ? DG_Thumber::getThumbnail( $this->ID )
60
+ : DG_Thumber::getDefaultThumbnail( $this->ID );
61
+ $target = $this->gallery->openLinkInNewWindow() ? '_blank' : '_self';
62
 
63
+ $repl = array( $this->link, $thumb, $this->title_attribute, $this->title, $target, $this->extension, $this->size, $this->path );
64
+ $find = array( '%link%', '%img%', '%title_attribute%', '%title%', '%target%', '%extension%', '%size%', '%path%' );
65
+ $description = '';
66
+
67
+ // if descriptions then add filterable tag and value to replaced tag
68
+ if ( $this->gallery->useDescriptions() ) {
69
+ $repl[] = $this->description;
70
+ $find[] = '%description%';
71
+ $description = ' <p>%description%</p>';
72
+ }
73
+
74
+ $doc_icon =
75
+ ' <div class="document-icon">' . PHP_EOL .
76
+ ' <a href="%link%" target="%target%"><img src="%img%" title="%title_attribute%" alt="%title_attribute%" /><br>%title%</a>' . PHP_EOL .
77
+ ' </div>' . PHP_EOL .
78
+ $description;
79
+
80
+ // allow developers to filter icon output
81
+ $doc_icon = apply_filters(
82
+ 'dg_icon_template',
83
+ $doc_icon,
84
+ $this->gallery->useDescriptions(),
85
+ $this->ID );
86
+
87
+ return str_replace( $find, $repl, $doc_icon );
88
+ }
89
+ }
inc/class-gallery.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  DG_Gallery::init();
5
 
@@ -10,827 +10,850 @@ DG_Gallery::init();
10
  */
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.
30
- */
31
- public function linkToAttachmentPg() {
32
- return $this->atts['attachment_pg'];
33
- }
34
-
35
- /**
36
- * @return bool Whether to open thumb links in new window.
37
- */
38
- public function openLinkInNewWindow() {
39
- return $this->atts['new_window'];
40
- }
41
-
42
- /**
43
- * @return bool Whether to use "fancy" thumbnails.
44
- */
45
- public function useFancyThumbs() {
46
- return $this->atts['fancy'];
47
- }
48
-
49
- /**
50
- * @return bool Whether descriptions should be included in output.
51
- */
52
- public function useDescriptions() {
53
- return $this->atts['descriptions'];
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).
62
- * @return multitype:unknown Gets gallery branch of DG options array.
63
- */
64
- public static function getOptions($blog = null) {
65
- $options = DocumentGallery::getOptions($blog);
66
- return $options['gallery'];
67
- }
68
-
69
- /**
70
- * @param multitype:unknown $options New value for gallery branch of DG options array.
71
- * @param int $blog The blog we're retrieving options for (null => current blog).
72
- */
73
- public static function setOptions($options, $blog = null) {
74
- $dg_options = DocumentGallery::getOptions($blog);
75
- $dg_options['gallery'] = $options;
76
- DocumentGallery::setOptions($dg_options, $blog);
77
- }
78
-
79
- /*==========================================================================
80
- * INIT GALLERY
81
- *=========================================================================*/
82
-
83
- /**
84
- * Initializes static values for this class.
85
- */
86
- public static function init() {
87
- if (!isset(self::$comment)) {
88
- self::$comment =
89
- PHP_EOL . '<!-- ' . __('Generated using Document Gallery. Get yours here: ', 'document-gallery') .
90
- 'http://wordpress.org/extend/plugins/document-gallery -->' . PHP_EOL;
91
- self::$no_docs = '<!-- ' . __('No attachments to display. How boring! :(', 'document-gallery') . ' -->';
92
- self::$unary_err = __('The %s value entered, "%s", is not valid.', 'document-gallery');
93
- self::$binary_err = __('The %s parameter may only be "%s" or "%s." You entered "%s."', 'document-gallery');
94
- }
95
- }
96
-
97
- /**
98
- * Builds a gallery object with attributes passed.
99
- * @param multitype:string $atts Array of attributes used in shortcode.
100
- */
101
- public function __construct($atts) {
102
- include_once DG_PATH . 'inc/class-document.php';
103
-
104
- $post = get_post();
105
-
106
- // empty string is passed when no arguments are given, but constructor expects an array
107
- $atts = empty($atts) ? array() : $atts;
108
-
109
- if (!empty($atts['ids'])) {
110
- // 'ids' is explicitly ordered, unless you specify otherwise.
111
- if (empty($atts['orderby'])) {
112
- $atts['orderby'] = 'post__in';
113
- }
114
-
115
- $atts['include'] = $atts['ids'];
116
- unset($atts['ids']);
117
- }
118
-
119
- // allow abbreviated columns attribute
120
- if (!empty($atts['cols'])) {
121
- $atts['columns'] = $atts['cols'];
122
- unset($atts['cols']);
123
- }
124
-
125
- if (!empty($atts['images'])) {
126
- $options = self::getOptions();
127
- $mimes = trim(isset($atts['mime_types']) ? $atts['mime_types'] : $options['mime_types']);
128
- if (!preg_match('/[,^]image[,$]/', $mimes)) {
129
- $atts['mime_types'] = empty($mimes) ? 'image' : ($mimes . ',image');
130
- }
131
- }
132
-
133
- /**
134
- * @deprecated localpost will be removed at some point.
135
- */
136
- if (!empty($atts['localpost'])) {
137
- $atts['id'] = -1;
138
- unset($atts['localpost']);
139
- }
140
-
141
- // merge options w/ default values not stored in options
142
- $defaults = array_merge(
143
- array('id' => $post->ID, 'include' => '', 'exclude' => ''),
144
- self::getOptions());
145
-
146
- // values used to construct tax query (may be empty)
147
- $this->taxa = array_diff_key($atts, $defaults);
148
-
149
- // all recognized attributes go here
150
- $this->atts = shortcode_atts($defaults, $atts);
151
-
152
- // goes through all values in atts, setting errs as needed
153
- $this->atts = self::sanitizeDefaults($defaults, $this->atts, $this->errs);
154
-
155
- // query DB for all documents requested
156
- try {
157
- foreach($this->getDocuments() as $doc) {
158
- $this->docs[] = new DG_Document($doc, $this);
159
- }
160
- } catch(InvalidArgumentException $e) {
161
- // errors will be printed in __toString()
162
- }
163
- }
164
-
165
- /**
166
- * Cleans up user input, making sure we don't pass crap on to WP core.
167
- * @param multitype:string $old_defaults The previous set of defaults.
168
- * @param multitype:string $defaults The defaults array to sanitize.
169
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
170
- * @param bool $isDefaults Whether we're sanitizing the defaults array from DG_Admin.
171
- */
172
- public static function sanitizeDefaults($old_defaults, $defaults, &$errs) {
173
- if (is_null($old_defaults)) {
174
- $old_defaults = self::getOptions();
175
- }
176
-
177
- // remove invalid keys
178
- $sanitized = is_array($defaults)
179
- ? array_intersect_key($defaults, $old_defaults)
180
- : array();
181
-
182
- // add any missing keys & sanitize each new value
183
- foreach ($old_defaults as $k => $v) {
184
- if (!isset($sanitized[$k])) {
185
- if (is_bool($v)) {
186
- // checkbox
187
- $sanitized[$k] = false;
188
- } else {
189
- // missing value
190
- $sanitized[$k] = $v;
191
- }
192
- } else if ($sanitized[$k] != $v) {
193
- // sanitize value if different from old value
194
- $sanitized[$k] = self::sanitizeParameter($k, $sanitized[$k], $errs);
195
- }
196
- }
197
-
198
- return $sanitized;
199
- }
200
-
201
- /**
202
- *
203
- * @param string $key The key to reference the current value in the defaults array.
204
- * @param unknown $value The value to be sanitized.
205
- * @param unknown $errs multitype:string The array of errors, which will be appended with any errors found.
206
- * @return unknown The sanitized value, falling back to the current default value when invalid value given.
207
- */
208
- private static function sanitizeParameter($key, $value, &$errs) {
209
- // all sanitize methods must be in the following form: sanitize<UpperCammelCaseKey>
210
- $funct = $key;
211
- $funct[0] = strtoupper($funct[0]);
212
- $funct = 'sanitize' . preg_replace_callback('/_([a-z])/', array(__CLASS__, 'secondCharToUpper'), $funct);
213
-
214
- $callable = array(__CLASS__, $funct);
215
-
216
- // avoid looking for method beforehand unless we're running in debug mode -- expensive call
217
- if (DG_Logger::logEnabled() && !method_exists(__CLASS__, $funct)) {
218
- DG_Logger::writeLog(
219
- DG_LogLevel::Error,
220
- __('Attempted to call invalid function: ', 'document-gallery') . implode('::', $callable),
221
- true);
222
- }
223
-
224
- // call param-specific sanitization
225
- $ret = call_user_func_array($callable, array($value, &$err));
226
-
227
- // check for error and return default
228
- if (isset($err)) {
229
- $defaults = self::getOptions();
230
- $ret = $defaults[$key];
231
-
232
- $errs[$key] = $err;
233
- }
234
-
235
- return $ret;
236
- }
237
-
238
- /**
239
- * Takes the provided value and returns a sanitized value.
240
- * @param string $value The attachment_pg value to be sanitized.
241
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
242
- * @return bool The sanitized attachment_pg value.
243
- */
244
- private static function sanitizeAttachmentPg($value, &$err) {
245
- $ret = self::toBool($value);
246
-
247
- if(is_null($ret)) {
248
- $err = sprintf(self::$binary_err, 'attachment_pg', 'true', 'false', $value);
249
- }
250
-
251
- return $ret;
252
- }
253
-
254
- /**
255
- * Takes the provided value and returns a sanitized value.
256
- * @param string $value The columns value to be sanitized.
257
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
258
- * @return int The sanitized columns value.
259
- */
260
- public static function sanitizeColumns($value, &$err) {
261
- return $value != -1 ? absint($value) : null;
262
- }
263
-
264
- /**
265
- * Takes the provided value and returns a sanitized value.
266
- * @param string $value The descriptions value to be sanitized.
267
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
268
- * @return bool The sanitized descriptions value.
269
- */
270
- private static function sanitizeDescriptions($value, &$err) {
271
- $ret = self::toBool($value);
272
-
273
- if(is_null($ret)) {
274
- $err = sprintf(self::$binary_err, 'descriptions', 'true', 'false', $value);
275
- }
276
-
277
- return $ret;
278
- }
279
-
280
- /**
281
- * Takes the provided value and returns a sanitized value.
282
- * @param string $value The exclude value to be sanitized.
283
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
284
- * @return bool The sanitized exclude value.
285
- */
286
- private static function sanitizeExclude($value, &$err) {
287
- return self::sanitizeIdList('Exclude', $value, $err);
288
- }
289
-
290
- /**
291
- * Takes the provided value and returns a sanitized value.
292
- * @param string $value The fancy value to be sanitized.
293
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
294
- * @return bool The sanitized fancy value.
295
- */
296
- private static function sanitizeFancy($value, &$err) {
297
- $ret = self::toBool($value);
298
-
299
- if(is_null($ret)) {
300
- $err = sprintf(self::$binary_err, 'fancy', 'true', 'false', $value);
301
- }
302
-
303
- return $ret;
304
- }
305
-
306
- /**
307
- * Takes the provided value and returns a sanitized value.
308
- * @param string $value The id value to be sanitized.
309
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
310
- * @return int The sanitized id value.
311
- */
312
- private static function sanitizeId($value, &$err) {
313
- return $value != -1 ? absint($value) : null;
314
- }
315
-
316
- /**
317
- * Takes the provided comma-delimited list of IDs and returns null if it is invalid.
318
- * @param string $name Name of the value being sanitized. Used in error string when needed.
319
- * @param string $value The ids value to be sanitized.
320
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
321
- * @return bool|multitype:int The sanitized comma-delimited list of IDs value.
322
- */
323
- private static function sanitizeIdList($name, $value, &$err) {
324
- static $regex = '/(?:|\d+(?:,\d+)*)/';
325
-
326
- $ret = $value;
327
-
328
- if (!preg_match($regex, $value)) {
329
- $err = sprintf(__('%s may only be a comma-delimited list of integers.', 'document-gallery'), name);
330
- $ret = null;
331
- }
332
-
333
- return $ret;
334
- }
335
-
336
- /**
337
- * Takes the provided value and returns a sanitized value.
338
- * @param string $value The ids value to be sanitized.
339
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
340
- * @return bool|multitype:int The sanitized ids value.
341
- */
342
- private static function sanitizeInclude($value, &$err) {
343
- return self::sanitizeIdList('Include', $value, $err);
344
- }
345
-
346
- /**
347
- * Takes the provided value and returns a sanitized value.
348
- * @param string $value The limit value to be sanitized.
349
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
350
- * @return int The sanitized limit value.
351
- */
352
- private static function sanitizeLimit($value, &$err) {
353
- $ret = intval($value);
354
-
355
- if (is_null($ret) || $ret < -1) {
356
- $err = sprintf(self::$unary_err, 'limit', '>= -1');
357
- $ret = null;
358
- }
359
-
360
- return $ret;
361
- }
362
-
363
- /**
364
- * Takes the provided value and returns a sanitized value.
365
- * @param string $value The mime_types value to be sanitized.
366
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
367
- * @return string The sanitized mime_types value.
368
- */
369
- private static function sanitizeMimeTypes($value, &$err) {
370
- // TODO: do some actual sanitization...
371
- return $value;
372
- }
373
-
374
- /**
375
- * Takes the provided value and returns a sanitized value.
376
- * @param string $value The new_window value to be sanitized.
377
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
378
- * @return bool The sanitized new_window value.
379
- */
380
- private static function sanitizeNewWindow($value, &$err) {
381
- $ret = self::toBool($value);
382
-
383
- if(is_null($ret)) {
384
- $err = sprintf(self::$binary_err, 'new_window', 'true', 'false', $value);
385
- }
386
-
387
- return $ret;
388
- }
389
-
390
- /**
391
- * Takes the provided value and returns a sanitized value.
392
- * @param string $value The order value to be sanitized.
393
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
394
- * @return string The sanitized order value.
395
- */
396
- private static function sanitizeOrder($value, &$err) {
397
- $ret = strtoupper($value);
398
-
399
- if(!in_array($ret, self::getOrderOptions())) {
400
- $err = sprintf(self::$binary_err, 'order', 'ASC', 'DESC', $value);
401
- $ret = null;
402
- }
403
-
404
- return $ret;
405
- }
406
-
407
- /**
408
- * @return multitype:string The valid options for order parameter.
409
- */
410
- public static function getOrderOptions() {
411
- return array('ASC', 'DESC');
412
- }
413
-
414
- /**
415
- * Takes the provided value and returns a sanitized value.
416
- * @param string $value The orderby value to be sanitized.
417
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
418
- * @return string The sanitized orderby value.
419
- */
420
- private static function sanitizeOrderby($value, &$err) {
421
- $ret = ('ID' === strtoupper($value)) ? 'ID' : strtolower($value);
422
-
423
- if (!in_array($ret, self::getOrderbyOptions())) {
424
- $err = sprintf(self::$unary_err, 'orderby', $value);
425
- $ret = null;
426
- }
427
-
428
- return $ret;
429
- }
430
-
431
- /**
432
- * @return multitype:string The valid options for orderby parameter.
433
- */
434
- public static function getOrderbyOptions() {
435
- return array('author', 'comment_count', 'date', 'ID',
436
- 'menu_order', 'modified', 'name', 'none',
437
- 'parent', 'post__in', 'rand', 'title');
438
- }
439
-
440
- /**
441
- * Takes the provided value and returns a sanitized value.
442
- * @param string $value The post_status value to be sanitized.
443
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
444
- * @return string The sanitized post_status value.
445
- */
446
- private static function sanitizePostStatus($value, &$err) {
447
- $ret = preg_grep('/^' . preg_quote($value) .'$/i', self::getPostStatuses());
448
- $ret = reset($ret);
449
-
450
- if($ret === false) {
451
- $err = sprintf(self::$unary_err, 'post_status', $value);
452
- }
453
-
454
- return $ret;
455
- }
456
-
457
- /**
458
- * @return multitype:string All registered post statuses.
459
- */
460
- public static function getPostStatuses() {
461
- static $statuses;
462
- if (!isset($statuses)) {
463
- $statuses = get_post_stati();
464
- $statuses[] = 'any';
465
- asort($statuses);
466
- }
467
-
468
- return $statuses;
469
- }
470
-
471
- /**
472
- * Takes the provided value and returns a sanitized value.
473
- * @param string $value The post_type value to be sanitized.
474
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
475
- * @return string The sanitized post_type value.
476
- */
477
- private static function sanitizePostType($value, &$err) {
478
- $ret = preg_grep('/^' . preg_quote($value) .'$/i', self::getPostTypes());
479
- $ret = reset($ret);
480
-
481
- if($ret === false) {
482
- $err = sprintf(self::$unary_err, 'post_type', $value);
483
- }
484
-
485
- return $ret;
486
- }
487
-
488
- /**
489
- * @return multitype:string All registered post types.
490
- */
491
- public static function getPostTypes() {
492
- static $types;
493
- if (!isset($types)) {
494
- $types = get_post_types();
495
- $types[] = 'any';
496
- asort($types);
497
- }
498
-
499
- return $types;
500
- }
501
-
502
- /**
503
- * Takes the provided value and returns a sanitized value.
504
- * @param string $value The relation value to be sanitized.
505
- * @param multitype:string &$errs The array of errors, which will be appended with any errors found.
506
- * @return string The sanitized relation value.
507
- */
508
- private static function sanitizeRelation($value, &$err) {
509
- $ret = strtoupper($value);
510
-
511
- if(!in_array($ret, self::getRelationOptions())) {
512
- $err = sprintf(self::$binary_err, 'relation', 'AND', 'OR', $value);
513
- $ret = null;
514
- }
515
-
516
- return $ret;
517
- }
518
-
519
- /**
520
- * @return multitype:string The valid options for relation parameter.
521
- */
522
- public static function getRelationOptions() {
523
- return array('AND', 'OR');
524
- }
525
-
526
- /**
527
- * Takes the provided value and returns a sanitized value.
528
- * @param string $operator The operator value to be sanitized.
529
- * @return string The sanitized operator value.
530
- */
531
- private function sanitizeOperator($operator) {
532
- $ret = strtoupper($operator);
533
-
534
- if (!in_array($ret, self::getOperatorOptions())) {
535
- $this->errs[] = sprintf(self::$binary_err, $key, 'IN", "NOT IN", "OR', 'AND', $operator);
536
- $ret = null;
537
- } else if ($ret === 'OR') {
538
- $ret = 'IN';
539
- }
540
-
541
- return $ret;
542
- }
543
-
544
- /**
545
- * @return multitype:string The valid options for *_relation/*_operator parameter.
546
- */
547
- public static function getOperatorOptions() {
548
- return array('IN', 'NOT IN', 'AND', 'OR');
549
- }
550
-
551
- /**
552
- * Gets all valid Documents based on the attributes passed by the user.
553
- * NOTE: Keys in returned array are arbitrary and will vary. They should be ignored.
554
- * @return multitype:unknown Contains all documents matching the query.
555
- * @throws InvalidArgumentException Thrown when $this->errs is not empty.
556
- */
557
- private function getDocuments() {
558
- $query = array(
559
- 'numberposts' => $this->atts['limit'],
560
- 'orderby' => $this->atts['orderby'],
561
- 'order' => $this->atts['order'],
562
- 'post_status' => $this->atts['post_status'],
563
- 'post_type' => $this->atts['post_type'],
564
- 'post_mime_type' => $this->atts['mime_types']);
565
-
566
- $this->setTaxa($query);
567
-
568
- if(!empty($this->errs)) {
569
- throw new InvalidArgumentException();
570
- }
571
-
572
- // NOTE: Derived from gallery shortcode
573
- if (!empty($this->atts['include'])) {
574
- $query['include'] = $this->atts['include'];
575
- $attachments = get_posts($query);
576
- } else {
577
- // id == 0 => all attachments w/o a parent
578
- // id == null => all matched attachments
579
- $query['post_parent'] = $this->atts['id'];
580
- if (!empty($exclude)) {
581
- $query['exclude'] = $this->atts['exclude'];
582
- }
583
-
584
- $attachments = get_children($query);
585
- }
586
-
587
- return $attachments;
588
- }
589
-
590
- /**
591
- * Function loops through all attributes passed that did not match
592
- * self::$defaults. If they are the name of a taxonomy, they are plugged
593
- * into the query, otherwise $this->errs is appended with an error string.
594
- * @param multitype:unknown $query Query to insert tax query into.
595
- */
596
- private function setTaxa(&$query) {
597
- if (!empty($this->taxa)) {
598
- $taxa = array('relation' => $this->atts['relation']);
599
- $operator = array();
600
- $suffix = array('relation', 'operator');
601
- $pattern = '/(.+)_(?:' . implode('|', $suffix) . ')$/i';
602
-
603
- // find any relations for taxa
604
- $iterable = $this->taxa;
605
- foreach ($iterable as $key => $value) {
606
- if (preg_match($pattern, $key, $matches)) {
607
- $base = $matches[1];
608
- if (array_key_exists($base, $this->taxa)) {
609
- $operator[$base] = self::sanitizeOperator($value);
610
- unset($this->taxa[$key]);
611
- }
612
- }
613
- }
614
-
615
- // build tax query
616
- foreach ($this->taxa as $taxon => $terms) {
617
- $terms = $this->getTermIdsByNames($taxon, explode(',', $terms));
618
-
619
- $taxa[] = array(
620
- 'taxonomy' => $taxon,
621
- 'field' => 'id',
622
- 'terms' => $terms,
623
- 'operator' => isset($operator[$taxon]) ? $operator[$taxon] : 'IN'
624
- );
625
- }
626
-
627
- // create nested structure
628
- $query['tax_query'] = $taxa;
629
- }
630
- }
631
-
632
- /*==========================================================================
633
- * HELPER FUNCTIONS
634
- *=========================================================================*/
635
-
636
- /**
637
- * Returns an array of term ids when provided with a list of term names.
638
- * Also appends an entry onto $errs if any invalid names are found.
639
- * @param string $taxon The taxon these terms are a member of.
640
- * @param multitype:string $term_names Terms to retrieve.
641
- * @return multitype:string All matched terms.
642
- */
643
- private function getTermIdsByNames($taxon, $term_names) {
644
- return $this->getTermXByNames('term_id', $taxon, $term_names);
645
- }
646
-
647
- /**
648
- * Returns an array of term slugs when provided with a list of term names.
649
- * Also appends an entry onto $errs if any invalid names are found.
650
- * @param string $taxon The taxon these terms are a member of.
651
- * @param multitype:string $term_names Terms to retrieve.
652
- * @return multitype:string All matched terms.
653
- */
654
- private function getTermSlugsByNames($taxon, $term_names) {
655
- return $this->getTermXByNames('slug', $taxon, $term_names);
656
- }
657
-
658
- /**
659
- * Returns a list of x, where x may be any of the fields within a
660
- * term object, when provided with a list of term names (not slugs).
661
- * (http://codex.wordpress.org/Function_Reference/get_term_by#Return_Values)
662
- *
663
- * Also appends an entry onto $errs if any invalid names are found.
664
- * @param string $x Field to retrieve from matched term.
665
- * @param string $taxon The taxon these terms are a member of.
666
- * @param multitype:string $term_names Terms to retrieve.
667
- * @return multitype:string All matched terms.
668
- */
669
- private function getTermXByNames($x, $taxon, $term_names) {
670
- $ret = array();
671
- $valid = true;
672
-
673
- // taxons may optionally be prefixed by 'tax_' --
674
- // this is only useful when avoiding collisions with other attributes
675
- if (!taxonomy_exists($taxon)) {
676
- $tmp = preg_replace('/^tax_(.*)/', '$1', $taxon, 1, $count);
677
- if ($count > 0 && taxonomy_exists($tmp)) {
678
- $taxon = $tmp;
679
- } else {
680
- $this->errs[] = sprintf(self::$unary_err, 'taxon', $taxon);
681
- $valid = false;
682
- }
683
- }
684
-
685
- // only check terms if we first have a valid taxon
686
- if ($valid) {
687
- foreach ($term_names as $name) {
688
- if (($term = get_term_by('name', $name, $taxon))) {
689
- $ret[] = $term->{$x};
690
- } else {
691
- $this->errs[] = sprintf(__('%s is not a valid term name in %s.',
692
- 'document-gallery'), $name, $taxon);
693
- }
694
- }
695
- }
696
-
697
- return $ret;
698
- }
699
-
700
- /**
701
- * @param string $string To take second char from.
702
- * @return char Capitalized second char of given string.
703
- */
704
- private static function secondCharToUpper($string) {
705
- return strtoupper($string[1]);
706
- }
707
-
708
- /**
709
- * Function returns false for positive ints, true otherwise.
710
- * @param string $var could be anything.
711
- * @return boolean indicating whether $var is not a positive int.
712
- */
713
- private static function negativeInt($var) {
714
- return !is_numeric($var) // isn't numeric
715
- || (int)$var != $var // isn't int
716
- || (int)$var < 0; // isn't positive
717
- }
718
-
719
- /**
720
- * Converts provided value to bool.
721
- * @param unknown $val To be converted.
722
- * @return bool|NULL Bool value if can be parsed, else NULL.
723
- */
724
- private static function toBool($val) {
725
- if (is_null($val)) {
726
- return false;
727
- }
728
-
729
- if (is_bool($val)) {
730
- return $val;
731
- }
732
-
733
- if (is_int($val)) {
734
- if (1 === $val) {
735
- return true;
736
- }
737
-
738
- if (0 === $val) {
739
- return false;
740
- }
741
- }
742
-
743
- if (is_string($val)) {
744
- $val = strtolower($val);
745
- if ('true' === $val || '1' === $val) {
746
- return true;
747
- }
748
-
749
- if ('false' === $val || '0' === $val) {
750
- return false;
751
- }
752
- }
753
-
754
- return null;
755
- }
756
-
757
- /*==========================================================================
758
- * OUTPUT HTML STRING
759
- *=========================================================================*/
760
-
761
- /**
762
- * @filter dg_gallery_template Allows the user to filter anything content surrounding the generated gallery.
763
- * @filter dg_row_template Filters the outer DG wrapper HTML. Passes a single
764
- * bool value indicating whether the gallery is using descriptions or not.
765
- * @return string HTML representing this Gallery.
766
- */
767
- public function __toString() {
768
- static $instance = 0;
769
- $instance++;
770
-
771
- static $find = null;
772
- if (is_null($find)) {
773
- $find = array('%class%', '%icons%');
774
- }
775
-
776
- if (!empty($this->errs)) {
777
- return '<p>' . implode('</p><p>', $this->errs) . '</p>';
778
- }
779
-
780
- if (empty($this->docs)) {
781
- return self::$no_docs;
782
- }
783
-
784
- $selector = "document-gallery-$instance";
785
- $template =
786
- "<div id='$selector' class='%class%'>". PHP_EOL .
787
- '%icons%' . PHP_EOL .
788
- '</div>' . PHP_EOL;
789
-
790
- $icon_wrapper = apply_filters(
791
- 'dg_row_template',
792
- $template,
793
- $this->useDescriptions());
794
-
795
- $core = '';
796
- $classes = array('document-icon-wrapper');
797
- if($this->useDescriptions()) {
798
- $classes[] = 'descriptions';
799
- }
800
-
801
- $repl = array(implode(' ', $classes));
802
- if($this->useDescriptions()) {
803
- foreach($this->docs as $doc) {
804
- $repl[1] = $doc;
805
- $core .= str_replace($find, $repl, $icon_wrapper);
806
- }
807
- } else {
808
- global $dg_gallery_style;
809
-
810
- $count = count($this->docs);
811
- $cols = !is_null($this->atts['columns']) ? $this->atts['columns'] : $count;
812
-
813
- if (apply_filters('dg_use_default_gallery_style', true )) {
814
- $itemwidth = $cols > 0 ? (floor(100/$cols) - 1) : 100;
815
- $core .= "<style type='text/css'>#$selector .document-icon{width:$itemwidth%}</style>";
816
- }
817
-
818
- for($i = 0; $i < $count; $i += $cols) {
819
- $repl[1] = '';
820
-
821
- $min = min($i + $cols, $count);
822
- for($x = $i; $x < $min; $x++) {
823
- $repl[1] .= $this->docs[$x];
824
- }
825
-
826
- $core .= str_replace($find, $repl, $icon_wrapper);
827
- }
828
- }
829
-
830
- // allow user to wrap gallery output
831
- $gallery = apply_filters('dg_gallery_template', '%rows%', $this->useDescriptions());
832
- return self::$comment . str_replace('%rows%', $core, $gallery);
833
- }
834
- }
835
-
836
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  DG_Gallery::init();
5
 
10
  */
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.
30
+ */
31
+ public function linkToAttachmentPg() {
32
+ return $this->atts['attachment_pg'];
33
+ }
34
+
35
+ /**
36
+ * @return bool Whether to open thumb links in new window.
37
+ */
38
+ public function openLinkInNewWindow() {
39
+ return $this->atts['new_window'];
40
+ }
41
+
42
+ /**
43
+ * @return bool Whether to use "fancy" thumbnails.
44
+ */
45
+ public function useFancyThumbs() {
46
+ return $this->atts['fancy'];
47
+ }
48
+
49
+ /**
50
+ * @return bool Whether descriptions should be included in output.
51
+ */
52
+ public function useDescriptions() {
53
+ return $this->atts['descriptions'];
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).
62
+ *
63
+ * @return array Gets gallery branch of DG options array.
64
+ */
65
+ public static function getOptions( $blog = null ) {
66
+ $options = DocumentGallery::getOptions( $blog );
67
+
68
+ return $options['gallery'];
69
+ }
70
+
71
+ /**
72
+ * @param array $options New value for gallery branch of DG options array.
73
+ * @param int $blog The blog we're retrieving options for (null => current blog).
74
+ */
75
+ public static function setOptions( $options, $blog = null ) {
76
+ $dg_options = DocumentGallery::getOptions( $blog );
77
+ $dg_options['gallery'] = $options;
78
+ DocumentGallery::setOptions( $dg_options, $blog );
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
+
99
+ /**
100
+ * Builds a gallery object with attributes passed.
101
+ *
102
+ * @param array $atts Array of attributes used in shortcode.
103
+ */
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'] ) ) {
115
+ $atts['orderby'] = 'post__in';
116
+ }
117
+
118
+ $atts['include'] = $atts['ids'];
119
+ unset( $atts['ids'] );
120
+ }
121
+
122
+ // allow abbreviated columns attribute
123
+ if ( ! empty( $atts['cols'] ) ) {
124
+ $atts['columns'] = $atts['cols'];
125
+ unset( $atts['cols'] );
126
+ }
127
+
128
+ if ( ! empty( $atts['images'] ) ) {
129
+ $options = self::getOptions();
130
+ $mimes = trim( isset( $atts['mime_types'] ) ? $atts['mime_types'] : $options['mime_types'] );
131
+ if ( ! preg_match( '/[,^]image[,$]/', $mimes ) ) {
132
+ $atts['mime_types'] = empty( $mimes ) ? 'image' : ( $mimes . ',image' );
133
+ }
134
+ }
135
+
136
+ /**
137
+ * @deprecated localpost will be removed at some point.
138
+ */
139
+ if ( ! empty( $atts['localpost'] ) ) {
140
+ $atts['id'] = - 1;
141
+ unset( $atts['localpost'] );
142
+ }
143
+
144
+ // merge options w/ default values not stored in options
145
+ $defaults = array_merge(
146
+ array( 'id' => $post->ID, 'include' => '', 'exclude' => '' ),
147
+ self::getOptions() );
148
+
149
+ // values used to construct tax query (may be empty)
150
+ $this->taxa = array_diff_key( $atts, $defaults );
151
+
152
+ // all recognized attributes go here
153
+ $this->atts = shortcode_atts( $defaults, $atts );
154
+
155
+ // goes through all values in atts, setting errs as needed
156
+ $this->atts = self::sanitizeDefaults( $defaults, $this->atts, $this->errs );
157
+
158
+ // query DB for all documents requested
159
+ try {
160
+ foreach ( $this->getDocuments() as $doc ) {
161
+ $this->docs[] = new DG_Document( $doc, $this );
162
+ }
163
+ } catch ( InvalidArgumentException $e ) {
164
+ // errors will be printed in __toString()
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Cleans up user input, making sure we don't pass crap on to WP core.
170
+ *
171
+ * @param array $old_defaults The previous set of defaults.
172
+ * @param array $defaults The defaults array to sanitize.
173
+ * @param array &$errs The array of errors, which will be appended with any errors found.
174
+ *
175
+ * @return array The sanitized defaults.
176
+ */
177
+ public static function sanitizeDefaults( $old_defaults, $defaults, &$errs ) {
178
+ if ( is_null( $old_defaults ) ) {
179
+ $old_defaults = self::getOptions();
180
+ }
181
+
182
+ // remove invalid keys
183
+ $sanitized = is_array( $defaults )
184
+ ? array_intersect_key( $defaults, $old_defaults )
185
+ : array();
186
+
187
+ // add any missing keys & sanitize each new value
188
+ foreach ( $old_defaults as $k => $v ) {
189
+ if ( ! isset( $sanitized[ $k ] ) ) {
190
+ if ( is_bool( $v ) ) {
191
+ // checkbox
192
+ $sanitized[ $k ] = false;
193
+ } else {
194
+ // missing value
195
+ $sanitized[ $k ] = $v;
196
+ }
197
+ } else if ( $sanitized[ $k ] !== $v ) { //Sometimes we get boolean in the string form for checkboxes
198
+ // sanitize value if different from old value
199
+ $sanitized[ $k ] = self::sanitizeParameter( $k, $sanitized[ $k ], $errs );
200
+ }
201
+ }
202
+
203
+ return $sanitized;
204
+ }
205
+
206
+ /**
207
+ *
208
+ * @param string $key The key to reference the current value in the defaults array.
209
+ * @param unknown $value The value to be sanitized.
210
+ * @param array $errs The array of errors, which will be appended with any errors found.
211
+ *
212
+ * @return unknown The sanitized value, falling back to the current default value when invalid value given.
213
+ */
214
+ private static function sanitizeParameter( $key, $value, &$errs ) {
215
+ // all sanitize methods must be in the following form: sanitize<UpperCammelCaseKey>
216
+ $funct = $key;
217
+ $funct[0] = strtoupper( $funct[0] );
218
+ $funct = 'sanitize' . preg_replace_callback( '/_([a-z])/', array( __CLASS__, 'secondCharToUpper' ), $funct );
219
+
220
+ $callable = array( __CLASS__, $funct );
221
+
222
+ // avoid looking for method beforehand unless we're running in debug mode -- expensive call
223
+ if ( DG_Logger::logEnabled() && ! method_exists( __CLASS__, $funct ) ) {
224
+ DG_Logger::writeLog(
225
+ DG_LogLevel::Error,
226
+ __( 'Attempted to call invalid function: ', 'document-gallery' ) . implode( '::', $callable ),
227
+ true );
228
+ }
229
+
230
+ // call param-specific sanitization
231
+ $ret = call_user_func_array( $callable, array( $value, &$err ) );
232
+
233
+ // check for error and return default
234
+ if ( isset( $err ) ) {
235
+ $defaults = self::getOptions();
236
+ $ret = $defaults[ $key ];
237
+
238
+ $errs[ $key ] = $err;
239
+ }
240
+
241
+ return $ret;
242
+ }
243
+
244
+ /**
245
+ * Takes the provided value and returns a sanitized value.
246
+ *
247
+ * @param string $value The attachment_pg value to be sanitized.
248
+ * @param string &$err String to be initialized with error, if any.
249
+ *
250
+ * @return bool The sanitized attachment_pg value.
251
+ */
252
+ private static function sanitizeAttachmentPg( $value, &$err ) {
253
+ $ret = DG_Util::toBool( $value );
254
+
255
+ if ( is_null( $ret ) ) {
256
+ $err = sprintf( self::$binary_err, 'attachment_pg', 'true', 'false', $value );
257
+ }
258
+
259
+ return $ret;
260
+ }
261
+
262
+ /**
263
+ * Takes the provided value and returns a sanitized value.
264
+ *
265
+ * @param string $value The columns value to be sanitized.
266
+ * @param string &$err String to be initialized with error, if any.
267
+ *
268
+ * @return int The sanitized columns value.
269
+ */
270
+ public static function sanitizeColumns( $value, &$err ) {
271
+ return $value != - 1 ? absint( $value ) : null;
272
+ }
273
+
274
+ /**
275
+ * Takes the provided value and returns a sanitized value.
276
+ *
277
+ * @param string $value The descriptions value to be sanitized.
278
+ * @param string &$err String to be initialized with error, if any.
279
+ *
280
+ * @return bool The sanitized descriptions value.
281
+ */
282
+ private static function sanitizeDescriptions( $value, &$err ) {
283
+ $ret = DG_Util::toBool( $value );
284
+
285
+ if ( is_null( $ret ) ) {
286
+ $err = sprintf( self::$binary_err, 'descriptions', 'true', 'false', $value );
287
+ }
288
+
289
+ return $ret;
290
+ }
291
+
292
+ /**
293
+ * Takes the provided value and returns a sanitized value.
294
+ *
295
+ * @param string $value The exclude value to be sanitized.
296
+ * @param string &$err String to be initialized with error, if any.
297
+ *
298
+ * @return bool The sanitized exclude value.
299
+ */
300
+ private static function sanitizeExclude( $value, &$err ) {
301
+ return self::sanitizeIdList( 'Exclude', $value, $err );
302
+ }
303
+
304
+ /**
305
+ * Takes the provided value and returns a sanitized value.
306
+ *
307
+ * @param string $value The fancy value to be sanitized.
308
+ * @param string &$err String to be initialized with error, if any.
309
+ *
310
+ * @return bool The sanitized fancy value.
311
+ */
312
+ private static function sanitizeFancy( $value, &$err ) {
313
+ $ret = DG_Util::toBool( $value );
314
+
315
+ if ( is_null( $ret ) ) {
316
+ $err = sprintf( self::$binary_err, 'fancy', 'true', 'false', $value );
317
+ }
318
+
319
+ return $ret;
320
+ }
321
+
322
+ /**
323
+ * Takes the provided value and returns a sanitized value.
324
+ *
325
+ * @param string $value The id value to be sanitized.
326
+ * @param string &$err String to be initialized with error, if any.
327
+ *
328
+ * @return int The sanitized id value.
329
+ */
330
+ private static function sanitizeId( $value, &$err ) {
331
+ return $value != - 1 ? absint( $value ) : null;
332
+ }
333
+
334
+ /**
335
+ * Takes the provided comma-delimited list of IDs and returns null if it is invalid.
336
+ *
337
+ * @param string $name Name of the value being sanitized. Used in error string when needed.
338
+ * @param string $value The ids value to be sanitized.
339
+ * @param string &$err String to be initialized with error, if any.
340
+ *
341
+ * @return bool|multitype:int The sanitized comma-delimited list of IDs value.
342
+ */
343
+ private static function sanitizeIdList( $name, $value, &$err ) {
344
+ static $regex = '/(?:|\d+(?:,\d+)*)/';
345
+
346
+ $ret = $value;
347
+
348
+ if ( ! preg_match( $regex, $value ) ) {
349
+ $err = sprintf( __( '%s may only be a comma-delimited list of integers.', 'document-gallery' ), $name );
350
+ $ret = null;
351
+ }
352
+
353
+ return $ret;
354
+ }
355
+
356
+ /**
357
+ * Takes the provided value and returns a sanitized value.
358
+ *
359
+ * @param string $value The ids value to be sanitized.
360
+ * @param string &$err String to be initialized with error, if any.
361
+ *
362
+ * @return bool|multitype:int The sanitized ids value.
363
+ */
364
+ private static function sanitizeInclude( $value, &$err ) {
365
+ return self::sanitizeIdList( 'Include', $value, $err );
366
+ }
367
+
368
+ /**
369
+ * Takes the provided value and returns a sanitized value.
370
+ *
371
+ * @param string $value The limit value to be sanitized.
372
+ * @param string &$err String to be initialized with error, if any.
373
+ *
374
+ * @return int The sanitized limit value.
375
+ */
376
+ private static function sanitizeLimit( $value, &$err ) {
377
+ $ret = intval( $value );
378
+
379
+ if ( is_null( $ret ) || $ret < - 1 ) {
380
+ $err = sprintf( self::$unary_err, 'limit', '>= -1' );
381
+ $ret = null;
382
+ }
383
+
384
+ return $ret;
385
+ }
386
+
387
+ /**
388
+ * Takes the provided value and returns a sanitized value.
389
+ *
390
+ * @param string $value The mime_types value to be sanitized.
391
+ * @param string &$err String to be initialized with error, if any.
392
+ *
393
+ * @return string The sanitized mime_types value.
394
+ */
395
+ private static function sanitizeMimeTypes( $value, &$err ) {
396
+ // TODO: do some actual sanitization...
397
+ return $value;
398
+ }
399
+
400
+ /**
401
+ * Takes the provided value and returns a sanitized value.
402
+ *
403
+ * @param string $value The new_window value to be sanitized.
404
+ * @param string &$err String to be initialized with error, if any.
405
+ *
406
+ * @return bool The sanitized new_window value.
407
+ */
408
+ private static function sanitizeNewWindow( $value, &$err ) {
409
+ $ret = DG_Util::toBool( $value );
410
+
411
+ if ( is_null( $ret ) ) {
412
+ $err = sprintf( self::$binary_err, 'new_window', 'true', 'false', $value );
413
+ }
414
+
415
+ return $ret;
416
+ }
417
+
418
+ /**
419
+ * Takes the provided value and returns a sanitized value.
420
+ *
421
+ * @param string $value The order value to be sanitized.
422
+ * @param string &$err String to be initialized with error, if any.
423
+ *
424
+ * @return string The sanitized order value.
425
+ */
426
+ private static function sanitizeOrder( $value, &$err ) {
427
+ $ret = strtoupper( $value );
428
+
429
+ if ( ! in_array( $ret, self::getOrderOptions() ) ) {
430
+ $err = sprintf( self::$binary_err, 'order', 'ASC', 'DESC', $value );
431
+ $ret = null;
432
+ }
433
+
434
+ return $ret;
435
+ }
436
+
437
+ /**
438
+ * @return array The valid options for order parameter.
439
+ */
440
+ public static function getOrderOptions() {
441
+ return array( 'ASC', 'DESC' );
442
+ }
443
+
444
+ /**
445
+ * Takes the provided value and returns a sanitized value.
446
+ *
447
+ * @param string $value The orderby value to be sanitized.
448
+ * @param string &$err String to be initialized with error, if any.
449
+ *
450
+ * @return string The sanitized orderby value.
451
+ */
452
+ private static function sanitizeOrderby( $value, &$err ) {
453
+ $ret = ( 'ID' === strtoupper( $value ) ) ? 'ID' : strtolower( $value );
454
+
455
+ if ( ! in_array( $ret, self::getOrderbyOptions() ) ) {
456
+ $err = sprintf( self::$unary_err, 'orderby', $value );
457
+ $ret = null;
458
+ }
459
+
460
+ return $ret;
461
+ }
462
+
463
+ /**
464
+ * @return array The valid options for orderby parameter.
465
+ */
466
+ public static function getOrderbyOptions() {
467
+ return array(
468
+ 'author',
469
+ 'comment_count',
470
+ 'date',
471
+ 'ID',
472
+ 'menu_order',
473
+ 'modified',
474
+ 'name',
475
+ 'none',
476
+ 'parent',
477
+ 'post__in',
478
+ 'rand',
479
+ 'title'
480
+ );
481
+ }
482
+
483
+ /**
484
+ * Takes the provided value and returns a sanitized value.
485
+ *
486
+ * @param string $value The post_status value to be sanitized.
487
+ * @param string &$err String to be initialized with error, if any.
488
+ *
489
+ * @return string The sanitized post_status value.
490
+ */
491
+ private static function sanitizePostStatus( $value, &$err ) {
492
+ $ret = preg_grep( '/^' . preg_quote( $value ) . '$/i', self::getPostStatuses() );
493
+ $ret = reset( $ret );
494
+
495
+ if ( $ret === false ) {
496
+ $err = sprintf( self::$unary_err, 'post_status', $value );
497
+ }
498
+
499
+ return $ret;
500
+ }
501
+
502
+ /**
503
+ * @return array All registered post statuses.
504
+ */
505
+ public static function getPostStatuses() {
506
+ static $statuses;
507
+ if ( ! isset( $statuses ) ) {
508
+ $statuses = get_post_stati();
509
+ $statuses[] = 'any';
510
+ asort( $statuses );
511
+ }
512
+
513
+ return $statuses;
514
+ }
515
+
516
+ /**
517
+ * Takes the provided value and returns a sanitized value.
518
+ *
519
+ * @param string $value The post_type value to be sanitized.
520
+ * @param string &$err String to be initialized with error, if any.
521
+ *
522
+ * @return string The sanitized post_type value.
523
+ */
524
+ private static function sanitizePostType( $value, &$err ) {
525
+ $ret = preg_grep( '/^' . preg_quote( $value ) . '$/i', self::getPostTypes() );
526
+ $ret = reset( $ret );
527
+
528
+ if ( $ret === false ) {
529
+ $err = sprintf( self::$unary_err, 'post_type', $value );
530
+ }
531
+
532
+ return $ret;
533
+ }
534
+
535
+ /**
536
+ * @return array All registered post types.
537
+ */
538
+ public static function getPostTypes() {
539
+ static $types;
540
+ if ( ! isset( $types ) ) {
541
+ $types = get_post_types();
542
+ $types[] = 'any';
543
+ asort( $types );
544
+ }
545
+
546
+ return $types;
547
+ }
548
+
549
+ /**
550
+ * Takes the provided value and returns a sanitized value.
551
+ *
552
+ * @param string $value The relation value to be sanitized.
553
+ * @param string &$err String to be initialized with error, if any.
554
+ *
555
+ * @return string The sanitized relation value.
556
+ */
557
+ private static function sanitizeRelation( $value, &$err ) {
558
+ $ret = strtoupper( $value );
559
+
560
+ if ( ! in_array( $ret, self::getRelationOptions() ) ) {
561
+ $err = sprintf( self::$binary_err, 'relation', 'AND', 'OR', $value );
562
+ $ret = null;
563
+ }
564
+
565
+ return $ret;
566
+ }
567
+
568
+ /**
569
+ * @return array The valid options for relation parameter.
570
+ */
571
+ public static function getRelationOptions() {
572
+ return array( 'AND', 'OR' );
573
+ }
574
+
575
+ /**
576
+ * Takes the provided value and returns a sanitized value.
577
+ *
578
+ * @param string $operator The operator value to be sanitized.
579
+ *
580
+ * @return string The sanitized operator value.
581
+ */
582
+ private function sanitizeOperator( $operator ) {
583
+ $ret = strtoupper( $operator );
584
+
585
+ if ( ! in_array( $ret, self::getOperatorOptions() ) ) {
586
+ $this->errs[] = sprintf( self::$binary_err, 'IN", "NOT IN", "OR', 'AND', $operator );
587
+ $ret = null;
588
+ } else if ( $ret === 'OR' ) {
589
+ $ret = 'IN';
590
+ }
591
+
592
+ return $ret;
593
+ }
594
+
595
+ /**
596
+ * @return array The valid options for *_relation/*_operator parameter.
597
+ */
598
+ public static function getOperatorOptions() {
599
+ return array( 'IN', 'NOT IN', 'AND', 'OR' );
600
+ }
601
+
602
+ /**
603
+ * Gets all valid Documents based on the attributes passed by the user.
604
+ * NOTE: Keys in returned array are arbitrary and will vary. They should be ignored.
605
+ * @return array Contains all documents matching the query.
606
+ * @throws InvalidArgumentException Thrown when $this->errs is not empty.
607
+ */
608
+ private function getDocuments() {
609
+ $query = array(
610
+ 'numberposts' => $this->atts['limit'],
611
+ 'orderby' => $this->atts['orderby'],
612
+ 'order' => $this->atts['order'],
613
+ 'post_status' => $this->atts['post_status'],
614
+ 'post_type' => $this->atts['post_type'],
615
+ 'post_mime_type' => $this->atts['mime_types']
616
+ );
617
+
618
+ $this->setTaxa( $query );
619
+
620
+ if ( ! empty( $this->errs ) ) {
621
+ throw new InvalidArgumentException();
622
+ }
623
+
624
+ // NOTE: Derived from gallery shortcode
625
+ if ( ! empty( $this->atts['include'] ) ) {
626
+ $query['include'] = $this->atts['include'];
627
+ $attachments = get_posts( $query );
628
+ } else {
629
+ // id == 0 => all attachments w/o a parent
630
+ // id == null => all matched attachments
631
+ $query['post_parent'] = $this->atts['id'];
632
+ if ( ! empty( $exclude ) ) {
633
+ $query['exclude'] = $this->atts['exclude'];
634
+ }
635
+
636
+ $attachments = get_children( $query );
637
+ }
638
+
639
+ return $attachments;
640
+ }
641
+
642
+ /**
643
+ * Function loops through all attributes passed that did not match
644
+ * self::$defaults. If they are the name of a taxonomy, they are plugged
645
+ * into the query, otherwise $this->errs is appended with an error string.
646
+ *
647
+ * @param array $query Query to insert tax query into.
648
+ */
649
+ private function setTaxa( &$query ) {
650
+ if ( ! empty( $this->taxa ) ) {
651
+ $taxa = array( 'relation' => $this->atts['relation'] );
652
+ $operator = array();
653
+ $suffix = array( 'relation', 'operator' );
654
+ $pattern = '/(.+)_(?:' . implode( '|', $suffix ) . ')$/i';
655
+
656
+ // find any relations for taxa
657
+ $iterable = $this->taxa;
658
+ foreach ( $iterable as $key => $value ) {
659
+ if ( preg_match( $pattern, $key, $matches ) ) {
660
+ $base = $matches[1];
661
+ if ( array_key_exists( $base, $this->taxa ) ) {
662
+ $operator[ $base ] = self::sanitizeOperator( $value );
663
+ unset( $this->taxa[ $key ] );
664
+ }
665
+ }
666
+ }
667
+
668
+ // build tax query
669
+ foreach ( $this->taxa as $taxon => $terms ) {
670
+ $terms = $this->getTermIdsByNames( $taxon, explode( ',', $terms ) );
671
+
672
+ $taxa[] = array(
673
+ 'taxonomy' => $taxon,
674
+ 'field' => 'id',
675
+ 'terms' => $terms,
676
+ 'operator' => isset( $operator[ $taxon ] ) ? $operator[ $taxon ] : 'IN'
677
+ );
678
+ }
679
+
680
+ // create nested structure
681
+ $query['tax_query'] = $taxa;
682
+ }
683
+ }
684
+
685
+ /*==========================================================================
686
+ * HELPER FUNCTIONS
687
+ *=========================================================================*/
688
+
689
+ /**
690
+ * Returns an array of term ids when provided with a list of term names.
691
+ * Also appends an entry onto $errs if any invalid names are found.
692
+ *
693
+ * @param string $taxon The taxon these terms are a member of.
694
+ * @param array $term_names Terms to retrieve.
695
+ *
696
+ * @return array All matched terms.
697
+ */
698
+ private function getTermIdsByNames( $taxon, $term_names ) {
699
+ return $this->getTermXByNames( 'term_id', $taxon, $term_names );
700
+ }
701
+
702
+ /**
703
+ * Returns an array of term slugs when provided with a list of term names.
704
+ * Also appends an entry onto $errs if any invalid names are found.
705
+ *
706
+ * @param string $taxon The taxon these terms are a member of.
707
+ * @param array $term_names Terms to retrieve.
708
+ *
709
+ * @return array All matched terms.
710
+ */
711
+ private function getTermSlugsByNames( $taxon, $term_names ) {
712
+ return $this->getTermXByNames( 'slug', $taxon, $term_names );
713
+ }
714
+
715
+ /**
716
+ * Returns a list of x, where x may be any of the fields within a
717
+ * term object, when provided with a list of term names (not slugs).
718
+ * (http://codex.wordpress.org/Function_Reference/get_term_by#Return_Values)
719
+ *
720
+ * Also appends an entry onto $errs if any invalid names are found.
721
+ *
722
+ * @param string $x Field to retrieve from matched term.
723
+ * @param string $taxon The taxon these terms are a member of.
724
+ * @param array $term_names Terms to retrieve.
725
+ *
726
+ * @return array All matched terms.
727
+ */
728
+ private function getTermXByNames( $x, $taxon, $term_names ) {
729
+ $ret = array();
730
+ $valid = true;
731
+
732
+ // taxons may optionally be prefixed by 'tax_' --
733
+ // this is only useful when avoiding collisions with other attributes
734
+ if ( ! taxonomy_exists( $taxon ) ) {
735
+ $tmp = preg_replace( '/^tax_(.*)/', '$1', $taxon, 1, $count );
736
+ if ( $count > 0 && taxonomy_exists( $tmp ) ) {
737
+ $taxon = $tmp;
738
+ } else {
739
+ $this->errs[] = sprintf( self::$unary_err, 'taxon', $taxon );
740
+ $valid = false;
741
+ }
742
+ }
743
+
744
+ // only check terms if we first have a valid taxon
745
+ if ( $valid ) {
746
+ foreach ( $term_names as $name ) {
747
+ if ( ( $term = get_term_by( 'name', $name, $taxon ) ) ) {
748
+ $ret[] = $term->{$x};
749
+ } else {
750
+ $this->errs[] = sprintf( __( '%s is not a valid term name in %s.',
751
+ 'document-gallery' ), $name, $taxon );
752
+ }
753
+ }
754
+ }
755
+
756
+ return $ret;
757
+ }
758
+
759
+ /**
760
+ * @param string $string To take second char from.
761
+ *
762
+ * @return string Capitalized second char of given string.
763
+ */
764
+ private static function secondCharToUpper( $string ) {
765
+ return strtoupper( $string[1] );
766
+ }
767
+
768
+ /**
769
+ * Function returns false for positive ints, true otherwise.
770
+ *
771
+ * @param string $var could be anything.
772
+ *
773
+ * @return boolean indicating whether $var is not a positive int.
774
+ */
775
+ private static function negativeInt( $var ) {
776
+ return ! is_numeric( $var ) // isn't numeric
777
+ || (int) $var != $var // isn't int
778
+ || (int) $var < 0; // isn't positive
779
+ }
780
+
781
+ /*==========================================================================
782
+ * OUTPUT HTML STRING
783
+ *=========================================================================*/
784
+
785
+ /**
786
+ * @filter dg_gallery_template Allows the user to filter anything content surrounding the generated gallery.
787
+ * @filter dg_row_template Filters the outer DG wrapper HTML. Passes a single
788
+ * bool value indicating whether the gallery is using descriptions or not.
789
+ * @return string HTML representing this Gallery.
790
+ */
791
+ public function __toString() {
792
+ static $instance = 0;
793
+ $instance ++;
794
+
795
+ static $find = null;
796
+ if ( is_null( $find ) ) {
797
+ $find = array( '%class%', '%icons%' );
798
+ }
799
+
800
+ if ( ! empty( $this->errs ) ) {
801
+ return '<p>' . implode( '</p><p>', $this->errs ) . '</p>';
802
+ }
803
+
804
+ if ( empty( $this->docs ) ) {
805
+ return self::$no_docs;
806
+ }
807
+
808
+ $selector = "document-gallery-$instance";
809
+ $template =
810
+ "<div id='$selector' class='%class%'>" . PHP_EOL .
811
+ '%icons%' . PHP_EOL .
812
+ '</div>' . PHP_EOL;
813
+
814
+ $icon_wrapper = apply_filters(
815
+ 'dg_row_template',
816
+ $template,
817
+ $this->useDescriptions() );
818
+
819
+ $core = '';
820
+ $classes = array( 'document-icon-wrapper' );
821
+ if ( $this->useDescriptions() ) {
822
+ $classes[] = 'descriptions';
823
+ }
824
+
825
+ $repl = array( implode( ' ', $classes ) );
826
+ if ( $this->useDescriptions() ) {
827
+ foreach ( $this->docs as $doc ) {
828
+ $repl[1] = $doc;
829
+ $core .= str_replace( $find, $repl, $icon_wrapper );
830
+ }
831
+ } else {
832
+ global $dg_gallery_style;
833
+
834
+ $count = count( $this->docs );
835
+ $cols = ! is_null( $this->atts['columns'] ) ? $this->atts['columns'] : $count;
836
+
837
+ if ( apply_filters( 'dg_use_default_gallery_style', true ) ) {
838
+ $itemwidth = $cols > 0 ? ( floor( 100 / $cols ) - 1 ) : 100;
839
+ $core .= "<style type='text/css'>#$selector .document-icon{width:$itemwidth%}</style>";
840
+ }
841
+
842
+ for ( $i = 0; $i < $count; $i += $cols ) {
843
+ $repl[1] = '';
844
+
845
+ $min = min( $i + $cols, $count );
846
+ for ( $x = $i; $x < $min; $x ++ ) {
847
+ $repl[1] .= $this->docs[ $x ];
848
+ }
849
+
850
+ $core .= str_replace( $find, $repl, $icon_wrapper );
851
+ }
852
+ }
853
+
854
+ // allow user to wrap gallery output
855
+ $gallery = apply_filters( 'dg_gallery_template', '%rows%', $this->useDescriptions() );
856
+
857
+ return self::$comment . str_replace( '%rows%', $core, $gallery );
858
+ }
859
+ }
inc/class-image-editor-imagick.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  include_once DG_WPINC_PATH . 'class-wp-image-editor.php';
5
  include_once DG_WPINC_PATH . 'class-wp-image-editor-imagick.php';
@@ -11,56 +11,56 @@ include_once DG_WPINC_PATH . 'class-wp-image-editor-imagick.php';
11
  * @author drossiter
12
  */
13
  class DG_Image_Editor_Imagick extends WP_Image_Editor_Imagick {
14
- /**
15
- * @var int The 0-indexed pg number
16
- */
17
- private $pg;
18
 
19
- /**
20
- * @param str $file
21
- * @param int $pg The 0-indexed pg number to edit.
22
- */
23
- public function __construct($file, $pg = null) {
24
- parent::__construct($file);
25
- $this->pg = $pg;
26
- }
27
 
28
- /**
29
- * Loads the filepath into Imagick object.
30
- */
31
- public function load() {
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
- $err = __('Failed to set Imagick page number', 'document-gallery');
38
-
39
- // setIteratorIndex() should return false on failure, but I've found
40
- // reports of it throwing an error so handling both cases.
41
- // NOTE: I've also seen it fail and return true, so we may not
42
- // log anything on failure...
43
- try {
44
- if (!$this->image->setIteratorIndex($this->pg)) {
45
- DG_Logger::writeLog(DG_LogLevel::Error, $err . '.');
46
- }
47
- } catch(Exception $e) {
48
- DG_Logger::writeLog(DG_LogLevel::Error, $err . ': ' . $e->getMessage());
49
- }
50
- }
51
 
52
- return $ret;
53
- }
54
-
55
- /**
56
- * @return array/bool The formats supported by Imagick, or false
57
- */
58
- public static function query_formats() {
59
- try {
60
- return @Imagick::queryFormats();
61
- } catch (Exception $ex) {
62
- return false;
63
- }
64
- }
65
- }
66
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  include_once DG_WPINC_PATH . 'class-wp-image-editor.php';
5
  include_once DG_WPINC_PATH . 'class-wp-image-editor-imagick.php';
11
  * @author drossiter
12
  */
13
  class DG_Image_Editor_Imagick extends WP_Image_Editor_Imagick {
14
+ /**
15
+ * @var int The 0-indexed pg number
16
+ */
17
+ private $pg;
18
 
19
+ /**
20
+ * @param string $file
21
+ * @param int $pg The 0-indexed pg number to edit.
22
+ */
23
+ public function __construct( $file, $pg = null ) {
24
+ parent::__construct( $file );
25
+ $this->pg = $pg;
26
+ }
27
 
28
+ /**
29
+ * Loads the filepath into Imagick object.
30
+ */
31
+ public function load() {
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
41
+ // reports of it throwing an error so handling both cases.
42
+ // NOTE: I've also seen it fail and return true, so we may not
43
+ // log anything on failure...
44
+ try {
45
+ if ( ! $this->image->setIteratorIndex( $this->pg ) ) {
46
+ DG_Logger::writeLog( DG_LogLevel::Error, $err . '.' );
47
+ }
48
+ } catch ( Exception $e ) {
49
+ DG_Logger::writeLog( DG_LogLevel::Error, $err . ': ' . $e->getMessage() );
50
+ }
51
+ }
52
+
53
+ return $ret;
54
+ }
55
+
56
+ /**
57
+ * @return array|bool The formats supported by Imagick, or false
58
+ */
59
+ public static function query_formats() {
60
+ try {
61
+ return @Imagick::queryFormats();
62
+ } catch ( Exception $ex ) {
63
+ return false;
64
+ }
65
+ }
66
+ }
inc/class-logger.php CHANGED
@@ -1,337 +1,354 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /**
5
  * Encapsulates the logic required to maintain and read log files.
6
  */
7
  class DG_Logger {
8
-
9
- /**
10
- * @var string Name of the log purge action.
11
- */
12
- const PurgeLogsAction = 'document-gallery_purge-logs';
13
-
14
- /**
15
- * Appends DG log file if logging is enabled.
16
- *
17
- * @param int The level of serverity (should be passed using DG_LogLevel consts).
18
- * @param string $entry Value to be logged.
19
- * @param bool $stacktrace Whether to include full stack trace.
20
- * @param bool $force Whether to ignore logging flag and log no matter what.
21
- * Only should be used when the user *must* know about something.
22
- */
23
- public static function writeLog($level, $entry, $stacktrace = false, $force = false) {
24
- if ($force || self::logEnabled()) {
25
- $fp = fopen(self::getLogFileName(), 'a');
26
- if (false !== $fp) {
27
- $fields = array(time(), $level, $entry);
28
-
29
- $trace = debug_backtrace(false);
30
- if ($stacktrace) {
31
- unset($trace[0]);
32
- $fields[] = self::getStackTraceString($trace);
33
- } else {
34
- // Remove first item from backtrace as it's this function which is redundant.
35
- $caller = $trace[1];
36
- $caller = (isset($caller['class']) ? $caller['class'] : '') . $caller['type'] . $caller['function'];
37
- $fields[2] = '(' . $caller . ') ' . $fields[2];
38
- }
39
-
40
- fputcsv($fp, $fields);
41
- fclose($fp);
42
- } // TODO: else
43
- }
44
- }
45
-
46
- /**
47
- * Reads the current blog's log file, placing the values in to a 2-dimensional array.
48
- * @param int $skip How many lines to skip before returning rows.
49
- * @param int $limit Max number of lines to read.
50
- * @return multitype:multitype:string|null The rows from the log file or null if failed to open log.
51
- */
52
- public static function readLog($skip = 0, $limit = PHP_INT_MAX) {
53
- $ret = null;
54
- $fp = @fopen(self::getLogFileName(), 'r');
55
-
56
- if ($fp !== false) {
57
- $ret = array();
58
- while (count($ret) < $limit && false !== ($fields = fgetcsv($fp))) {
59
- if ($skip > 0) {
60
- $skip--;
61
- continue;
62
- }
63
-
64
- if (!is_null($fields)) {
65
- $ret[] = $fields;
66
- }
67
- }
68
-
69
- @fclose($fp);
70
- }
71
-
72
- return $ret;
73
- }
74
-
75
- /**
76
- * Clears the log file for the active blog.
77
- */
78
- public static function clearLog() {
79
- // we don't care if the file actually exists -- it won't when we're done
80
- @unlink(self::getLogFileName());
81
- }
82
-
83
- /**
84
- * Clears the log file for all blogs.
85
- */
86
- public static function clearLogs() {
87
- // we don't care if the files actually exist -- they won't when we're done
88
- foreach (self::getLogFileNames() as $file) {
89
- @unlink($file);
90
- }
91
- }
92
-
93
- /**
94
- * Truncates all blog logs to the current purge interval.
95
- *
96
- * TODO: This is a memory hog. Consider switching to stream filter.
97
- */
98
- public static function purgeExpiredEntries() {
99
- self::writeLog(DG_LogLevel::Detail, 'Beginning scheduled log file purge.');
100
-
101
- $blogs = array(null);
102
- if (is_multisite()) {
103
- global $wpdb;
104
- $blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
105
- }
106
-
107
- // truncate each blog's log file
108
- $time = time();
109
- foreach ($blogs as $blog) {
110
- $blog_num = !is_null($blog) ? $blog : get_current_blog_id();
111
- $options = self::getOptions($blog);
112
- $purge_interval = $options['purge_interval'] * DAY_IN_SECONDS;
113
-
114
- // purging is disabled for this blog
115
- if ($purge_interval <= 0) continue;
116
-
117
- // do perge for this blog
118
- $file = self::getLogFileName($blog_num);
119
- if (file_exists($file)) {
120
- $fp = @fopen($file, 'r');
121
-
122
- if ($fp !== false) {
123
- $truncate = false;
124
- $offset = 0;
125
-
126
- // find the first non-expired entry
127
- while (($fields = fgetcsv($fp)) !== false) {
128
- if (!is_null($fields) && $time > ($fields[0] + $purge_interval)) {
129
- // we've reached the recent entries -- nothing beyond here will be removed
130
- break;
131
- }
132
-
133
- $truncate = true;
134
- $offset = @ftell($fp);
135
- if (false === $offset) {
136
- break;
137
- }
138
- }
139
-
140
- @fclose($fp);
141
-
142
- // if any expired entries exist -- remove them from the file
143
- if ($truncate) {
144
- self::writeLog(DG_LogLevel::Detail, "Purging log entries for blog #$blog_num.");
145
- $data = file_get_contents($file, false, null, $offset);
146
- file_put_contents($file, $data, LOCK_EX);
147
- }
148
- }
149
- }
150
- }
151
- }
152
-
153
- /**
154
- * Generally not necessary to call external to this class -- only use if generating
155
- * log entry will take significant resources and you want to avoid this operation
156
- * if it will not actually be logged.
157
- *
158
- * @return bool Whether debug logging is currently enabled.
159
- */
160
- public static function logEnabled() {
161
- $options = self::getOptions();
162
- return $options['enabled'];
163
- }
164
-
165
- /**
166
- * Gets logging options.
167
- *
168
- * @param int $blog ID of the blog to be retrieved in multisite env.
169
- * @return multitype:unknown Logger options for the blog.
170
- */
171
- public static function getOptions($blog = null) {
172
- $options = DocumentGallery::getOptions($blog);
173
- return $options['logging'];
174
- }
175
-
176
- /**
177
- * @param $id int The ID of the blog to retrieve log file name for. Defaults to current blog.
178
- * @return string Full path to log file for current blog.
179
- */
180
- private static function getLogFileName($id = null) {
181
- $id = !is_null($id) ? $id : get_current_blog_id();
182
- return DG_PATH . 'log/' . $id . '.log';
183
- }
184
-
185
- /**
186
- * @param multitype $trace Array containing stack trace to be converted to string.
187
- * @return string The stack trace in human-readable form.
188
- */
189
- private static function getStackTraceString($trace) {
190
- $trace_str = '';
191
- $i = 1;
192
-
193
- foreach($trace as $node) {
194
- $trace_str .= "#$i ";
195
-
196
- $file = '';
197
- if (isset($node['file'])) {
198
- // convert to relative path from WP root
199
- $file = str_replace(ABSPATH, '', $node['file']);
200
- }
201
-
202
- if (isset($node['line'])) {
203
- $file .= "({$node['line']})";
204
- }
205
-
206
- if ($file) {
207
- $trace_str .= "$file: ";
208
- }
209
-
210
- if(isset($node['class'])) {
211
- $trace_str .= "{$node['class']}{$node['type']}";
212
- }
213
-
214
- if (isset($node['function'])) {
215
- $args = '';
216
- if (isset($node['args'])) {
217
- $args = implode(', ', array_map(array(__CLASS__, 'print_r'), $node['args']));
218
- }
219
-
220
- $trace_str .= "{$node['function']}($args)" . PHP_EOL;
221
- }
222
- $i++;
223
- }
224
-
225
- return $trace_str;
226
- }
227
-
228
- /**
229
- * Wraps print_r passing true for the return argument.
230
- * @param unknown $v Value to be printed.
231
- * @return string Printed value.
232
- */
233
- private static function print_r($v) {
234
- return print_r($v, true);
235
- }
236
-
237
- /**
238
- * Blocks instantiation. All functions are static.
239
- */
240
- private function __construct() {
241
-
242
- }
 
 
 
 
 
 
 
 
 
 
 
243
  }
244
 
245
  /**
246
  * LogLevel acts as an enumeration of all possible log levels.
247
  */
248
  class DG_LogLevel {
249
- /**
250
- * @var int Log level for anything that doesn't indicate a problem.
251
- */
252
- const Detail = 0;
253
-
254
- /**
255
- * @var int Log level for anything that is a minor issue.
256
- */
257
- const Warning = 1;
258
-
259
- /**
260
- * @var int Log level for when something went wrong.
261
- */
262
- const Error = 2;
263
-
264
- /**
265
- * @var ReflectionClass Backs the getter.
266
- */
267
- private static $ref = null;
268
-
269
- /**
270
- * @return ReflectionClass Instance of reflection class for this class.
271
- */
272
- private static function getReflectionClass() {
273
- if (is_null(self::$ref)) {
274
- self::$ref = new ReflectionClass(__CLASS__);
275
- }
276
-
277
- return self::$ref;
278
- }
279
-
280
- /**
281
- * @var multitype Backs the getter.
282
- */
283
- private static $levels = null;
284
-
285
- /**
286
- * @return multitype Associative array containing all log level names mapped to their int value.
287
- */
288
- public static function getLogLevels() {
289
- if (is_null(self::$levels)) {
290
- $ref = self::getReflectionClass();
291
- self::$levels = $ref->getConstants();
292
- }
293
-
294
- return self::$levels;
295
- }
296
-
297
- /**
298
- * @param string $name Name to be checked for validity.
299
- * @return bool Whether given name represents valid log level.
300
- */
301
- public static function isValidName($name) {
302
- return array_key_exists($name, self::getLogLevels());
303
- }
304
-
305
- /**
306
- * @param int $value Value to be checked for validity.
307
- * @return bool Whether given value represents valid log level.
308
- */
309
- public static function isValidValue($value) {
310
- return (false !== array_search($value, self::getLogLevels()));
311
- }
312
-
313
- /**
314
- * @param string $name The name for which to retrieve a value.
315
- * @return int|null The value associated with the given name.
316
- */
317
- public static function getValueByName($name) {
318
- $levels = self::getLogLevels();
319
- return array_key_exists($name, self::getLogLevels()) ? $levels[$name] : null;
320
- }
321
-
322
- /**
323
- * @param int $value The value for which to retrieve a name.
324
- * @return string|null The name associated with the given value.
325
- */
326
- public static function getNameByValue($value) {
327
- $ret = array_search($value, self::getLogLevels());
328
- return (false !== $ret) ? $ret : null;
329
- }
330
-
331
- /**
332
- * Blocks instantiation. All functions are static.
333
- */
334
- private function __construct() {
335
-
336
- }
 
 
 
 
 
 
337
  }
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /**
5
  * Encapsulates the logic required to maintain and read log files.
6
  */
7
  class DG_Logger {
8
+
9
+ /**
10
+ * @var string Name of the log purge action.
11
+ */
12
+ const PurgeLogsAction = 'document-gallery_purge-logs';
13
+
14
+ /**
15
+ * Appends DG log file if logging is enabled.
16
+ *
17
+ * @param int $level The level of serverity (should be passed using DG_LogLevel consts).
18
+ * @param string $entry Value to be logged.
19
+ * @param bool $stacktrace Whether to include full stack trace.
20
+ * @param bool $force Whether to ignore logging flag and log no matter what.
21
+ * Only should be used when the user *must* know about something.
22
+ */
23
+ public static function writeLog( $level, $entry, $stacktrace = false, $force = false ) {
24
+ if ( $force || self::logEnabled() ) {
25
+ $fp = fopen( self::getLogFileName(), 'a' );
26
+ if ( false !== $fp ) {
27
+ $fields = array( time(), $level, $entry );
28
+
29
+ $trace = debug_backtrace( false );
30
+ if ( $stacktrace ) {
31
+ unset( $trace[0] );
32
+ $fields[] = self::getStackTraceString( $trace );
33
+ } else {
34
+ // Remove first item from backtrace as it's this function which is redundant.
35
+ $caller = $trace[1];
36
+ $caller = ( isset( $caller['class'] ) ? $caller['class'] : '' ) . $caller['type'] . $caller['function'];
37
+ $fields[2] = '(' . $caller . ') ' . $fields[2];
38
+ }
39
+
40
+ fputcsv( $fp, $fields );
41
+ fclose( $fp );
42
+ } // TODO: else
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Reads the current blog's log file, placing the values in to a 2-dimensional array.
48
+ *
49
+ * @param int $skip How many lines to skip before returning rows.
50
+ * @param int $limit Max number of lines to read.
51
+ *
52
+ * @return array|null The rows from the log file or null if failed to open log.
53
+ */
54
+ public static function readLog( $skip = 0, $limit = PHP_INT_MAX ) {
55
+ $ret = null;
56
+ $fp = @fopen( self::getLogFileName(), 'r' );
57
+
58
+ if ( $fp !== false ) {
59
+ $ret = array();
60
+ while ( count( $ret ) < $limit && false !== ( $fields = fgetcsv( $fp ) ) ) {
61
+ if ( $skip > 0 ) {
62
+ $skip --;
63
+ continue;
64
+ }
65
+
66
+ if ( ! is_null( $fields ) ) {
67
+ $ret[] = $fields;
68
+ }
69
+ }
70
+
71
+ @fclose( $fp );
72
+ }
73
+
74
+ return $ret;
75
+ }
76
+
77
+ /**
78
+ * Clears the log file for the active blog.
79
+ */
80
+ public static function clearLog() {
81
+ // we don't care if the file actually exists -- it won't when we're done
82
+ @unlink( self::getLogFileName() );
83
+ }
84
+
85
+ /**
86
+ * Clears the log file for all blogs.
87
+ */
88
+ public static function clearLogs() {
89
+ // we don't care if the files actually exist -- they won't when we're done
90
+ foreach ( DG_Util::getBlogIds() as $id ) {
91
+ @unlink( self::getLogFileName( $id ) );
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Truncates all blog logs to the current purge interval.
97
+ *
98
+ * TODO: This is a memory hog. Consider switching to stream filter.
99
+ */
100
+ public static function purgeExpiredEntries() {
101
+ self::writeLog( DG_LogLevel::Detail, 'Beginning scheduled log file purge.' );
102
+
103
+ $blogs = array( null );
104
+ if ( is_multisite() ) {
105
+ $blogs = DG_Util::getBlogIds();
106
+ }
107
+
108
+ // truncate each blog's log file
109
+ $time = time();
110
+ foreach ( $blogs as $blog ) {
111
+ $blog_num = ! is_null( $blog ) ? $blog : get_current_blog_id();
112
+ $options = self::getOptions( $blog );
113
+ $purge_interval = $options['purge_interval'] * DAY_IN_SECONDS;
114
+
115
+ // purging is disabled for this blog
116
+ if ( $purge_interval <= 0 ) {
117
+ continue;
118
+ }
119
+
120
+ // do perge for this blog
121
+ $file = self::getLogFileName( $blog_num );
122
+ if ( file_exists( $file ) ) {
123
+ $fp = @fopen( $file, 'r' );
124
+
125
+ if ( $fp !== false ) {
126
+ $truncate = false;
127
+ $offset = 0;
128
+
129
+ // find the first non-expired entry
130
+ while ( ( $fields = fgetcsv( $fp ) ) !== false ) {
131
+ if ( ! is_null( $fields ) && $time > ( $fields[0] + $purge_interval ) ) {
132
+ // we've reached the recent entries -- nothing beyond here will be removed
133
+ break;
134
+ }
135
+
136
+ $truncate = true;
137
+ $offset = @ftell( $fp );
138
+ if ( false === $offset ) {
139
+ break;
140
+ }
141
+ }
142
+
143
+ @fclose( $fp );
144
+
145
+ // if any expired entries exist -- remove them from the file
146
+ if ( $truncate ) {
147
+ self::writeLog( DG_LogLevel::Detail, "Purging log entries for blog #$blog_num." );
148
+ $data = file_get_contents( $file, false, null, $offset );
149
+ file_put_contents( $file, $data, LOCK_EX );
150
+ }
151
+ }
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Generally not necessary to call external to this class -- only use if generating
158
+ * log entry will take significant resources and you want to avoid this operation
159
+ * if it will not actually be logged.
160
+ *
161
+ * @return bool Whether debug logging is currently enabled.
162
+ */
163
+ public static function logEnabled() {
164
+ $options = self::getOptions();
165
+
166
+ return $options['enabled'];
167
+ }
168
+
169
+ /**
170
+ * Gets logging options.
171
+ *
172
+ * @param int $blog ID of the blog to be retrieved in multisite env.
173
+ *
174
+ * @return array Logger options for the blog.
175
+ */
176
+ public static function getOptions( $blog = null ) {
177
+ $options = DocumentGallery::getOptions( $blog );
178
+
179
+ return $options['logging'];
180
+ }
181
+
182
+ /**
183
+ * @param $id int The ID of the blog to retrieve log file name for. Defaults to current blog.
184
+ *
185
+ * @return string Full path to log file for current blog.
186
+ */
187
+ private static function getLogFileName( $id = null ) {
188
+ $id = ! is_null( $id ) ? $id : get_current_blog_id();
189
+
190
+ return DG_PATH . 'log/' . $id . '.log';
191
+ }
192
+
193
+ /**
194
+ * @param array $trace Array containing stack trace to be converted to string.
195
+ *
196
+ * @return string The stack trace in human-readable form.
197
+ */
198
+ private static function getStackTraceString( $trace ) {
199
+ $trace_str = '';
200
+ $i = 1;
201
+
202
+ foreach ( $trace as $node ) {
203
+ $trace_str .= "#$i ";
204
+
205
+ $file = '';
206
+ if ( isset( $node['file'] ) ) {
207
+ // convert to relative path from WP root
208
+ $file = str_replace( ABSPATH, '', $node['file'] );
209
+ }
210
+
211
+ if ( isset( $node['line'] ) ) {
212
+ $file .= "({$node['line']})";
213
+ }
214
+
215
+ if ( $file ) {
216
+ $trace_str .= "$file: ";
217
+ }
218
+
219
+ if ( isset( $node['class'] ) ) {
220
+ $trace_str .= "{$node['class']}{$node['type']}";
221
+ }
222
+
223
+ if ( isset( $node['function'] ) ) {
224
+ $args = '';
225
+ if ( isset( $node['args'] ) ) {
226
+ $args = implode( ', ', array_map( array( __CLASS__, 'print_r' ), $node['args'] ) );
227
+ }
228
+
229
+ $trace_str .= "{$node['function']}($args)" . PHP_EOL;
230
+ }
231
+ $i ++;
232
+ }
233
+
234
+ return $trace_str;
235
+ }
236
+
237
+ /**
238
+ * Wraps print_r passing true for the return argument.
239
+ *
240
+ * @param unknown $v Value to be printed.
241
+ *
242
+ * @return string Printed value.
243
+ */
244
+ private static function print_r( $v ) {
245
+ return print_r( $v, true );
246
+ }
247
+
248
+ /**
249
+ * Blocks instantiation. All functions are static.
250
+ */
251
+ private function __construct() {
252
+
253
+ }
254
  }
255
 
256
  /**
257
  * LogLevel acts as an enumeration of all possible log levels.
258
  */
259
  class DG_LogLevel {
260
+ /**
261
+ * @var int Log level for anything that doesn't indicate a problem.
262
+ */
263
+ const Detail = 0;
264
+
265
+ /**
266
+ * @var int Log level for anything that is a minor issue.
267
+ */
268
+ const Warning = 1;
269
+
270
+ /**
271
+ * @var int Log level for when something went wrong.
272
+ */
273
+ const Error = 2;
274
+
275
+ /**
276
+ * @var ReflectionClass Backs the getter.
277
+ */
278
+ private static $ref = null;
279
+
280
+ /**
281
+ * @return ReflectionClass Instance of reflection class for this class.
282
+ */
283
+ private static function getReflectionClass() {
284
+ if ( is_null( self::$ref ) ) {
285
+ self::$ref = new ReflectionClass( __CLASS__ );
286
+ }
287
+
288
+ return self::$ref;
289
+ }
290
+
291
+ /**
292
+ * @var array Backs the getter.
293
+ */
294
+ private static $levels = null;
295
+
296
+ /**
297
+ * @return array Associative array containing all log level names mapped to their int value.
298
+ */
299
+ public static function getLogLevels() {
300
+ if ( is_null( self::$levels ) ) {
301
+ $ref = self::getReflectionClass();
302
+ self::$levels = $ref->getConstants();
303
+ }
304
+
305
+ return self::$levels;
306
+ }
307
+
308
+ /**
309
+ * @param string $name Name to be checked for validity.
310
+ *
311
+ * @return bool Whether given name represents valid log level.
312
+ */
313
+ public static function isValidName( $name ) {
314
+ return array_key_exists( $name, self::getLogLevels() );
315
+ }
316
+
317
+ /**
318
+ * @param int $value Value to be checked for validity.
319
+ *
320
+ * @return bool Whether given value represents valid log level.
321
+ */
322
+ public static function isValidValue( $value ) {
323
+ return ( false !== array_search( $value, self::getLogLevels() ) );
324
+ }
325
+
326
+ /**
327
+ * @param string $name The name for which to retrieve a value.
328
+ *
329
+ * @return int|null The value associated with the given name.
330
+ */
331
+ public static function getValueByName( $name ) {
332
+ $levels = self::getLogLevels();
333
+
334
+ return array_key_exists( $name, self::getLogLevels() ) ? $levels[ $name ] : null;
335
+ }
336
+
337
+ /**
338
+ * @param int $value The value for which to retrieve a name.
339
+ *
340
+ * @return string|null The name associated with the given value.
341
+ */
342
+ public static function getNameByValue( $value ) {
343
+ $ret = array_search( $value, self::getLogLevels() );
344
+
345
+ return ( false !== $ret ) ? $ret : null;
346
+ }
347
+
348
+ /**
349
+ * Blocks instantiation. All functions are static.
350
+ */
351
+ private function __construct() {
352
+
353
+ }
354
  }
inc/class-setup.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /**
5
  * Holds functions that handle DG setup / uninstallation.
@@ -8,402 +8,415 @@ defined('WPINC') OR exit;
8
  */
9
  class DG_Setup {
10
 
11
- /**
12
- * The default DG options to be used on install and when validating structure of options.
13
- * @param $skeleton bool When true, expensive values are not calculated. Only keys may be trusted when returning skeleton.
14
- * @return array Contains default options for DG.
15
- */
16
- public static function getDefaultOptions($skeleton = false) {
17
- include_once DG_PATH . 'inc/class-thumber.php';
18
-
19
- $gs = $donate_link = null;
20
- if (!$skeleton) {
21
- $gs = DG_Thumber::getGhostscriptExecutable();
22
- $donate_link = self::getDonateLink();
23
- }
24
-
25
- return array(
26
- 'thumber' => array(
27
- // cached thumbnails, keyed by post ID
28
- 'thumbs' => array(),
29
-
30
- // Ghostscript path
31
- 'gs' => $gs,
32
-
33
- // which thumbnail generation methods are available
34
- 'active' => DG_Thumber::getDefaultThumbers($skeleton),
35
-
36
- // max width to generate thumbnails
37
- 'width' => 200,
38
-
39
- // max height to generate thumbnails
40
- 'height' => 200,
41
-
42
- // time after which to quite trying to generate new thumbanils for gallery
43
- 'timeout' => 30
44
- ),
45
- 'gallery' => array(
46
- // default: link directly to file (true to link to attachment pg)
47
- 'attachment_pg' => false,
48
-
49
- // include the attachment description in output
50
- 'descriptions' => false,
51
-
52
- // include thumbnail of actual document in gallery display
53
- 'fancy' => true,
54
-
55
- // comma-delimited list of all mime types to be included
56
- 'mime_types' => implode(',', self::getDefaultMimeTypes()),
57
-
58
- // ascending/descending order for included documents
59
- 'order' => 'ASC',
60
-
61
- // which property to order by
62
- 'orderby' => 'menu_order',
63
-
64
- // AND or OR
65
- 'relation' => 'AND',
66
-
67
- // the status the post must be in when returned by DG
68
- 'post_status' => 'any',
69
-
70
- // the type of post to be returned
71
- 'post_type' => 'attachment',
72
-
73
- // the max number of thumbnails to return
74
- 'limit' => -1,
75
-
76
- // # of columns to be used in gallery
77
- 'columns' => 4,
78
-
79
- // whether to open documents in new window
80
- 'new_window' => false
81
- ),
82
- 'css' => array(
83
- // plain text of CSS to be edited by user
84
- 'text' => '',
85
-
86
- // "minified" text to be rendered on pages
87
- 'minified' => ''
88
- ),
89
-
90
- 'meta' => array(
91
- // current DG version
92
- 'version' => DG_VERSION,
93
-
94
- // URL to donate to plugin development
95
- 'donate_link' => $donate_link
96
- ),
97
-
98
- // logging options
99
- 'logging' => array(
100
- // TODO: more granular -- log_level instead of blanket enable/disable
101
- 'enabled' => false,
102
-
103
- // max age of log entry (days)
104
- 'purge_interval' => 7
105
- ),
106
-
107
- // whether to validate DG option structure on save
108
- 'validation' => false
109
- );
110
- }
111
-
112
- /**
113
- * @return multitype:string The default MIME types to include in gallery.
114
- */
115
- public static function getDefaultMimeTypes() {
116
- return array('application', 'video', 'text', 'audio', 'image');
117
- }
118
-
119
- /**
120
- * Runs every page load, updates as needed.
121
- */
122
- public static function maybeUpdate() {
123
- global $dg_options;
124
-
125
- // do update
126
- if (!is_null($dg_options) && (isset($dg_options['version']) || DG_VERSION !== $dg_options['meta']['version'])) {
127
- $blogs = array(null);
128
-
129
- if (is_multisite()) {
130
- global $wpdb;
131
- $blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
132
- }
133
-
134
- foreach ($blogs as $blog) {
135
- self::_update($blog);
136
- }
137
- }
138
- }
139
-
140
- /**
141
- * Runs when update is needed, updating the given blog. If $blog is null,
142
- * active blog is updated.
143
- * @param int $blog Blog to update or null if updating current blog.
144
- */
145
- private static function _update($blog) {
146
- $options = DocumentGallery::getOptions($blog);
147
- if (is_null($options)) return;
148
-
149
- // version-specific updates
150
- self::twoPointTwo($options);
151
- self::twoPointThree($options);
152
- self::threePointZeroBeta($options);
153
- self::threePointOne($options);
154
- self::threePointTwo($options);
155
-
156
- // update plugin meta data
157
- $options['meta']['version'] = DG_VERSION;
158
- $options['meta']['donate_link'] = self::getDonateLink();
159
-
160
- // remove previously-failed thumbs
161
- $thumbs = $options['thumber']['thumbs'];
162
- foreach ($thumbs as $k => $v) {
163
- if (empty($v['thumber'])) {
164
- unset($options['thumber']['thumbs'][$k]);
165
- }
166
- }
167
-
168
- DocumentGallery::setOptions($options, $blog);
169
- }
170
-
171
- /**
172
- * The 'created_timestamp' key in each thumb record is being moved
173
- * to 'timestamp' as part of a move to store timestamp for failed
174
- * thumbnails in addition to successful ones.
175
- *
176
- * The defaults sub-branch in the gallery branch is being flattened into its parent.
177
- *
178
- * @param array $options The options to be modified.
179
- */
180
- private static function twoPointTwo(&$options) {
181
- if (isset($options['version']) && version_compare($options['version'], '2.2', '<')) {
182
- $thumbs = array();
183
-
184
- // "created_timestamp" moving to just "timestamp"
185
- foreach ($options['thumber']['thumbs'] as $id => $thumb) {
186
- if (false === $thumb) continue;
187
-
188
- $thumbs[$id] = array(
189
- 'timestamp' => $thumb['created_timestamp'],
190
- 'thumb_url' => $thumb['thumb_url'],
191
- 'thumb_path' => $thumb['thumb_path'],
192
- 'thumber' => $thumb['thumber']
193
- );
194
- }
195
-
196
- $options['thumber']['thumbs'] = $thumbs;
197
-
198
- // adding default thumbnail generation timeout
199
- $options['thumber']['timeout'] = 30;
200
-
201
- // flatten out "defaults" level
202
- $options['gallery'] = $options['gallery']['defaults'];
203
-
204
- // adding "validation" branch
205
- $options['validation'] = false;
206
-
207
- // adding "logging" branch
208
- $options['logging'] = false;
209
- }
210
- }
211
-
212
- /**
213
- * Some of the data previously stored along with custom CSS is no longer needed.
214
- *
215
- * @param array $options The options to be modified.
216
- */
217
- private static function twoPointThree(&$options) {
218
- if (isset($options['version']) && version_compare($options['version'], '2.3', '<')) {
219
- include_once DG_PATH . 'inc/class-thumber.php';
220
-
221
- unset($options['css']['last-modified']);
222
- unset($options['css']['etag']);
223
- unset($options['css']['version']);
224
-
225
- // need to recalculate minified, excluding static CSS which was previously included
226
- $options['css']['minified'] = DocumentGallery::compileCustomCss($options['css']['text']);
227
-
228
- // if user inadvertantly enabled google drive viewer on system where it's not supported
229
- // then avoid locking it in the on state
230
- if ($options['thumber']['active']['google']) {
231
- $options['thumber']['active']['google'] = false /* DG_Thumber::isGoogleDriveAvailable() */;
232
- }
233
-
234
- $options['gallery']['post_status'] = 'any';
235
- $options['gallery']['post_type'] = 'attachment';
236
- $options['gallery']['limit'] = -1;
237
- }
238
- }
239
-
240
- /**
241
- * Creating new meta branch in options to store plugin meta information.
242
- *
243
- * "Localpost" no longer supported. Replaced by "id" attribute.
244
- * "Images" no longer supported. Replaced by "mime_types" attribute.
245
- * "Ids" still supported, but not stored in DB.
246
- *
247
- * Google thumber no longer supported.
248
- *
249
- * Added "columns" attribute.
250
- * Added "mime_types" attribute.
251
- *
252
- * @param array $options The options to be modified.
253
- */
254
- private static function threePointZeroBeta(&$options) {
255
- if (isset($options['version']) /*&& version_compare($options['version'], '3.0.0-beta', '<')*/) {
256
- $options['meta'] = array('version' => $options['version']);
257
- unset($options['version']);
258
-
259
- $images = $options['gallery']['images'];
260
-
261
- unset($options['gallery']['localpost']);
262
- unset($options['gallery']['ids']);
263
- unset($options['gallery']['images']);
264
-
265
- unset($options['thumber']['active']['google']);
266
-
267
- $defaults = self::getDefaultOptions();
268
- $options['gallery']['columns'] = $defaults['gallery']['columns'];
269
- $options['gallery']['mime_types'] = $defaults['gallery']['mime_types'];
270
- if ($images) {
271
- $options['gallery']['mime_types'] .= ',image';
272
- }
273
- }
274
- }
275
-
276
- /**
277
- * Flat logging option split out into multiple options in a nested array.
278
- *
279
- * Added scheduled log purge event to handle rollovers.
280
- *
281
- * @param unknown $options
282
- */
283
- private static function threePointOne(&$options) {
284
- if (version_compare($options['meta']['version'], '3.1', '<')) {
285
- $logging_enabled = $options['logging'];
286
- $options['logging'] = array(
287
- 'enabled' => $logging_enabled,
288
- 'purge_interval' => 7);
289
-
290
- // purge log entries regularly
291
- wp_schedule_event(time(), 'daily', DG_Logger::PurgeLogsAction);
292
- }
293
- }
294
-
295
- /**
296
- * Adds 'new_window' under gallery options.
297
- *
298
- * @param unknown $options
299
- */
300
- private static function threePointTwo(&$options) {
301
- if (version_compare($options['meta']['version'], '3.2', '<')) {
302
- $options['gallery']['new_window'] = false;
303
- }
304
- }
305
-
306
- /**
307
- * Sets up Document Gallery on all blog(s) activated.
308
- * @param bool $networkwide Whether this is a network-wide update (multisite only).
309
- */
310
- public static function activate($networkwide) {
311
- $blogs = array(null);
312
-
313
- if (is_multisite()) {
314
- // check if it is a network activation
315
- if ($networkwide) {
316
- global $wpdb;
317
- $blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
318
- }
319
- }
320
-
321
- foreach ($blogs as $blog) {
322
- self::_activate($blog);
323
- }
324
-
325
- // handle purging log entries regularly
326
- wp_schedule_event(time(), 'daily', DG_Logger::PurgeLogsAction);
327
- }
328
-
329
- /**
330
- * Hooked into wpmu_new_blog to handle activating a new blog when plugin
331
- * is already network activated.
332
- * See discussion: https://core.trac.wordpress.org/ticket/14170
333
- * @param int $blog Blog ID.
334
- */
335
- public static function activateNewBlog($blog) {
336
- if (is_plugin_active_for_network(DG_BASENAME)) {
337
- self::_activate($blog);
338
- }
339
- }
340
-
341
- /**
342
- * Runs activation setup for Document Gallery on all blog(s) it is activated on.
343
- * @param int $blog Blog to update or null if updating current blog.
344
- */
345
- private static function _activate($blog) {
346
- $options = DocumentGallery::getOptions($blog);
347
-
348
- // first activation
349
- if (is_null($options)) {
350
- DocumentGallery::setOptions(self::getDefaultOptions(), $blog);
351
- }
352
- }
353
-
354
- /**
355
- * Runs when DG is uninstalled.
356
- */
357
- public static function uninstall() {
358
- if (!current_user_can('activate_plugins')) return;
359
- check_admin_referer('bulk-plugins');
360
-
361
- $blogs = array(null);
362
-
363
- if (is_multisite()) {
364
- global $wpdb;
365
- $blogs = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
366
- }
367
-
368
- foreach ($blogs as $blog) {
369
- self::_uninstall($blog);
370
- }
371
-
372
- wp_clear_scheduled_hook(DG_Logger::PurgeLogsAction);
373
- }
374
-
375
- /**
376
- * Runs when DG is uninstalled for an individual blog.
377
- */
378
- private static function _uninstall($blog) {
379
- $options = DG_Thumber::getOptions($blog);
380
- if (is_null($options)) return;
381
-
382
- foreach ($options['thumbs'] as $val) {
383
- if (isset($val['thumber'])) {
384
- @unlink($val['thumb_path']);
385
- }
386
- }
387
-
388
- DocumentGallery::deleteOptions($blog);
389
- }
390
-
391
- /**
392
- * NOTE: This is expensive as is involves file I/O reading the README. Only use when
393
- * the equivilent value in options array is not viable.
394
- * @return string URL where users can donate to plugin.
395
- */
396
- private static function getDonateLink() {
397
- $data = get_file_data(DG_PATH . 'README.txt', array('donate' => 'Donate link'));
398
- return $data['donate'];
399
- }
400
-
401
- /**
402
- * Blocks instantiation. All functions are static.
403
- */
404
- private function __construct() {
405
-
406
- }
407
- }
408
 
409
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /**
5
  * Holds functions that handle DG setup / uninstallation.
8
  */
9
  class DG_Setup {
10
 
11
+ /**
12
+ * The default DG options to be used on install and when validating structure of options.
13
+ *
14
+ * @param $skeleton bool When true, expensive values are not calculated. Only keys may be trusted when returning skeleton.
15
+ *
16
+ * @return array Contains default options for DG.
17
+ */
18
+ public static function getDefaultOptions( $skeleton = false ) {
19
+ include_once DG_PATH . 'inc/class-thumber.php';
20
+
21
+ $gs = $donate_link = null;
22
+ if ( ! $skeleton ) {
23
+ $gs = DG_Thumber::getGhostscriptExecutable();
24
+ $donate_link = self::getDonateLink();
25
+ }
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
34
+ 'active' => DG_Thumber::getDefaultThumbers( $skeleton ),
35
+ // max width to generate thumbnails
36
+ 'width' => 200,
37
+ // max height to generate thumbnails
38
+ 'height' => 200,
39
+ // time after which to quite trying to generate new thumbanils for gallery
40
+ 'timeout' => 30
41
+ ),
42
+ 'gallery' => array(
43
+ // default: link directly to file (true to link to attachment pg)
44
+ 'attachment_pg' => false,
45
+ // include the attachment description in output
46
+ 'descriptions' => false,
47
+ // include thumbnail of actual document in gallery display
48
+ 'fancy' => true,
49
+ // comma-delimited list of all mime types to be included
50
+ 'mime_types' => implode( ',', self::getDefaultMimeTypes() ),
51
+ // ascending/descending order for included documents
52
+ 'order' => 'ASC',
53
+ // which property to order by
54
+ 'orderby' => 'menu_order',
55
+ // AND or OR
56
+ 'relation' => 'AND',
57
+ // the status the post must be in when returned by DG
58
+ 'post_status' => 'any',
59
+ // the type of post to be returned
60
+ 'post_type' => 'attachment',
61
+ // the max number of thumbnails to return
62
+ 'limit' => - 1,
63
+ // # of columns to be used in gallery
64
+ 'columns' => 4,
65
+ // whether to open documents in new window
66
+ 'new_window' => false
67
+ ),
68
+ 'css' => array(
69
+ // plain text of CSS to be edited by user
70
+ 'text' => ''
71
+ ),
72
+ 'meta' => array(
73
+ // current DG version
74
+ 'version' => DG_VERSION,
75
+ // URL to donate to plugin development
76
+ 'donate_link' => $donate_link
77
+ ),
78
+ // logging options
79
+ 'logging' => array(
80
+ // TODO: more granular -- log_level instead of blanket enable/disable
81
+ 'enabled' => false,
82
+ // max age of log entry (days)
83
+ 'purge_interval' => 7
84
+ ),
85
+ // whether to validate DG option structure on save
86
+ 'validation' => false
87
+ );
88
+ }
89
+
90
+ /**
91
+ * @return array The default MIME types to include in gallery.
92
+ */
93
+ public static function getDefaultMimeTypes() {
94
+ return array( 'application', 'video', 'text', 'audio', 'image' );
95
+ }
96
+
97
+ /**
98
+ * Runs every page load, updates as needed.
99
+ */
100
+ public static function maybeUpdate() {
101
+ global $dg_options;
102
+
103
+ // do update
104
+ if ( ! is_null( $dg_options ) && ( isset( $dg_options['version'] ) || DG_VERSION !== $dg_options['meta']['version'] ) ) {
105
+ $blogs = array( null );
106
+
107
+ if ( is_multisite() ) {
108
+ $blogs = DG_Util::getBlogIds();
109
+ }
110
+
111
+ foreach ( $blogs as $blog ) {
112
+ self::_update( $blog );
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Runs when update is needed, updating the given blog. If $blog is null,
119
+ * active blog is updated.
120
+ *
121
+ * @param int $blog Blog to update or null if updating current blog.
122
+ */
123
+ private static function _update( $blog ) {
124
+ $options = DocumentGallery::getOptions( $blog );
125
+ if ( is_null( $options ) ) {
126
+ return;
127
+ }
128
+
129
+ // version-specific updates
130
+ self::twoPointTwo( $options );
131
+ self::twoPointThree( $options );
132
+ self::threePointZeroBeta( $options );
133
+ self::threePointOne( $options );
134
+ self::threePointTwo( $options );
135
+ self::threePointThree( $options );
136
+
137
+ // update plugin meta data
138
+ $options['meta']['version'] = DG_VERSION;
139
+ $options['meta']['donate_link'] = self::getDonateLink();
140
+
141
+ // remove previously-failed thumbs
142
+ $thumbs = $options['thumber']['thumbs'];
143
+ foreach ( $thumbs as $k => $v ) {
144
+ if ( empty( $v['thumber'] ) ) {
145
+ unset( $options['thumber']['thumbs'][ $k ] );
146
+ }
147
+ }
148
+
149
+ DocumentGallery::setOptions( $options, $blog );
150
+ }
151
+
152
+ /**
153
+ * The 'created_timestamp' key in each thumb record is being moved
154
+ * to 'timestamp' as part of a move to store timestamp for failed
155
+ * thumbnails in addition to successful ones.
156
+ *
157
+ * The defaults sub-branch in the gallery branch is being flattened into its parent.
158
+ *
159
+ * @param array $options The options to be modified.
160
+ */
161
+ private static function twoPointTwo( &$options ) {
162
+ if ( isset( $options['version'] ) && version_compare( $options['version'], '2.2', '<' ) ) {
163
+ $thumbs = array();
164
+
165
+ // "created_timestamp" moving to just "timestamp"
166
+ foreach ( $options['thumber']['thumbs'] as $id => $thumb ) {
167
+ if ( false === $thumb ) {
168
+ continue;
169
+ }
170
+
171
+ $thumbs[ $id ] = array(
172
+ 'timestamp' => $thumb['created_timestamp'],
173
+ 'thumb_url' => $thumb['thumb_url'],
174
+ 'thumb_path' => $thumb['thumb_path'],
175
+ 'thumber' => $thumb['thumber']
176
+ );
177
+ }
178
+
179
+ $options['thumber']['thumbs'] = $thumbs;
180
+
181
+ // adding default thumbnail generation timeout
182
+ $options['thumber']['timeout'] = 30;
183
+
184
+ // flatten out "defaults" level
185
+ $options['gallery'] = $options['gallery']['defaults'];
186
+
187
+ // adding "validation" branch
188
+ $options['validation'] = false;
189
+
190
+ // adding "logging" branch
191
+ $options['logging'] = false;
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Some of the data previously stored along with custom CSS is no longer needed.
197
+ *
198
+ * @param array $options The options to be modified.
199
+ */
200
+ private static function twoPointThree( &$options ) {
201
+ if ( isset( $options['version'] ) && version_compare( $options['version'], '2.3', '<' ) ) {
202
+ include_once DG_PATH . 'inc/class-thumber.php';
203
+
204
+ unset( $options['css']['last-modified'] );
205
+ unset( $options['css']['etag'] );
206
+ unset( $options['css']['version'] );
207
+
208
+ // need to recalculate minified, excluding static CSS which was previously included
209
+ //$options['css']['minified'] = DocumentGallery::compileCustomCss($options['css']['text']);
210
+
211
+ // if user inadvertantly enabled google drive viewer on system where it's not supported
212
+ // then avoid locking it in the on state
213
+ if ( $options['thumber']['active']['google'] ) {
214
+ $options['thumber']['active']['google'] = false /* DG_Thumber::isGoogleDriveAvailable() */
215
+ ;
216
+ }
217
+
218
+ $options['gallery']['post_status'] = 'any';
219
+ $options['gallery']['post_type'] = 'attachment';
220
+ $options['gallery']['limit'] = - 1;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Creating new meta branch in options to store plugin meta information.
226
+ *
227
+ * "Localpost" no longer supported. Replaced by "id" attribute.
228
+ * "Images" no longer supported. Replaced by "mime_types" attribute.
229
+ * "Ids" still supported, but not stored in DB.
230
+ *
231
+ * Google thumber no longer supported.
232
+ *
233
+ * Added "columns" attribute.
234
+ * Added "mime_types" attribute.
235
+ *
236
+ * @param array $options The options to be modified.
237
+ */
238
+ private static function threePointZeroBeta( &$options ) {
239
+ if ( isset( $options['version'] ) /*&& version_compare($options['version'], '3.0.0-beta', '<')*/ ) {
240
+ $options['meta'] = array( 'version' => $options['version'] );
241
+ unset( $options['version'] );
242
+
243
+ $images = $options['gallery']['images'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ unset( $options['gallery']['localpost'] );
246
+ unset( $options['gallery']['ids'] );
247
+ unset( $options['gallery']['images'] );
248
+
249
+ unset( $options['thumber']['active']['google'] );
250
+
251
+ $defaults = self::getDefaultOptions();
252
+ $options['gallery']['columns'] = $defaults['gallery']['columns'];
253
+ $options['gallery']['mime_types'] = $defaults['gallery']['mime_types'];
254
+ if ( $images ) {
255
+ $options['gallery']['mime_types'] .= ',image';
256
+ }
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Flat logging option split out into multiple options in a nested array.
262
+ *
263
+ * Added scheduled log purge event to handle rollovers.
264
+ *
265
+ * @param array $options The options to be modified.
266
+ */
267
+ private static function threePointOne( &$options ) {
268
+ if ( version_compare( $options['meta']['version'], '3.1', '<' ) ) {
269
+ $logging_enabled = $options['logging'];
270
+ $options['logging'] = array(
271
+ 'enabled' => $logging_enabled,
272
+ 'purge_interval' => 7
273
+ );
274
+
275
+ // purge log entries regularly
276
+ wp_schedule_event( time(), 'daily', DG_Logger::PurgeLogsAction );
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Adds 'new_window' under gallery options.
282
+ *
283
+ * @param array $options The options to be modified.
284
+ */
285
+ private static function threePointTwo( &$options ) {
286
+ if ( version_compare( $options['meta']['version'], '3.2', '<' ) ) {
287
+ $options['gallery']['new_window'] = false;
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Removes minified CSS. Fixing corrupt data for boolean fields that may have gotten strings.
293
+ *
294
+ * @param array $options The options to be modified.
295
+ */
296
+ private static function threePointThree( &$options ) {
297
+ if ( version_compare( $options['meta']['version'], '3.3', '<' ) ) {
298
+ unset( $options['css']['minified'] );
299
+
300
+ $defaults = self::getDefaultOptions();
301
+ foreach ( $defaults as $class => $block ) {
302
+ if ( is_array($block) ) {
303
+ foreach ( $block as $prop => $value ) {
304
+ if ( is_bool( $value ) && isset( $options[$class][$prop] ) && ! is_bool( $options[$class][$prop] ) ) {
305
+ $options[$class][$prop] = DG_Util::toBool( $options[$class][$prop], $value );
306
+ }
307
+ }
308
+ } elseif ( is_bool( $block ) && isset( $options[$class] ) && ! is_bool( $options[$class] ) ) {
309
+ $options[$class] = DG_Util::toBool( $options[$class], $block );
310
+ }
311
+ }
312
+ }
313
+ }
314
+
315
+ /**
316
+ * Sets up Document Gallery on all blog(s) activated.
317
+ *
318
+ * @param bool $networkwide Whether this is a network-wide update (multisite only).
319
+ */
320
+ public static function activate( $networkwide ) {
321
+ $blogs = array( null );
322
+
323
+ if ( is_multisite() ) {
324
+ // check if it is a network activation
325
+ if ( $networkwide ) {
326
+ $blogs = DG_Util::getBlogIds();
327
+ }
328
+ }
329
+
330
+ foreach ( $blogs as $blog ) {
331
+ self::_activate( $blog );
332
+ }
333
+
334
+ // handle purging log entries regularly
335
+ wp_schedule_event( time(), 'daily', DG_Logger::PurgeLogsAction );
336
+ }
337
+
338
+ /**
339
+ * Hooked into wpmu_new_blog to handle activating a new blog when plugin
340
+ * is already network activated.
341
+ * See discussion: https://core.trac.wordpress.org/ticket/14170
342
+ *
343
+ * @param int $blog Blog ID.
344
+ */
345
+ public static function activateNewBlog( $blog ) {
346
+ if ( is_plugin_active_for_network( DG_BASENAME ) ) {
347
+ self::_activate( $blog );
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Runs activation setup for Document Gallery on all blog(s) it is activated on.
353
+ *
354
+ * @param int $blog Blog to update or null if updating current blog.
355
+ */
356
+ private static function _activate( $blog ) {
357
+ $options = DocumentGallery::getOptions( $blog );
358
+
359
+ // first activation
360
+ if ( is_null( $options ) ) {
361
+ DocumentGallery::setOptions( self::getDefaultOptions(), $blog );
362
+ }
363
+ }
364
+
365
+ /**
366
+ * Runs when DG is uninstalled.
367
+ */
368
+ public static function uninstall() {
369
+ if ( ! current_user_can( 'activate_plugins' ) ) {
370
+ return;
371
+ }
372
+ check_admin_referer( 'bulk-plugins' );
373
+
374
+ $blogs = array( null );
375
+
376
+ if ( is_multisite() ) {
377
+ $blogs = DG_Util::getBlogIds();
378
+ }
379
+
380
+ foreach ( $blogs as $blog ) {
381
+ self::_uninstall( $blog );
382
+ }
383
+
384
+ wp_clear_scheduled_hook( DG_Logger::PurgeLogsAction );
385
+ }
386
+
387
+ /**
388
+ * Runs when DG is uninstalled for an individual blog.
389
+ */
390
+ private static function _uninstall( $blog ) {
391
+ $options = DG_Thumber::getOptions( $blog );
392
+ if ( is_null( $options ) ) {
393
+ return;
394
+ }
395
+
396
+ foreach ( $options['thumbs'] as $val ) {
397
+ if ( isset( $val['thumber'] ) ) {
398
+ @unlink( $val['thumb_path'] );
399
+ }
400
+ }
401
+
402
+ DocumentGallery::deleteOptions( $blog );
403
+ }
404
+
405
+ /**
406
+ * NOTE: This is expensive as is involves file I/O reading the README. Only use when
407
+ * the equivalent value in options array is not viable.
408
+ * @return string URL where users can donate to plugin.
409
+ */
410
+ private static function getDonateLink() {
411
+ $data = get_file_data( DG_PATH . 'README.txt', array( 'donate' => 'Donate link' ) );
412
+
413
+ return $data['donate'];
414
+ }
415
+
416
+ /**
417
+ * Blocks instantiation. All functions are static.
418
+ */
419
+ private function __construct() {
420
+
421
+ }
422
+ }
inc/class-thumber.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /**
5
  * Thumber wraps the functionality required to
@@ -9,772 +9,791 @@ defined('WPINC') OR exit;
9
  */
10
  class DG_Thumber {
11
 
12
- /**
13
- * Returns the default mapping of thumber slug to whether it is active or not.
14
- * @param $skeleton bool When true, values that require computation will be
15
- * skipped. Useful when only structure of options is needed.
16
- * @return array
17
- */
18
- public static function getDefaultThumbers($skeleton = false) {
19
- $gs_active = $imagick_active = null;
20
- if (!$skeleton) {
21
- $gs_active = (bool)self::getGhostscriptExecutable();
22
- $imagick_active = self::isImagickAvailable();
23
- }
24
-
25
- return array('av' => true, 'gs' => $gs_active,
26
- 'imagick' => $imagick_active);
27
- }
28
-
29
- /**
30
- * Sets the thumbnail for the given attachment ID.
31
- *
32
- * @param int $ID Document ID.
33
- * @param string $path System path to thumbnail.
34
- * @return bool Whether set was successful.
35
- */
36
- public static function setThumbnail($ID, $path, $generator = 'unknown') {
37
- return self::thumbnailGenerationHarness($generator, $ID, $path);
38
- }
39
-
40
- /**
41
- * Sets the thumbnail for the given attachment ID to a failed state.
42
- *
43
- * @param int $ID Document ID.
44
- */
45
- public static function setThumbnailFailed($ID) {
46
- $options = self::getOptions();
47
- $options['thumbs'][$ID] = array('timestamp' => time());
48
- self::setOptions($options);
49
- }
50
-
51
- /**
52
- * Wraps generation of thumbnails for various attachment filetypes.
53
- *
54
- * @param int $ID Document ID
55
- * @param int $pg Page number to get thumb from.
56
- * @param bool $generate_if_missing Whether to attempt generating the thumbnail if missing.
57
- * @return str URL to the thumbnail.
58
- */
59
- public static function getThumbnail($ID, $pg = 1, $generate_if_missing = true) {
60
- static $start = null;
61
- if (is_null($start)) {
62
- $start = time();
63
- }
64
-
65
- $options = self::getOptions();
66
-
67
- // if we haven't saved a thumb, generate one
68
- if (empty($options['thumbs'][$ID])) {
69
- // short-circuit generation if not required
70
- if (!$generate_if_missing) {
71
- return null;
72
- }
73
-
74
- // prevent page timing out or user waiting too long for page
75
- if ((time() - $start) > $options['timeout']) {
76
- return self::getDefaultThumbnail($ID, $pg);
77
- }
78
-
79
- // do the processing
80
- $file = get_attached_file($ID);
81
-
82
- foreach (self::getThumbers() as $ext_preg => $thumber) {
83
- $ext_preg = '!\.(?:' . $ext_preg . ')$!i';
84
-
85
- if (preg_match($ext_preg, $file)) {
86
- if (DG_Logger::logEnabled()) {
87
- $toLog = sprintf(__('Attempting to generate thumbnail for attachment #%d with (%s)',
88
- 'document-gallery'), $ID, is_array($thumber) ? implode('::',$thumber) : print_r($thumber, true));
89
- DG_Logger::writeLog(DG_LogLevel::Detail, $toLog);
90
- }
91
-
92
- if (self::thumbnailGenerationHarness($thumber, $ID, $pg)) {
93
- // harness updates options so we need a new copy
94
- $options = self::getOptions();
95
- break;
96
- }
97
- }
98
- }
99
- }
100
-
101
- $new = empty($options['thumbs'][$ID]);
102
- if ($new || empty($options['thumbs'][$ID]['thumber'])) {
103
- if ($new) {
104
- self::setThumbnailFailed($ID);
105
- }
106
-
107
- // fallback to default thumb for attachment type
108
- $url = self::getDefaultThumbnail($ID, $pg);
109
- } else {
110
- // use generated thumbnail
111
- $url = $options['thumbs'][$ID]['thumb_url'];
112
- }
113
-
114
- return $url;
115
- }
116
-
117
- /*==========================================================================
118
- * AUDIO VIDEO THUMBNAILS
119
- *=========================================================================*/
120
-
121
- /**
122
- * Uses wp_read_video_metadata() and wp_read_audio_metadata() to retrieve
123
- * an embedded image to use as a thumbnail.
124
- *
125
- * @param str $ID The attachment ID to retrieve thumbnail from.
126
- * @param int $pg Unused.
127
- * @return bool|str False on failure, URL to thumb on success.
128
- */
129
- public static function getAudioVideoThumbnail($ID, $pg = 1) {
130
- include_once DG_WPADMIN_PATH . 'includes/media.php';
131
-
132
- $attachment = get_post($ID);
133
- $doc_path = get_attached_file($ID);
134
-
135
- if (preg_match('#^video/#', get_post_mime_type($attachment))) {
136
- $metadata = wp_read_video_metadata($doc_path);
137
- }
138
- elseif (preg_match('#^audio/#', get_post_mime_type($attachment))) {
139
- $metadata = wp_read_audio_metadata($doc_path);
140
- }
141
-
142
- // unsupported mime type || no embedded image present
143
- if(!isset($metadata) || empty($metadata['image']['data'])) {
144
- return false;
145
- }
146
-
147
- $ext = 'jpg';
148
- switch ($metadata['image']['mime']) {
149
- case 'image/gif':
150
- $ext = 'gif';
151
- break;
152
- case 'image/png':
153
- $ext = 'png';
154
- break;
155
- }
156
-
157
- $temp_file = self::getTempFile($ext);
158
-
159
- if (!$fp = @fopen($temp_file, 'wb')) {
160
- DG_Logger::writeLog(DG_LogLevel::Error, __('Could not open file: ', 'document-gallery') . $temp_file);
161
- return false;
162
- }
163
-
164
- if (!@fwrite($fp, $metadata['image']['data'])) {
165
- DG_Logger::writeLog(DG_LogLevel::Error, __('Could not write file: ', 'document-gallery') . $temp_file);
166
- fclose($fp);
167
- return false;
168
- }
169
-
170
- fclose($fp);
171
-
172
- return $temp_file;
173
- }
174
-
175
- /**
176
- * @return array All extensions supported by WP Audio Video Media metadata.
177
- */
178
- private static function getAudioVideoExts() {
179
- return array_merge(wp_get_audio_extensions(), wp_get_video_extensions());
180
- }
181
-
182
- /*==========================================================================
183
- * IMAGICK THUMBNAILS
184
- *=========================================================================*/
185
-
186
- /**
187
- * Uses WP_Image_Editor_Imagick to generate thumbnails.
188
- *
189
- * @param int $ID The attachment ID to retrieve thumbnail from.
190
- * @param int $pg The page to get the thumbnail of.
191
- * @return bool|str False on failure, URL to thumb on success.
192
- */
193
- public static function getImagickThumbnail($ID, $pg = 1) {
194
- include_once DG_PATH . 'inc/class-image-editor-imagick.php';
195
-
196
- $doc_path = get_attached_file($ID);
197
- $img = new DG_Image_Editor_Imagick($doc_path, $pg - 1);
198
- $err = $img->load();
199
- if(is_wp_error($err)) {
200
- DG_Logger::writeLog(
201
- DG_LogLevel::Error,
202
- __('Failed to open file in Imagick: ', 'document-gallery') .
203
- $err->get_error_message());
204
- return false;
205
- }
206
-
207
- $temp_file = self::getTempFile();
208
-
209
- $err = $img->save($temp_file, 'image/png');
210
- if (is_wp_error($err)) {
211
- DG_Logger::writeLog(
212
- DG_LogLevel::Error,
213
- __('Failed to save image in Imagick: ', 'document-gallery') .
214
- $err->get_error_message());
215
- return false;
216
- }
217
-
218
- return $temp_file;
219
- }
220
-
221
- /**
222
- * @return bool Whether WP_Image_Editor_Imagick can be used on this system.
223
- */
224
- public static function isImagickAvailable() {
225
- static $ret = null;
226
-
227
- if (is_null($ret)) {
228
- include_once DG_WPINC_PATH . 'class-wp-image-editor.php';
229
- include_once DG_WPINC_PATH . 'class-wp-image-editor-imagick.php';
230
- $ret = WP_Image_Editor_Imagick::test();
231
- }
232
-
233
- return $ret;
234
- }
235
-
236
- /*==========================================================================
237
- * GHOSTSCRIPT THUMBNAILS
238
- *=========================================================================*/
239
-
240
- /**
241
- * Get thumbnail for document with given ID using Ghostscript. Imagick could
242
- * also handle this, but is *much* slower.
243
- *
244
- * @param int $ID The attachment ID to retrieve thumbnail from.
245
- * @param int $pg The page number to make thumbnail of -- index starts at 1.
246
- * @return bool|str False on failure, URL to thumb on success.
247
- */
248
- public static function getGhostscriptThumbnail($ID, $pg = 1) {
249
- static $gs = null;
250
-
251
- if (is_null($gs)) {
252
- $options = self::getOptions();
253
- $gs = $options['gs'];
254
-
255
- if (false !== $gs) {
256
- $gs = escapeshellarg($gs) . ' -sDEVICE=png16m -dFirstPage=%1$d'
257
- . ' -dLastPage=%1$d -dBATCH -dNOPAUSE -dPDFFitPage -sOutputFile=%2$s %3$s 2>&1';
258
- }
259
- }
260
-
261
- if (false === $gs) {
262
- return false;
263
- }
264
-
265
- $doc_path = get_attached_file($ID);
266
- $temp_path = self::getTempFile();
267
-
268
- exec(sprintf($gs, $pg, $temp_path, $doc_path), $out, $ret);
269
-
270
- if ($ret != 0) {
271
- DG_Logger::writeLog(DG_LogLevel::Error, __('Ghostscript failed: ', 'document-gallery') . print_r($out));
272
- @unlink($temp_path);
273
- return false;
274
- }
275
-
276
- return $temp_path;
277
- }
278
-
279
- /**
280
- * @return array All extensions supported by Ghostscript.
281
- */
282
- private static function getGhostscriptExts() {
283
- return array('pdf', 'ps', 'eps');
284
- }
285
-
286
- /**
287
- * Dynamically determines whether we may call gs through exec().
288
- *
289
- * NOTE: This does not check the options for gs path. Don't use in
290
- * thumbnail generation as it's slow and not configurable.
291
- *
292
- * @return bool|str If available, returns exe path. False otherwise.
293
- */
294
- public static function getGhostscriptExecutable() {
295
- static $executable = null;
296
-
297
- if (is_null($executable)) {
298
- // we must be able to exec()
299
- $executable = self::isExecAvailable();
300
- if (!$executable) {
301
- return $executable;
302
- }
303
-
304
- // find on Windows system
305
- if ('WIN' === strtoupper(substr(PHP_OS, 0, 3))) {
306
- // look for environment variable
307
- $executable = getenv('GSC');
308
- if ($executable) {
309
- return $executable;
310
- }
311
-
312
- // hope GS in the path
313
- $executable = exec('where gswin*c.exe');
314
- if (!empty($executable)) {
315
- return $executable;
316
- }
317
-
318
- // look directly in filesystem
319
- // 64- or 32-bit binary
320
- $executable = exec('dir /o:n/s/b "C:\Program Files\gs\*gswin*c.exe"');
321
- if (!empty($executable)) {
322
- return $executable;
323
- }
324
-
325
- // 32-bit binary on 64-bit OS
326
- $executable = exec('dir /o:n/s/b "C:\Program Files (x86)\gs\*gswin32c.exe"');
327
- $executable = empty($executable) ? false : $executable;
328
- return $executable;
329
- }
330
-
331
- // handle Linux systems
332
- $executable = exec('which gs');
333
- if (!empty($executable)) {
334
- return $executable;
335
- }
336
-
337
- // GoDaddy and others aren't setup in such a way that
338
- // the above works so we need to fallback to a direct
339
- // filesystem check in most common location
340
- exec('test -e /usr/bin/gs', $dummy, $ret);
341
- $executable = ($ret === 0) ? '/usr/bin/gs' : false;
342
-
343
- return $executable;
344
- }
345
-
346
- return $executable;
347
- }
348
-
349
- /**
350
- * @return bool Whether we can use the GS executable.
351
- */
352
- public static function isGhostscriptAvailable() {
353
- static $ret = null;
354
-
355
- if (is_null($ret)) {
356
- $options = self::getOptions();
357
- $ret = $options['gs'] && self::isExecAvailable();
358
- }
359
-
360
- return $ret;
361
- }
362
-
363
- /*==========================================================================
364
- * DEFAULT THUMBNAILS
365
- *=========================================================================*/
366
-
367
- /**
368
- * Get thumbnail for document with given ID from default images.
369
- *
370
- * @param str $ID The attachment ID to retrieve thumbnail from.
371
- * @param int $pg Unused.
372
- * @return str URL to thumbnail.
373
- */
374
- public static function getDefaultThumbnail($ID, $pg = 1) {
375
- $options = self::getOptions();
376
- $width = $options['width'];
377
- $height = $options['height'];
378
- $icon_url = DG_URL . 'assets/icons/';
379
-
380
- $url = wp_get_attachment_url($ID);
381
- $ext = self::getExt($url);
382
-
383
- // handle images
384
- if ($icon = image_downsize($ID, array($width, $height))) {
385
- $icon = $icon[0];
386
- }
387
- // default extension icon
388
- elseif ($name = self::getDefaultIcon($ext)) {
389
- $icon = $icon_url . $name;
390
- }
391
- // fallback to standard WP icons
392
- elseif (!$icon = wp_mime_type_icon($ID)) {
393
- // everything failed. This is bad...
394
- $icon = $icon_url . 'missing.png';
395
- }
396
-
397
- return $icon;
398
- }
399
-
400
- /**
401
- * Returns the name of the image to represent the filetype given.
402
- *
403
- * @param str $ext
404
- * @return str
405
- */
406
- private static function getDefaultIcon($ext) {
407
- // Maps file ext to default image name.
408
- static $exts = array(
409
- // Most Common First
410
- 'pdf' => 'pdf.png',
411
-
412
- // MS Office
413
- 'doc|docx|docm|dotx|dotm' => 'msdoc.png',
414
- 'ppt|pot|pps|pptx|pptm|ppsx|ppsm|potx|potm|ppam|sldx|sldm' => 'msppt.png',
415
- 'xla|xls|xlt|xlw|xlsx|xlsm|xlsb|xltx|xltm|xlam' => 'msxls.png',
416
- 'mdb' => 'msaccess.png',
417
-
418
- // iWork
419
- 'key' => 'key.png',
420
- 'numbers' => 'numbers.png',
421
- 'pages' => 'pages.png',
422
-
423
- // Images
424
- 'jpg|jpeg|jpe|gif|png|bmp|tif|tiff|ico' => 'image.png',
425
-
426
- // Video formats
427
- 'asf|asx|wmv|wmx|wm|avi|divx|flv|mov' => 'video.png',
428
- 'qt|mpeg|mpg|mpe|mp4|m4v|ogv|webm|mkv' => 'video.png',
429
-
430
- // Audio formats
431
- 'mp3|m4a|m4b|ra|ram|wav|ogg|oga|wma|wax|mka' => 'audio.png',
432
- 'midi|mid' => 'midi.png',
433
-
434
- // Text formats
435
- 'txt|tsv|csv' => 'text.png',
436
- 'rtx' => 'rtx.png',
437
- 'rtf' => 'rtf.png',
438
- 'ics' => 'ics.png',
439
- 'wp|wpd' => 'wordperfect.png',
440
-
441
- // Programming
442
- 'html|htm' => 'html.png',
443
- 'css' => 'css.png',
444
- 'js' => 'javascript.png',
445
- 'class' => 'java.png',
446
- 'asc' => 'asc.png',
447
- 'c' => 'c.png',
448
- 'cc|cpp' => 'cpp.png',
449
- 'h' => 'h.png',
450
-
451
- // Msc application formats
452
- 'zip|tar|gzip|gz|bz2|tgz|7z|rar' => 'compressed.png',
453
- 'exe' => 'exec.png',
454
- 'swf' => 'shockwave.png',
455
-
456
- // OpenDocument formats
457
- 'odt' => 'opendocument-text.png',
458
- 'odp' => 'opendocument-presentation.png',
459
- 'ods' => 'opendocument-spreadsheet.png',
460
- 'odg' => 'opendocument-graphics.png',
461
- 'odb' => 'opendocument-database.png',
462
- 'odf' => 'opendocument-formula.png'
463
- );
464
-
465
- foreach ($exts as $ext_preg => $icon) {
466
- $ext_preg = '!(' . $ext_preg . ')$!i';
467
- if (preg_match($ext_preg, $ext)) {
468
- return $icon;
469
- }
470
- }
471
-
472
- return false;
473
- }
474
-
475
- /*==========================================================================
476
- * GENERAL THUMBNAIL HELPER FUNCTIONS
477
- *=========================================================================*/
478
-
479
- /**
480
- * @return array WP_Post objects for each attachment that has been processed.
481
- */
482
- public static function getThumbed() {
483
- $options = self::getOptions();
484
- $args = array(
485
- 'post_type' => 'attachment',
486
- 'post_status' => 'inherit',
487
- 'post_per_page' => -1,
488
- 'post__in' => array_keys($options['thumbs'])
489
- );
490
-
491
- return count($args['post__in']) ? get_posts($args) : array();
492
- }
493
-
494
- /**
495
- * Key: Attachment ID
496
- * Val: array
497
- * + timestamp - When the thumbnail was generated (or generation failed).
498
- * + thumb_path - System path to thumbnail image.
499
- * + thumb_url - URL pointing to the thumbnail for this document.
500
- * + thumber - Generator used to create thumb OR false if failed to gen.
501
- * @return array|null Thumber options from DB or null if options not initialized.
502
- */
503
- public static function getOptions($blog = null) {
504
- $options = DocumentGallery::getOptions($blog);
505
- return $options['thumber'];
506
- }
507
-
508
- /**
509
- * Key: Attachment ID
510
- * Val: array
511
- * + timestamp - When the thumbnail was generated (or generation failed).
512
- * + thumb_path - System path to thumbnail image.
513
- * + thumb_url - URL pointing to the thumbnail for this document.
514
- * + thumber - Generator used to create thumb OR false if failed to gen.
515
- * @param array $options Thumber options to store in DB
516
- */
517
- private static function setOptions($options, $blog = null) {
518
- $dg_options = DocumentGallery::getOptions($blog);
519
- $dg_options['thumber'] = $options;
520
- DocumentGallery::setOptions($dg_options, $blog);
521
- }
522
-
523
- /**
524
- * @filter dg_thumbers Allows developers to filter the Thumbers used
525
- * for specific filetypes. Index is the regex to match file extensions
526
- * supported and the value is anything that can be accepted by call_user_func().
527
- * The function must take two parameters, 1st is the int ID of the attachment
528
- * to get a thumbnail for, 2nd is the page to take a thumbnail of
529
- * (may not be relevant for some filetypes).
530
- *
531
- * @return array
532
- */
533
- private static function getThumbers() {
534
- static $thumbers = null;
535
-
536
- if (is_null($thumbers)) {
537
- $options = self::getOptions();
538
- $active = $options['active'];
539
- $thumbers = array();
540
-
541
- // Audio/Video embedded images
542
- if ($active['av']) {
543
- $exts = implode('|', self::getAudioVideoExts());
544
- $thumbers[$exts] = array(__CLASS__, 'getAudioVideoThumbnail');
545
- }
546
-
547
- // Ghostscript
548
- if ($active['gs'] && self::isGhostscriptAvailable()) {
549
- $exts = implode('|', self::getGhostscriptExts());
550
- $thumbers[$exts] = array(__CLASS__, 'getGhostscriptThumbnail');
551
- }
552
-
553
- // Imagick
554
- if ($active['imagick'] && self::isImagickAvailable()) {
555
- include_once DG_PATH . 'inc/class-image-editor-imagick.php';
556
- if ($exts = DG_Image_Editor_Imagick::query_formats()) {
557
- $exts = implode('|', $exts);
558
- $thumbers[$exts] = array(__CLASS__, 'getImagickThumbnail');
559
- }
560
- }
561
-
562
- // allow users to filter thumbers used
563
- $thumbers = apply_filters('dg_thumbers', $thumbers);
564
-
565
- // strip out anything that can't be called
566
- $thumbers = array_filter($thumbers, 'is_callable');
567
-
568
- // log which thumbers are being used
569
- if (DG_Logger::logEnabled()) {
570
- if (count($thumbers) > 0) {
571
- $entry = __('Thumbnail Generators: ', 'document-gallery');
572
- foreach ($thumbers as $k => $v) {
573
- $thumber = is_array($v) ? implode('::', $v) : print_r($v, true);
574
-
575
- // TODO: The following works for all internal regexes, but may have unpredictable
576
- // results if developer adds additional thumbnail generators using different regexes
577
- $filetypes = str_replace('|', ', ', $k);
578
-
579
- $entry .= PHP_EOL . "$thumber: $filetypes";
580
- }
581
- } else {
582
- $entry = __('No thumbnail generators enabled.', 'document-gallery');
583
- }
584
- DG_Logger::writeLog(DG_LogLevel::Detail, $entry);
585
- }
586
- }
587
-
588
- return $thumbers;
589
- }
590
-
591
- /**
592
- * Template that handles generating a thumbnail.
593
- *
594
- * If image has already been generated through other means, $pg may be set to the system path where the
595
- * thumbnail is located. In this case, $generator will not be invoked, but *will* be kept for historical purposes.
596
- *
597
- * @param callable $generator Takes ID and pg and returns path to temp file or false.
598
- * @param int $ID ID for the attachment that we need a thumbnail for.
599
- * @param int|str $pg Page number of the attachment to get a thumbnail for or the system path to the image to be used.
600
- * @return bool Whether generation was successful.
601
- */
602
- private static function thumbnailGenerationHarness($generator, $ID, $pg = 1) {
603
- // handle system page in $pg variable
604
- if (is_string($pg) && !is_numeric($pg)) {
605
- $temp_path = $pg;
606
- }
607
- // delegate thumbnail generation to $generator
608
- elseif (false === ($temp_path = call_user_func($generator, $ID, $pg))) {
609
- return false;
610
- }
611
-
612
- // get some useful stuff
613
- $doc_path = get_attached_file($ID);
614
- $doc_url = wp_get_attachment_url($ID);
615
- $dirname = dirname($doc_path);
616
- $basename = basename($doc_path);
617
- if (false === ($len = strrpos($basename, '.'))) {
618
- $len = strlen($basename);
619
- }
620
- $extless = substr($basename, 0, $len);
621
- $ext = self::getExt($temp_path);
622
-
623
- $thumb_name = self::getUniqueThumbName($dirname, $extless, $ext);
624
- $thumb_path = $dirname . DIRECTORY_SEPARATOR . $thumb_name;
625
-
626
- // scale generated image down
627
- $img = wp_get_image_editor($temp_path);
628
-
629
- if (is_wp_error($img)) {
630
- DG_Logger::writeLog(
631
- DG_LogLevel::Error,
632
- __('Failed to get image editor: ', 'document-gallery') .
633
- $img->get_error_message());
634
- return false;
635
- }
636
-
637
- $options = self::getOptions();
638
- $img->resize($options['width'], $options['height'], false);
639
- $err = $img->save($thumb_path);
640
-
641
- if (is_wp_error($err)) {
642
- DG_Logger::writeLog(
643
- DG_LogLevel::Error,
644
- __('Failed to save image: ', 'document-gallery') .
645
- $err->get_error_message());
646
- return false;
647
- }
648
-
649
- // do some cleanup
650
- @unlink($temp_path);
651
- self::deleteThumbMeta($ID);
652
-
653
- // store new thumbnail in DG options
654
- $options['thumbs'][$ID] = array(
655
- 'timestamp' => time(),
656
- 'thumb_url' => preg_replace('#'.preg_quote($basename).'$#', $thumb_name, $doc_url),
657
- 'thumb_path' => $thumb_path,
658
- 'thumber' => $generator
659
- );
660
- self::setOptions($options);
661
-
662
- return true;
663
- }
664
-
665
- /**
666
- * Caller should handle removal of the temp file when finished.
667
- *
668
- * @param str $ext
669
- */
670
- private static function getTempFile($ext = 'png') {
671
- static $base = null;
672
- static $tmp;
673
-
674
- if (is_null($base)) {
675
- $base = md5(time());
676
- $tmp = untrailingslashit(get_temp_dir());
677
- }
678
-
679
- return $tmp . DIRECTORY_SEPARATOR . wp_unique_filename($tmp, $base.'.'.$ext);
680
- }
681
-
682
- /**
683
- * Constructs name for file's thumbnail, ensuring that it does not conflict
684
- * with any existing file.
685
- *
686
- * @param str $dirname Directory where the document is located.
687
- * @param str $extless Base name, less the extension.
688
- * @param str $ext The extension of the image to be created.
689
- * @return str Name unique within the directory given, derived from the basename given.
690
- */
691
- private static function getUniqueThumbName($dirname, $extless, $ext = 'png') {
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
- * @return array All IDs that were deleted -- some subset of IDs requested to be deleted.
701
- */
702
- public static function deleteThumbMeta($ids) {
703
- $options = self::getOptions();
704
-
705
- $deleted = array();
706
- foreach ((array)$ids as $id) {
707
- if (isset($options['thumbs'][$id])) {
708
- if (isset($options['thumbs'][$id]['thumber'])) {
709
- @unlink($options['thumbs'][$id]['thumb_path']);
710
- }
711
-
712
- unset($options['thumbs'][$id]);
713
- $deleted[] = $id;
714
- }
715
- }
716
-
717
- if (count($deleted) > 0) { self::setOptions($options); }
718
-
719
- return $deleted;
720
- }
721
-
722
- /**
723
- * Checks whether exec() may be used.
724
- * Source: http://stackoverflow.com/a/12980534/866618
725
- *
726
- * @return bool Whether exec() is available.
727
- */
728
- public static function isExecAvailable() {
729
- static $available = null;
730
-
731
- if (is_null($available)) {
732
- $available = true;
733
-
734
- if (ini_get('safe_mode')) {
735
- $available = false;
736
- } else {
737
- $d = ini_get('disable_functions');
738
- $s = ini_get('suhosin.executor.func.blacklist');
739
- if ("$d$s") {
740
- $array = preg_split('/,\s*/', "$d,$s");
741
- $available = !in_array('exec', $array);
742
- }
743
- }
744
- }
745
-
746
- return $available;
747
- }
748
-
749
- /**
750
- * Formerly achieved with wp_check_filetype(), but it was only returning
751
- * valid results if the active user had permission to upload the given filetype.
752
- *
753
- * @param str $filename Name of the file to get extension from.
754
- * @return str|bool Returns the file extension on success, false on failure.
755
- */
756
- private static function getExt($filename, $img = false) {
757
- $options = $img ? array('jpg', 'jpeg', 'gif', 'png', 'bmp') : array_keys(wp_get_mime_types());
758
- if ($ext = pathinfo($filename, PATHINFO_EXTENSION)) {
759
- $res = preg_grep('/^(?:.*\|)?' . $ext . '(?:\|.*)?$/i', $options);
760
- $res = reset($res);
761
- if ($res!== false) {
762
- return $ext;
763
- }
764
- }
765
- elseif ( ($info = getimagesize($filename)) && ($ext = image_type_to_extension($info[2], false)) ) {
766
- return $ext;
767
- }
768
-
769
- return false;
770
- }
771
-
772
- /**
773
- * Blocks instantiation. All functions are static.
774
- */
775
- private function __construct() {
776
-
777
- }
778
- }
779
-
780
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /**
5
  * Thumber wraps the functionality required to
9
  */
10
  class DG_Thumber {
11
 
12
+ /**
13
+ * Returns the default mapping of thumber slug to whether it is active or not.
14
+ *
15
+ * @param $skeleton bool When true, values that require computation will be
16
+ * skipped. Useful when only structure of options is needed.
17
+ *
18
+ * @return array The default thumbnail generation methods.
19
+ */
20
+ public static function getDefaultThumbers( $skeleton = false ) {
21
+ $gs_active = $imagick_active = null;
22
+ if ( ! $skeleton ) {
23
+ $gs_active = (bool) self::getGhostscriptExecutable();
24
+ $imagick_active = self::isImagickAvailable();
25
+ }
26
+
27
+ return array(
28
+ 'av' => true,
29
+ 'gs' => $gs_active,
30
+ 'imagick' => $imagick_active
31
+ );
32
+ }
33
+
34
+ /**
35
+ * Sets the thumbnail for the given attachment ID.
36
+ *
37
+ * @param int $ID Document ID.
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 );
45
+ }
46
+
47
+ /**
48
+ * Sets the thumbnail for the given attachment ID to a failed state.
49
+ *
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
+ /**
59
+ * Wraps generation of thumbnails for various attachment filetypes.
60
+ *
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
+ static $start = null;
69
+ if ( is_null( $start ) ) {
70
+ $start = time();
71
+ }
72
+
73
+ $options = self::getOptions();
74
+
75
+ // if we haven't saved a thumb, generate one
76
+ if ( empty( $options['thumbs'][ $ID ] ) ) {
77
+ // short-circuit generation if not required
78
+ if ( ! $generate_if_missing ) {
79
+ return null;
80
+ }
81
+
82
+ // prevent page timing out or user waiting too long for page
83
+ if ( ( time() - $start ) > $options['timeout'] ) {
84
+ return self::getDefaultThumbnail( $ID, $pg );
85
+ }
86
+
87
+ // do the processing
88
+ $file = get_attached_file( $ID );
89
+
90
+ foreach ( self::getThumbers() as $ext_preg => $thumber ) {
91
+ $ext_preg = '!\.(?:' . $ext_preg . ')$!i';
92
+
93
+ if ( preg_match( $ext_preg, $file ) ) {
94
+ if ( DG_Logger::logEnabled() ) {
95
+ $toLog = sprintf( __( 'Attempting to generate thumbnail for attachment #%d with (%s)',
96
+ 'document-gallery' ), $ID, is_array( $thumber ) ? implode( '::', $thumber ) : print_r( $thumber, true ) );
97
+ DG_Logger::writeLog( DG_LogLevel::Detail, $toLog );
98
+ }
99
+
100
+ if ( self::thumbnailGenerationHarness( $thumber, $ID, $pg ) ) {
101
+ // harness updates options so we need a new copy
102
+ $options = self::getOptions();
103
+ break;
104
+ }
105
+ }
106
+ }
107
+ }
108
+
109
+ $new = empty( $options['thumbs'][ $ID ] );
110
+ if ( $new || empty( $options['thumbs'][ $ID ]['thumber'] ) ) {
111
+ if ( $new ) {
112
+ self::setThumbnailFailed( $ID );
113
+ }
114
+
115
+ // fallback to default thumb for attachment type
116
+ $url = self::getDefaultThumbnail( $ID, $pg );
117
+ } else {
118
+ // use generated thumbnail
119
+ $url = $options['thumbs'][ $ID ]['thumb_url'];
120
+ }
121
+
122
+ return $url;
123
+ }
124
+
125
+ /*==========================================================================
126
+ * AUDIO VIDEO THUMBNAILS
127
+ *=========================================================================*/
128
+
129
+ /**
130
+ * Uses wp_read_video_metadata() and wp_read_audio_metadata() to retrieve
131
+ * an embedded image to use as a thumbnail.
132
+ *
133
+ * @param string $ID The attachment ID to retrieve thumbnail from.
134
+ * @param int $pg Unused.
135
+ *
136
+ * @return bool|string False on failure, URL to thumb on success.
137
+ */
138
+ public static function getAudioVideoThumbnail( $ID, $pg = 1 ) {
139
+ include_once DG_WPADMIN_PATH . 'includes/media.php';
140
+
141
+ $attachment = get_post( $ID );
142
+ $doc_path = get_attached_file( $ID );
143
+
144
+ if ( preg_match( '#^video/#', get_post_mime_type( $attachment ) ) ) {
145
+ $metadata = wp_read_video_metadata( $doc_path );
146
+ } elseif ( preg_match( '#^audio/#', get_post_mime_type( $attachment ) ) ) {
147
+ $metadata = wp_read_audio_metadata( $doc_path );
148
+ }
149
+
150
+ // unsupported mime type || no embedded image present
151
+ if ( ! isset( $metadata ) || empty( $metadata['image']['data'] ) ) {
152
+ return false;
153
+ }
154
+
155
+ $ext = 'jpg';
156
+ switch ( $metadata['image']['mime'] ) {
157
+ case 'image/gif':
158
+ $ext = 'gif';
159
+ break;
160
+ case 'image/png':
161
+ $ext = 'png';
162
+ break;
163
+ }
164
+
165
+ $temp_file = self::getTempFile( $ext );
166
+
167
+ if ( ! $fp = @fopen( $temp_file, 'wb' ) ) {
168
+ DG_Logger::writeLog( DG_LogLevel::Error, __( 'Could not open file: ', 'document-gallery' ) . $temp_file );
169
+
170
+ return false;
171
+ }
172
+
173
+ if ( ! @fwrite( $fp, $metadata['image']['data'] ) ) {
174
+ DG_Logger::writeLog( DG_LogLevel::Error, __( 'Could not write file: ', 'document-gallery' ) . $temp_file );
175
+ fclose( $fp );
176
+
177
+ return false;
178
+ }
179
+
180
+ fclose( $fp );
181
+
182
+ return $temp_file;
183
+ }
184
+
185
+ /**
186
+ * @return array All extensions supported by WP Audio Video Media metadata.
187
+ */
188
+ private static function getAudioVideoExts() {
189
+ return array_merge( wp_get_audio_extensions(), wp_get_video_extensions() );
190
+ }
191
+
192
+ /*==========================================================================
193
+ * IMAGICK THUMBNAILS
194
+ *=========================================================================*/
195
+
196
+ /**
197
+ * Uses WP_Image_Editor_Imagick to generate thumbnails.
198
+ *
199
+ * @param int $ID The attachment ID to retrieve thumbnail from.
200
+ * @param int $pg The page to get the thumbnail of.
201
+ *
202
+ * @return bool|string False on failure, URL to thumb on success.
203
+ */
204
+ public static function getImagickThumbnail( $ID, $pg = 1 ) {
205
+ include_once DG_PATH . 'inc/class-image-editor-imagick.php';
206
+
207
+ $doc_path = get_attached_file( $ID );
208
+ $img = new DG_Image_Editor_Imagick( $doc_path, $pg - 1 );
209
+ $err = $img->load();
210
+ if ( is_wp_error( $err ) ) {
211
+ DG_Logger::writeLog(
212
+ DG_LogLevel::Error,
213
+ __( 'Failed to open file in Imagick: ', 'document-gallery' ) .
214
+ $err->get_error_message() );
215
+
216
+ return false;
217
+ }
218
+
219
+ $temp_file = self::getTempFile();
220
+
221
+ $err = $img->save( $temp_file, 'image/png' );
222
+ if ( is_wp_error( $err ) ) {
223
+ DG_Logger::writeLog(
224
+ DG_LogLevel::Error,
225
+ __( 'Failed to save image in Imagick: ', 'document-gallery' ) .
226
+ $err->get_error_message() );
227
+
228
+ return false;
229
+ }
230
+
231
+ return $temp_file;
232
+ }
233
+
234
+ /**
235
+ * @return bool Whether WP_Image_Editor_Imagick can be used on this system.
236
+ */
237
+ public static function isImagickAvailable() {
238
+ static $ret = null;
239
+
240
+ if ( is_null( $ret ) ) {
241
+ include_once DG_WPINC_PATH . 'class-wp-image-editor.php';
242
+ include_once DG_WPINC_PATH . 'class-wp-image-editor-imagick.php';
243
+ $ret = WP_Image_Editor_Imagick::test();
244
+ }
245
+
246
+ return $ret;
247
+ }
248
+
249
+ /*==========================================================================
250
+ * GHOSTSCRIPT THUMBNAILS
251
+ *=========================================================================*/
252
+
253
+ /**
254
+ * Get thumbnail for document with given ID using Ghostscript. Imagick could
255
+ * also handle this, but is *much* slower.
256
+ *
257
+ * @param int $ID The attachment ID to retrieve thumbnail from.
258
+ * @param int $pg The page number to make thumbnail of -- index starts at 1.
259
+ *
260
+ * @return bool|string False on failure, URL to thumb on success.
261
+ */
262
+ public static function getGhostscriptThumbnail( $ID, $pg = 1 ) {
263
+ static $gs = null;
264
+
265
+ if ( is_null( $gs ) ) {
266
+ $options = self::getOptions();
267
+ $gs = $options['gs'];
268
+
269
+ if ( false !== $gs ) {
270
+ $gs = escapeshellarg( $gs ) . ' -sDEVICE=png16m -dFirstPage=%1$d'
271
+ . ' -dLastPage=%1$d -dBATCH -dNOPAUSE -dPDFFitPage -sOutputFile=%2$s %3$s 2>&1';
272
+ }
273
+ }
274
+
275
+ if ( false === $gs ) {
276
+ return false;
277
+ }
278
+
279
+ $doc_path = get_attached_file( $ID );
280
+ $temp_path = self::getTempFile();
281
+
282
+ exec( sprintf( $gs, $pg, $temp_path, $doc_path ), $out, $ret );
283
+
284
+ if ( $ret != 0 ) {
285
+ DG_Logger::writeLog( DG_LogLevel::Error, __( 'Ghostscript failed: ', 'document-gallery' ) . print_r( $out ) );
286
+ @unlink( $temp_path );
287
+
288
+ return false;
289
+ }
290
+
291
+ return $temp_path;
292
+ }
293
+
294
+ /**
295
+ * @return array All extensions supported by Ghostscript.
296
+ */
297
+ private static function getGhostscriptExts() {
298
+ return array( 'pdf', 'ps', 'eps' );
299
+ }
300
+
301
+ /**
302
+ * Dynamically determines whether we may call gs through exec().
303
+ *
304
+ * NOTE: This does not check the options for gs path. Don't use in
305
+ * thumbnail generation as it's slow and not configurable.
306
+ *
307
+ * @return bool|string If available, returns exe path. False otherwise.
308
+ */
309
+ public static function getGhostscriptExecutable() {
310
+ static $executable = null;
311
+
312
+ if ( is_null( $executable ) ) {
313
+ // we must be able to exec()
314
+ $executable = self::isExecAvailable();
315
+ if ( ! $executable ) {
316
+ return $executable;
317
+ }
318
+
319
+ // find on Windows system
320
+ if ( 'WIN' === strtoupper( substr( PHP_OS, 0, 3 ) ) ) {
321
+ // look for environment variable
322
+ $executable = getenv( 'GSC' );
323
+ if ( $executable ) {
324
+ return $executable;
325
+ }
326
+
327
+ // hope GS in the path
328
+ $executable = exec( 'where gswin*c.exe' );
329
+ if ( ! empty( $executable ) ) {
330
+ return $executable;
331
+ }
332
+
333
+ // look directly in filesystem
334
+ // 64- or 32-bit binary
335
+ $executable = exec( 'dir /o:n/s/b "C:\Program Files\gs\*gswin*c.exe"' );
336
+ if ( ! empty( $executable ) ) {
337
+ return $executable;
338
+ }
339
+
340
+ // 32-bit binary on 64-bit OS
341
+ $executable = exec( 'dir /o:n/s/b "C:\Program Files (x86)\gs\*gswin32c.exe"' );
342
+ $executable = empty( $executable ) ? false : $executable;
343
+
344
+ return $executable;
345
+ }
346
+
347
+ // handle Linux systems
348
+ $executable = exec( 'which gs' );
349
+ if ( ! empty( $executable ) ) {
350
+ return $executable;
351
+ }
352
+
353
+ // GoDaddy and others aren't setup in such a way that
354
+ // the above works so we need to fallback to a direct
355
+ // filesystem check in most common location
356
+ exec( 'test -e /usr/bin/gs', $dummy, $ret );
357
+ $executable = ( $ret === 0 ) ? '/usr/bin/gs' : false;
358
+
359
+ return $executable;
360
+ }
361
+
362
+ return $executable;
363
+ }
364
+
365
+ /**
366
+ * @return bool Whether we can use the GS executable.
367
+ */
368
+ public static function isGhostscriptAvailable() {
369
+ static $ret = null;
370
+
371
+ if ( is_null( $ret ) ) {
372
+ $options = self::getOptions();
373
+ $ret = $options['gs'] && self::isExecAvailable();
374
+ }
375
+
376
+ return $ret;
377
+ }
378
+
379
+ /*==========================================================================
380
+ * DEFAULT THUMBNAILS
381
+ *=========================================================================*/
382
+
383
+ /**
384
+ * Get thumbnail for document with given ID from default images.
385
+ *
386
+ * @param string $ID The attachment ID to retrieve thumbnail from.
387
+ * @param int $pg Unused.
388
+ *
389
+ * @return string URL to thumbnail.
390
+ */
391
+ public static function getDefaultThumbnail( $ID, $pg = 1 ) {
392
+ $options = self::getOptions();
393
+ $width = $options['width'];
394
+ $height = $options['height'];
395
+ $icon_url = DG_URL . 'assets/icons/';
396
+
397
+ // handle images
398
+ if ( $icon = image_downsize( $ID, array( $width, $height ) ) ) {
399
+ $icon = $icon[0];
400
+ } // default extension icon
401
+ elseif ( $name = self::getDefaultIcon( self::getExt( wp_get_attachment_url( $ID ) ) ) ) {
402
+ $icon = $icon_url . $name;
403
+ } // fallback to standard WP icons
404
+ elseif ( ! $icon = wp_mime_type_icon( $ID ) ) {
405
+ // everything failed. This is bad...
406
+ $icon = $icon_url . 'missing.png';
407
+ }
408
+
409
+ return $icon;
410
+ }
411
+
412
+ /**
413
+ * Returns the name of the image to represent the filetype given.
414
+ *
415
+ * @param string $ext
416
+ *
417
+ * @return string Default icon based on extension.
418
+ */
419
+ private static function getDefaultIcon( $ext ) {
420
+ // Maps file ext to default image name.
421
+ static $exts = array(
422
+ // Most Common First
423
+ 'pdf' => 'pdf.png',
424
+ // MS Office
425
+ 'doc|docx|docm|dotx|dotm' => 'msdoc.png',
426
+ 'ppt|pot|pps|pptx|pptm|ppsx|ppsm|potx|potm|ppam|sldx|sldm' => 'msppt.png',
427
+ 'xla|xls|xlt|xlw|xlsx|xlsm|xlsb|xltx|xltm|xlam' => 'msxls.png',
428
+ 'mdb' => 'msaccess.png',
429
+ // iWork
430
+ 'key' => 'key.png',
431
+ 'numbers' => 'numbers.png',
432
+ 'pages' => 'pages.png',
433
+ // Images
434
+ 'jpg|jpeg|jpe|gif|png|bmp|tif|tiff|ico' => 'image.png',
435
+ // Video formats
436
+ 'asf|asx|wmv|wmx|wm|avi|divx|flv|mov' => 'video.png',
437
+ 'qt|mpeg|mpg|mpe|mp4|m4v|ogv|webm|mkv' => 'video.png',
438
+ // Audio formats
439
+ 'mp3|m4a|m4b|ra|ram|wav|ogg|oga|wma|wax|mka' => 'audio.png',
440
+ 'midi|mid' => 'midi.png',
441
+ // Text formats
442
+ 'txt|tsv|csv' => 'text.png',
443
+ 'rtx' => 'rtx.png',
444
+ 'rtf' => 'rtf.png',
445
+ 'ics' => 'ics.png',
446
+ 'wp|wpd' => 'wordperfect.png',
447
+ // Programming
448
+ 'html|htm' => 'html.png',
449
+ 'css' => 'css.png',
450
+ 'js' => 'javascript.png',
451
+ 'class' => 'java.png',
452
+ 'asc' => 'asc.png',
453
+ 'c' => 'c.png',
454
+ 'cc|cpp' => 'cpp.png',
455
+ 'h' => 'h.png',
456
+ // Msc application formats
457
+ 'zip|tar|gzip|gz|bz2|tgz|7z|rar' => 'compressed.png',
458
+ 'exe' => 'exec.png',
459
+ 'swf' => 'shockwave.png',
460
+ // OpenDocument formats
461
+ 'odt' => 'opendocument-text.png',
462
+ 'odp' => 'opendocument-presentation.png',
463
+ 'ods' => 'opendocument-spreadsheet.png',
464
+ 'odg' => 'opendocument-graphics.png',
465
+ 'odb' => 'opendocument-database.png',
466
+ 'odf' => 'opendocument-formula.png'
467
+ );
468
+
469
+ foreach ( $exts as $ext_preg => $icon ) {
470
+ $ext_preg = '!(' . $ext_preg . ')$!i';
471
+ if ( preg_match( $ext_preg, $ext ) ) {
472
+ return $icon;
473
+ }
474
+ }
475
+
476
+ return false;
477
+ }
478
+
479
+ /*==========================================================================
480
+ * GENERAL THUMBNAIL HELPER FUNCTIONS
481
+ *=========================================================================*/
482
+
483
+ /**
484
+ * @return array WP_Post objects for each attachment that has been processed.
485
+ */
486
+ public static function getThumbed() {
487
+ $options = self::getOptions();
488
+ $args = array(
489
+ 'post_type' => 'attachment',
490
+ 'post_status' => 'inherit',
491
+ 'post_per_page' => - 1,
492
+ 'post__in' => array_keys( $options['thumbs'] )
493
+ );
494
+
495
+ return count( $args['post__in'] ) ? get_posts( $args ) : array();
496
+ }
497
+
498
+ /**
499
+ * Key: Attachment ID
500
+ * Val: array
501
+ * + timestamp - When the thumbnail was generated (or generation failed).
502
+ * + thumb_path - System path to thumbnail image.
503
+ * + thumb_url - URL pointing to the thumbnail for this document.
504
+ * + thumber - Generator used to create thumb OR false if failed to gen.
505
+ * @return array|null Thumber options from DB or null if options not initialized.
506
+ */
507
+ public static function getOptions( $blog = null ) {
508
+ $options = DocumentGallery::getOptions( $blog );
509
+
510
+ return $options['thumber'];
511
+ }
512
+
513
+ /**
514
+ * Key: Attachment ID
515
+ * Val: array
516
+ * + timestamp - When the thumbnail was generated (or generation failed).
517
+ * + thumb_path - System path to thumbnail image.
518
+ * + thumb_url - URL pointing to the thumbnail for this document.
519
+ * + thumber - Generator used to create thumb OR false if failed to gen.
520
+ *
521
+ * @param array $options Thumber options to store in DB
522
+ */
523
+ private static function setOptions( $options, $blog = null ) {
524
+ $dg_options = DocumentGallery::getOptions( $blog );
525
+ $dg_options['thumber'] = $options;
526
+ DocumentGallery::setOptions( $dg_options, $blog );
527
+ }
528
+
529
+ /**
530
+ * @filter dg_thumbers Allows developers to filter the Thumbers used
531
+ * for specific filetypes. Index is the regex to match file extensions
532
+ * supported and the value is anything that can be accepted by call_user_func().
533
+ * The function must take two parameters, 1st is the int ID of the attachment
534
+ * to get a thumbnail for, 2nd is the page to take a thumbnail of
535
+ * (may not be relevant for some filetypes).
536
+ *
537
+ * @return array
538
+ */
539
+ private static function getThumbers() {
540
+ static $thumbers = null;
541
+
542
+ if ( is_null( $thumbers ) ) {
543
+ $options = self::getOptions();
544
+ $active = $options['active'];
545
+ $thumbers = array();
546
+
547
+ // Audio/Video embedded images
548
+ if ( $active['av'] ) {
549
+ $exts = implode( '|', self::getAudioVideoExts() );
550
+ $thumbers[ $exts ] = array( __CLASS__, 'getAudioVideoThumbnail' );
551
+ }
552
+
553
+ // Ghostscript
554
+ if ( $active['gs'] && self::isGhostscriptAvailable() ) {
555
+ $exts = implode( '|', self::getGhostscriptExts() );
556
+ $thumbers[ $exts ] = array( __CLASS__, 'getGhostscriptThumbnail' );
557
+ }
558
+
559
+ // Imagick
560
+ if ( $active['imagick'] && self::isImagickAvailable() ) {
561
+ include_once DG_PATH . 'inc/class-image-editor-imagick.php';
562
+ if ( $exts = DG_Image_Editor_Imagick::query_formats() ) {
563
+ $exts = implode( '|', $exts );
564
+ $thumbers[ $exts ] = array( __CLASS__, 'getImagickThumbnail' );
565
+ }
566
+ }
567
+
568
+ // allow users to filter thumbers used
569
+ $thumbers = apply_filters( 'dg_thumbers', $thumbers );
570
+
571
+ // strip out anything that can't be called
572
+ $thumbers = array_filter( $thumbers, 'is_callable' );
573
+
574
+ // log which thumbers are being used
575
+ if ( DG_Logger::logEnabled() ) {
576
+ if ( count( $thumbers ) > 0 ) {
577
+ $entry = __( 'Thumbnail Generators: ', 'document-gallery' );
578
+ foreach ( $thumbers as $k => $v ) {
579
+ $thumber = is_array( $v ) ? implode( '::', $v ) : print_r( $v, true );
580
+
581
+ // TODO: The following works for all internal regexes, but may have unpredictable
582
+ // results if developer adds additional thumbnail generators using different regexes
583
+ $filetypes = str_replace( '|', ', ', $k );
584
+
585
+ $entry .= PHP_EOL . "$thumber: $filetypes";
586
+ }
587
+ } else {
588
+ $entry = __( 'No thumbnail generators enabled.', 'document-gallery' );
589
+ }
590
+ DG_Logger::writeLog( DG_LogLevel::Detail, $entry );
591
+ }
592
+ }
593
+
594
+ return $thumbers;
595
+ }
596
+
597
+ /**
598
+ * Template that handles generating a thumbnail.
599
+ *
600
+ * If image has already been generated through other means, $pg may be set to the system path where the
601
+ * thumbnail is located. In this case, $generator will not be invoked, but *will* be kept for historical purposes.
602
+ *
603
+ * @param callable $generator Takes ID and pg and returns path to temp file or false.
604
+ * @param int $ID ID for the attachment that we need a thumbnail for.
605
+ * @param int|string $pg Page number of the attachment to get a thumbnail for or the system path to the image to be used.
606
+ *
607
+ * @return bool Whether generation was successful.
608
+ */
609
+ private static function thumbnailGenerationHarness( $generator, $ID, $pg = 1 ) {
610
+ // handle system page in $pg variable
611
+ if ( is_string( $pg ) && ! is_numeric( $pg ) ) {
612
+ $temp_path = $pg;
613
+ } // delegate thumbnail generation to $generator
614
+ elseif ( false === ( $temp_path = call_user_func( $generator, $ID, $pg ) ) ) {
615
+ return false;
616
+ }
617
+
618
+ // get some useful stuff
619
+ $doc_path = get_attached_file( $ID );
620
+ $doc_url = wp_get_attachment_url( $ID );
621
+ $dirname = dirname( $doc_path );
622
+ $basename = basename( $doc_path );
623
+ if ( false === ( $len = strrpos( $basename, '.' ) ) ) {
624
+ $len = strlen( $basename );
625
+ }
626
+ $extless = substr( $basename, 0, $len );
627
+ $ext = self::getExt( $temp_path );
628
+
629
+ $thumb_name = self::getUniqueThumbName( $dirname, $extless, $ext );
630
+ $thumb_path = $dirname . DIRECTORY_SEPARATOR . $thumb_name;
631
+
632
+ // scale generated image down
633
+ $img = wp_get_image_editor( $temp_path );
634
+
635
+ if ( is_wp_error( $img ) ) {
636
+ DG_Logger::writeLog(
637
+ DG_LogLevel::Error,
638
+ __( 'Failed to get image editor: ', 'document-gallery' ) . $img->get_error_message() );
639
+
640
+ return false;
641
+ }
642
+
643
+ $options = self::getOptions();
644
+ $img->resize( $options['width'], $options['height'], false );
645
+ $err = $img->save( $thumb_path );
646
+
647
+ if ( is_wp_error( $err ) ) {
648
+ DG_Logger::writeLog(
649
+ DG_LogLevel::Error,
650
+ __( 'Failed to save image: ', 'document-gallery' ) .
651
+ $err->get_error_message() );
652
+
653
+ return false;
654
+ }
655
+
656
+ // do some cleanup
657
+ @unlink( $temp_path );
658
+ self::deleteThumbMeta( $ID );
659
+
660
+ // store new thumbnail in DG options
661
+ $options['thumbs'][ $ID ] = array(
662
+ 'timestamp' => time(),
663
+ 'thumb_url' => preg_replace( '#' . preg_quote( $basename ) . '$#', $thumb_name, $doc_url ),
664
+ 'thumb_path' => $thumb_path,
665
+ 'thumber' => $generator
666
+ );
667
+ self::setOptions( $options );
668
+
669
+ return true;
670
+ }
671
+
672
+ /**
673
+ * Caller should handle removal of the temp file when finished.
674
+ *
675
+ * @param string $ext The extension to be given to the temp file.
676
+ *
677
+ * @return string A temp file with the given extension.
678
+ */
679
+ private static function getTempFile( $ext = 'png' ) {
680
+ static $base = null;
681
+ static $tmp;
682
+
683
+ if ( is_null( $base ) ) {
684
+ $base = md5( time() );
685
+ $tmp = untrailingslashit( get_temp_dir() );
686
+ }
687
+
688
+ return $tmp . DIRECTORY_SEPARATOR . wp_unique_filename( $tmp, $base . '.' . $ext );
689
+ }
690
+
691
+ /**
692
+ * Constructs name for file's thumbnail, ensuring that it does not conflict
693
+ * with any existing file.
694
+ *
695
+ * @param string $dirname Directory where the document is located.
696
+ * @param string $extless Base name, less the extension.
697
+ * @param string $ext The extension of the image to be created.
698
+ *
699
+ * @return string Name unique within the directory given, derived from the basename given.
700
+ */
701
+ private static function getUniqueThumbName( $dirname, $extless, $ext = 'png' ) {
702
+ return wp_unique_filename( $dirname, str_replace( '.', '-', $extless ) . '-thumb.' . $ext );
703
+ }
704
+
705
+ /**
706
+ * Removes the existing thumbnail/document meta for the attachment(s)
707
+ * with the ID(s), if such a thumbnails exists.
708
+ *
709
+ * @param int|array $ids
710
+ *
711
+ * @return array All IDs that were deleted -- some subset of IDs requested to be deleted.
712
+ */
713
+ public static function deleteThumbMeta( $ids ) {
714
+ $options = self::getOptions();
715
+
716
+ $deleted = array();
717
+ foreach ( (array) $ids as $id ) {
718
+ if ( isset( $options['thumbs'][ $id ] ) ) {
719
+ if ( isset( $options['thumbs'][ $id ]['thumber'] ) ) {
720
+ @unlink( $options['thumbs'][ $id ]['thumb_path'] );
721
+ }
722
+
723
+ unset( $options['thumbs'][ $id ] );
724
+ $deleted[] = $id;
725
+ }
726
+ }
727
+
728
+ if ( count( $deleted ) > 0 ) {
729
+ self::setOptions( $options );
730
+ }
731
+
732
+ return $deleted;
733
+ }
734
+
735
+ /**
736
+ * Checks whether exec() may be used.
737
+ * Source: http://stackoverflow.com/a/12980534/866618
738
+ *
739
+ * @return bool Whether exec() is available.
740
+ */
741
+ public static function isExecAvailable() {
742
+ static $available = null;
743
+
744
+ if ( is_null( $available ) ) {
745
+ $available = true;
746
+
747
+ if ( ini_get( 'safe_mode' ) ) {
748
+ $available = false;
749
+ } else {
750
+ $d = ini_get( 'disable_functions' );
751
+ $s = ini_get( 'suhosin.executor.func.blacklist' );
752
+ if ( "$d$s" ) {
753
+ $array = preg_split( '/,\s*/', "$d,$s" );
754
+ $available = ! in_array( 'exec', $array );
755
+ }
756
+ }
757
+ }
758
+
759
+ return $available;
760
+ }
761
+
762
+ /**
763
+ * Formerly achieved with wp_check_filetype(), but it was only returning
764
+ * valid results if the active user had permission to upload the given filetype.
765
+ *
766
+ * @param string $filename Name of the file to get extension from.
767
+ *
768
+ * @return bool|string Returns the file extension on success, false on failure.
769
+ */
770
+ private static function getExt( $filename ) {
771
+ if ( $ext = pathinfo( $filename, PATHINFO_EXTENSION ) ) {
772
+ $res = preg_grep( '/^(?:.*\|)?' . $ext . '(?:\|.*)?$/i', self::getAllExts() );
773
+ $res = reset( $res );
774
+ if ( $res !== false ) {
775
+ return $ext;
776
+ }
777
+ } elseif ( ( $info = getimagesize( $filename ) ) && ( $ext = image_type_to_extension( $info[2], false ) ) ) {
778
+ return $ext;
779
+ }
780
+
781
+ return false;
782
+ }
783
+
784
+ /**
785
+ * Addresses issues with getting a complete list of supported MIME types as
786
+ * described in this issue: https://core.trac.wordpress.org/ticket/32544
787
+ * @return array Contains all MIME types supported by WordPress, including custom types added by plugins.
788
+ */
789
+ private static function getAllExts() {
790
+ return array_keys( array_merge( wp_get_mime_types(), get_allowed_mime_types() ) );
791
+ }
792
+
793
+ /**
794
+ * Blocks instantiation. All functions are static.
795
+ */
796
+ private function __construct() {
797
+
798
+ }
799
+ }
inc/class-util.php CHANGED
@@ -1,5 +1,5 @@
1
  <?php
2
- defined('WPINC') OR exit;
3
 
4
  /**
5
  * General utility function for Document Gallery.
@@ -7,74 +7,129 @@ defined('WPINC') OR exit;
7
  * @author drossiter
8
  */
9
  class DG_Util {
10
- /**
11
- * @var callable Either native JSON encode or custom JSON encode if needed.
12
- */
13
- private static $nativeJsonEncode;
14
-
15
- /**
16
- * Wraps JSON encoding functionality, utilizing native functions if available.
17
- *
18
- * @param unknown $decoded Value to be encoded.
19
- * @return string The JSON string.
20
- */
21
- public static function jsonEncode($decoded) {
22
- if (!isset(self::$nativeJsonEncode)) {
23
- self::$nativeJsonEncode = function_exists('json_encode');
24
- }
25
-
26
- // do encoding
27
- return self::$nativeJsonEncode ? json_encode($decoded) : self::_jsonEncode($decoded);
28
- }
29
-
30
- /**
31
- * Home-made JSON encode to replace mssing json_encode when needed.
32
- *
33
- * @param unknown $decoded Value to be encoded.
34
- * @return string The JSON string.
35
- */
36
- private static function _jsonEncode($decoded) {
37
- if (self::isJsonObj($decoded)) {
38
- $ret = '';
39
- $first = true;
40
- foreach ($decoded as $k => $v) {
41
- if (!$first) $ret .= ',';
42
- $ret .= "\"$k\":" . self::_jsonEncode($v);
43
- $first = false;
44
- }
45
-
46
- return "\{$ret\}";
47
- } elseif (is_array($decoded)) {
48
- return '[' . implode(',', array_map(array(__CLASS__, __FUNCTION__), $decoded)) . ']';
49
- } elseif (is_bool($decoded)) {
50
- static $boolMap = array('false', 'true');
51
- return $boolMap[(int)$decoded];
52
- } elseif (is_string($decoded)) {
53
- return '"' . str_replace(array('\\', '"'), array('\\\\', '\\"'), $decoded) . '"';
54
- }
55
-
56
- return (string)$decoded;
57
- }
58
-
59
- /**
60
- * Returns true for PHP objects and associative arrays.
61
- *
62
- * @param unknown $decoded Value to be checked.
63
- * @return bool Whether passed value should be encoded as a JSON object.
64
- */
65
- private static function isJsonObj($decoded) {
66
- $ret = is_object($decoded);
67
-
68
- if (!$ret && is_array($decoded)) {
69
- $next = 0;
70
- foreach (array_keys($decoded) as $k) {
71
- if ($next++ !== $k) {
72
- $ret = true;
73
- break;
74
- }
75
- }
76
- }
77
-
78
- return $ret;
79
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  }
1
  <?php
2
+ defined( 'WPINC' ) OR exit;
3
 
4
  /**
5
  * General utility function for Document Gallery.
7
  * @author drossiter
8
  */
9
  class DG_Util {
10
+ /**
11
+ * @var callable Either native JSON encode or custom JSON encode if needed.
12
+ */
13
+ private static $nativeJsonEncode;
14
+
15
+ /**
16
+ * Wraps JSON encoding functionality, utilizing native functions if available.
17
+ *
18
+ * @param unknown $decoded Value to be encoded.
19
+ *
20
+ * @return string The JSON string.
21
+ */
22
+ public static function jsonEncode( $decoded ) {
23
+ if ( ! isset( self::$nativeJsonEncode ) ) {
24
+ self::$nativeJsonEncode = function_exists( 'json_encode' );
25
+ }
26
+
27
+ // do encoding
28
+ return self::$nativeJsonEncode ? json_encode( $decoded ) : self::_jsonEncode( $decoded );
29
+ }
30
+
31
+ /**
32
+ * Home-made JSON encode to replace missing json_encode when needed.
33
+ *
34
+ * @param unknown $decoded Value to be encoded.
35
+ *
36
+ * @return string The JSON string.
37
+ */
38
+ private static function _jsonEncode( $decoded ) {
39
+ if ( self::isJsonObj( $decoded ) ) {
40
+ $ret = '';
41
+ $first = true;
42
+ foreach ( $decoded as $k => $v ) {
43
+ if ( ! $first ) {
44
+ $ret .= ',';
45
+ }
46
+ $ret .= "\"$k\":" . self::_jsonEncode( $v );
47
+ $first = false;
48
+ }
49
+
50
+ return "\{$ret\}";
51
+ } elseif ( is_array( $decoded ) ) {
52
+ return '[' . implode( ',', array_map( array( __CLASS__, __FUNCTION__ ), $decoded ) ) . ']';
53
+ } elseif ( is_bool( $decoded ) ) {
54
+ static $boolMap = array( 'false', 'true' );
55
+
56
+ return $boolMap[ (int) $decoded ];
57
+ } elseif ( is_string( $decoded ) ) {
58
+ return '"' . str_replace( array( '\\', '"' ), array( '\\\\', '\\"' ), $decoded ) . '"';
59
+ }
60
+
61
+ return (string) $decoded;
62
+ }
63
+
64
+ /**
65
+ * Returns true for PHP objects and associative arrays.
66
+ *
67
+ * @param unknown $decoded Value to be checked.
68
+ *
69
+ * @return bool Whether passed value should be encoded as a JSON object.
70
+ */
71
+ private static function isJsonObj( $decoded ) {
72
+ $ret = is_object( $decoded );
73
+
74
+ if ( ! $ret && is_array( $decoded ) ) {
75
+ $next = 0;
76
+ foreach ( array_keys( $decoded ) as $k ) {
77
+ if ( $next ++ !== $k ) {
78
+ $ret = true;
79
+ break;
80
+ }
81
+ }
82
+ }
83
+
84
+ return $ret;
85
+ }
86
+
87
+ /**
88
+ * @return array All blog IDs.
89
+ */
90
+ public static function getBlogIds() {
91
+ global $wpdb;
92
+ return $wpdb->get_col( "SELECT blog_id FROM $wpdb->blogs" );
93
+ }
94
+
95
+ /**
96
+ * Converts provided value to bool.
97
+ *
98
+ * @param unknown $val To be converted.
99
+ * @param bool|NULL $default The value to return if unable to parse $val.
100
+ *
101
+ * @return bool|NULL Bool value if can be parsed, else NULL.
102
+ */
103
+ public static function toBool( $val, $default = null ) {
104
+ if ( is_null( $val ) ) {
105
+ return false;
106
+ }
107
+
108
+ if ( is_bool( $val ) ) {
109
+ return $val;
110
+ }
111
+
112
+ if ( is_int( $val ) ) {
113
+ if ( 1 === $val ) {
114
+ return true;
115
+ }
116
+
117
+ if ( 0 === $val ) {
118
+ return false;
119
+ }
120
+ }
121
+
122
+ if ( is_string( $val ) ) {
123
+ $val = strtolower( $val );
124
+ if ( 'true' === $val || '1' === $val ) {
125
+ return true;
126
+ }
127
+
128
+ if ( 'false' === $val || '0' === $val ) {
129
+ return false;
130
+ }
131
+ }
132
+
133
+ return $default;
134
+ }
135
  }
languages/{document-gallery-fi_FI.mo → document-gallery-fi.mo} RENAMED
File without changes
languages/{document-gallery-fi_FI.po → document-gallery-fi.po} RENAMED
File without changes
languages/document-gallery-ru_RU.mo CHANGED
Binary file
languages/document-gallery-ru_RU.po CHANGED
@@ -1,14 +1,14 @@
1
- # Copyright (C) 2014 Document Gallery
2
  # This file is distributed under the same license as the Document Gallery package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Document Gallery 2.2.7\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/document-gallery\n"
7
- "POT-Creation-Date: 2014-09-04 02:15:50+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2014-12-14 13:32-0600\n"
12
  "Last-Translator: Dan Rossiter <dan.rossiters@gmail.com>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
  "X-Generator: Poedit 1.6.9\n"
@@ -16,76 +16,106 @@ msgstr ""
16
  "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
17
  "Language: ru_RU\n"
18
 
19
- #: admin/class-admin.php:28
20
  msgid "General"
21
  msgstr "Основные"
22
 
23
- #: admin/class-admin.php:29
24
  msgid "Thumbnail Management"
25
  msgstr "Управление миниатюрами"
26
 
27
- #: admin/class-admin.php:30 admin/class-admin.php:898
28
  msgid "Logging"
29
  msgstr "Журнал событий"
30
 
31
- #: admin/class-admin.php:31
32
  msgid "Advanced"
33
  msgstr "Для опытных пользователей"
34
 
35
- #: admin/class-admin.php:68
 
 
 
 
 
36
  msgid "Settings"
37
  msgstr "Настройки"
38
 
39
- #: admin/class-admin.php:78
40
- msgid "Document Gallery Settings"
41
- msgstr "Настройки Document Gallery"
42
 
 
43
  #. Plugin Name of the plugin/theme
44
- #: admin/class-admin.php:79
45
  msgid "Document Gallery"
46
  msgstr "Document Gallery"
47
 
48
- #: admin/class-admin.php:124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  msgid "Default Settings"
50
  msgstr "Настройки по умолчанию"
51
 
52
- #: admin/class-admin.php:128
53
  msgid "Thumbnail Generation"
54
  msgstr "Создание миниатюр"
55
 
56
- #: admin/class-admin.php:132
57
  msgid "Custom CSS"
58
  msgstr "Индивидуальные CSS"
59
 
60
- #: admin/class-admin.php:144
61
  msgid "Link to attachment page rather than to file"
62
  msgstr "Ссылка на страницу вложения, а не на сам файл"
63
 
64
- #: admin/class-admin.php:156
 
 
 
 
65
  msgid "Include document descriptions"
66
  msgstr "Включать описания документов"
67
 
68
- #: admin/class-admin.php:168
69
  msgid "Use auto-generated document thumbnails"
70
  msgstr "Использовать автоматически создаваемые миниатюры документов"
71
 
72
- #: admin/class-admin.php:180
73
- msgid "Include image attachments in gallery"
74
- msgstr "Включать прикрепленные к посту картинки в галерею"
75
-
76
- #: admin/class-admin.php:192
77
- msgid "Only look for attachments in post where [dg] is used"
78
- msgstr "Искать вложения только в постах, где используется [dg]"
79
-
80
- #: admin/class-admin.php:205
81
  msgid "Ascending or descending sorting of documents"
82
  msgstr "Сортировка документов по возрастанию или по убыванию"
83
 
84
- #: admin/class-admin.php:218
85
  msgid "Which field to order documents by"
86
  msgstr "По какому полю сортировать документы"
87
 
88
- #: admin/class-admin.php:231
89
  msgid ""
90
  "Whether matched documents must have all taxa_names (AND) or at least one (OR)"
91
  msgstr ""
@@ -93,11 +123,39 @@ msgstr ""
93
  "параметре category, также возможно использование taxon_name, или хотя бы "
94
  "одной (OR)"
95
 
96
- #: admin/class-admin.php:243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  msgid "Locally generate thumbnails for audio & video files."
98
  msgstr "Локально создавать миниатюры для аудио и видео файлов."
99
 
100
- #: admin/class-admin.php:256
101
  msgid ""
102
  "Use <a href=\"http://www.ghostscript.com/\" target=\"_blank\">Ghostscript</"
103
  "a> for faster local PDF processing (compared to Imagick)."
@@ -106,7 +164,7 @@ msgstr ""
106
  "\">Ghostscript</a> для более быстрой локальной обработки PDF (в сравнении с "
107
  "Imagick)."
108
 
109
- #: admin/class-admin.php:257
110
  msgid ""
111
  "Your server is not configured to run <a href=\"http://www.ghostscript.com/\" "
112
  "target=\"_blank\">Ghostscript</a>."
@@ -114,7 +172,7 @@ msgstr ""
114
  "Ваш сервер не настроен для использования <a href=\"http://www.ghostscript."
115
  "com/\" target=\"_blank\">Ghostscript</a>."
116
 
117
- #: admin/class-admin.php:271
118
  msgid ""
119
  "Use <a href=\"http://www.php.net/manual/en/book.imagick.php\" target=\"_blank"
120
  "\">Imagick</a> to handle lots of filetypes locally."
@@ -122,7 +180,7 @@ msgstr ""
122
  "Использовать <a href=\"http://www.php.net/manual/ru/book.imagick.php\" "
123
  "target=\"_blank\">Imagick</a> для локальной обработки множества типов файлов."
124
 
125
- #: admin/class-admin.php:272
126
  msgid ""
127
  "Your server is not configured to run <a href=\"http://www.php.net/manual/en/"
128
  "book.imagick.php\" target=\"_blank\">Imagick</a>."
@@ -130,37 +188,43 @@ msgstr ""
130
  "Ваш сервер не настроен для использования <a href=\"http://www.php.net/manual/"
131
  "ru/book.imagick.php\" target=\"_blank\">Imagick</a>."
132
 
133
- #: admin/class-admin.php:286
134
- msgid ""
135
- "Use <a href=\"https://drive.google.com/viewer\" target=\"_blank\">Google "
136
- "Drive Viewer</a> to generate thumbnails for MS Office files and many other "
137
- "file types remotely."
138
- msgstr ""
139
- "Использовать <a href=\"https://drive.google.com/viewer\" target=\"_blank"
140
- "\">Google Drive Viewer</a> для создания миниатюр файлов MS Office и многих "
141
- "других типов файлов удалённо."
142
-
143
- #: admin/class-admin.php:287
144
- msgid "Your server does not allow remote HTTP access."
145
- msgstr "Ваш сервер не разрешает удаленные HTTP обращения."
146
 
147
- #: admin/class-admin.php:309
148
  msgid "The max width and height (in pixels) that thumbnails will be generated."
149
  msgstr ""
150
  "Максимальная ширина и высота (в пикселях) с которыми будут создаваться "
151
  "миниатюры."
152
 
153
- #: admin/class-admin.php:338
154
  msgid "Advanced Thumbnail Generation"
155
  msgstr "Создание миниатюр"
156
 
157
- #: admin/class-admin.php:350
 
 
 
 
158
  msgid "Whether to log debug and error information related to Document Gallery."
159
  msgstr ""
160
  "Вести ли журнал отладки и информации об ошибках имеющих отношение к Document "
161
  "Gallery."
162
 
163
- #: admin/class-admin.php:362
 
 
 
 
 
 
 
 
 
 
 
 
164
  msgid ""
165
  "Whether option structure should be validated before save. This is not "
166
  "generally necessary."
@@ -168,7 +232,11 @@ msgstr ""
168
  "Проверять ли структуру выбранных настроек при сохранении. Как правило в этом "
169
  "нет необходимости."
170
 
171
- #: admin/class-admin.php:375
 
 
 
 
172
  msgid ""
173
  "Max number of seconds to wait for thumbnail generation before defaulting to "
174
  "filetype icons."
@@ -176,7 +244,7 @@ msgstr ""
176
  "Максимальное количество секунд ожидания при создании миниатюры до "
177
  "использования иконки по умолчанию."
178
 
179
- #: admin/class-admin.php:376
180
  msgid ""
181
  "Note that generation will continue where timeout happened next time the "
182
  "gallery is loaded."
@@ -184,35 +252,59 @@ msgstr ""
184
  "Обратите внимание, что создание продолжится с того места, где оно было "
185
  "приостановлено, при следующем обращении к галереи."
186
 
187
- #: admin/class-admin.php:388
 
 
 
 
188
  msgid "Successfully auto-detected the location of Ghostscript."
189
  msgstr "Успешно автоматически определено местонахождение Ghostscript."
190
 
191
- #: admin/class-admin.php:389
192
  msgid "Failed to auto-detect the location of Ghostscript."
193
  msgstr "Не удалось автоматически определить местонахождение Ghostscript."
194
 
195
- #: admin/class-admin.php:393
196
  msgid "Options Array Dump"
197
  msgstr "Дамп массива установленных настроек"
198
 
199
- #: admin/class-admin.php:440
200
  msgid "Invalid width given: "
201
  msgstr "Задана неверная ширина: "
202
 
203
- #: admin/class-admin.php:453
204
  msgid "Invalid height given: "
205
  msgstr "Задана неверная высота: "
206
 
207
- #: admin/class-admin.php:566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  msgid "Invalid Ghostscript path given: "
209
  msgstr "Задан неверный путь Ghostscript: "
210
 
211
- #: admin/class-admin.php:577
212
  msgid "Invalid timeout given: "
213
  msgstr "Задано неверное время ожидания: "
214
 
215
- #: admin/class-admin.php:608
 
 
 
 
216
  msgid ""
217
  "The following values will be used by default in the shortcode. You can still "
218
  "manually set each of these values in each individual shortcode."
@@ -221,11 +313,11 @@ msgstr ""
221
  "этом вручную можно установить любые из этих значений в каждом конкретном "
222
  "экземпляре шорткода."
223
 
224
- #: admin/class-admin.php:615
225
  msgid "Select which tools to use when generating thumbnails."
226
  msgstr "Выберите, какие инструменты использовать для создании миниатюр."
227
 
228
- #: admin/class-admin.php:624
229
  msgid ""
230
  "Enter custom CSS styling for use with document galleries. To see which ids "
231
  "and classes you can style, take a look at <a href=\"%s\" target=\"_blank"
@@ -235,7 +327,7 @@ msgstr ""
235
  "Чтобы увидеть, какие селекторы стоит использовать, посмотрите на <a href=\"%s"
236
  "\" target=\"_blank\">style.css</a>."
237
 
238
- #: admin/class-admin.php:642
239
  msgid ""
240
  "Unless you <em>really</em> know what you're doing, you should not touch "
241
  "these values."
@@ -243,13 +335,13 @@ msgstr ""
243
  "Не стоит менять этих значений, за исключением случаев когда Вы "
244
  "<em>действительно</em> знаете что делаете."
245
 
246
- #: admin/class-admin.php:645
247
  msgid ""
248
  "NOTE: <code>exec()</code> is not accessible. Ghostscript will not function."
249
  msgstr ""
250
  "ВНИМАНИЕ: <code>exec()</code> недоступный. Ghostscript не будет работать."
251
 
252
- #: admin/class-admin.php:656
253
  msgid ""
254
  "The following <em>readonly text</em> should be provided when <a href="
255
  "\"http://wordpress.org/support/plugin/document-gallery\" target=\"_blank"
@@ -259,188 +351,203 @@ msgstr ""
259
  "предоставлен при <a href=\"http://wordpress.org/support/plugin/document-"
260
  "gallery\" target=\"_blank\">сообщении об ошибке</a>:"
261
 
262
- #: admin/class-admin.php:757
263
  msgid "Select All"
264
  msgstr "Выбрать всё"
265
 
266
- #: admin/class-admin.php:760
267
  msgid "Thumbnail"
268
  msgstr "Миниатюра"
269
 
270
- #: admin/class-admin.php:761
271
  msgid "File name"
272
  msgstr "Имя файла"
273
 
274
- #: admin/class-admin.php:762 admin/class-admin.php:834
 
 
 
 
275
  msgid "Date"
276
  msgstr "Дата"
277
 
278
- #: admin/class-admin.php:765
279
  msgid "Delete Selected"
280
  msgstr "Удалить выбранные"
281
 
282
- #: admin/class-admin.php:767
283
  msgid "item"
284
  msgid_plural "items"
285
  msgstr[0] "запись"
286
  msgstr[1] "записи"
287
  msgstr[2] "записей"
288
 
289
- #: admin/class-admin.php:770
290
  msgid "Go to the first page"
291
  msgstr "Перейти к первой странице"
292
 
293
- #: admin/class-admin.php:771
294
  msgid "Go to the previous page"
295
  msgstr "Перейти к предыдущей странице"
296
 
297
- #: admin/class-admin.php:773
298
  msgid "Current page"
299
  msgstr "Текущая страница"
300
 
301
- #: admin/class-admin.php:773
302
  msgid "of"
303
  msgstr "из"
304
 
305
- #: admin/class-admin.php:774
306
  msgid "Go to the next page"
307
  msgstr "Перейти к следующей странице"
308
 
309
- #: admin/class-admin.php:775
310
  msgid "Go to the last page"
311
  msgstr "Перейти к последней странице"
312
 
313
- #: admin/class-admin.php:777
314
  msgid "items per page"
315
  msgstr "записей на странице"
316
 
317
- # Due to language structure translation of "View" and "attachment page" strings were merged into one
318
- #: admin/class-admin.php:815
319
- msgid "View"
320
- msgstr "Посмотреть страницу вложения"
321
 
322
- # Due to language structure translation of "View" and "attachment page" strings were merged into one
323
- #: admin/class-admin.php:816
324
- msgid "attachment page"
325
- msgstr ""
326
-
327
- #: admin/class-admin.php:816
328
  msgid "Attachment not found"
329
  msgstr "Вложение не найдено"
330
 
331
- #: admin/class-admin.php:835
 
 
 
 
332
  msgid "Level"
333
  msgstr "Уровень"
334
 
335
- #: admin/class-admin.php:836
336
  msgid "Message"
337
  msgstr "Сообщение"
338
 
339
- #: admin/class-admin.php:845
340
  msgid "Expand All"
341
  msgstr "Развернуть Все"
342
 
343
- #: admin/class-admin.php:848
344
  msgid "Collapse All"
345
  msgstr "Свернуть Все"
346
 
347
- #: admin/class-admin.php:891
348
  msgid "Clear Log"
349
  msgstr "Очистить журнал"
350
 
351
- #: admin/class-admin.php:898
352
  msgid "There are no log entries at this time."
353
  msgstr "На данный момент журнал пуст."
354
 
355
- #: admin/class-admin.php:898
356
  msgid "For Your information:"
357
  msgstr "К Вашему сведению:"
358
 
359
- #: admin/class-admin.php:898
360
  msgid "is turned ON"
361
  msgstr "включён"
362
 
363
- #: admin/class-admin.php:898
364
  msgid "is turned OFF"
365
  msgstr "выключен"
366
 
367
- #: inc/class-gallery.php:86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  msgid "Generated using Document Gallery. Get yours here: "
369
  msgstr ""
370
  "Сгенерировано используя Document Gallery. Понравилось? Получите Вашу копию "
371
  "здесь: "
372
 
373
- #: inc/class-gallery.php:88
374
  msgid "No attachments to display. How boring! :("
375
  msgstr "Ни одного вложения для отображения. Скукотища! :("
376
 
377
- #: inc/class-gallery.php:89
 
 
 
 
378
  msgid "The %s parameter may only be \"%s\" or \"%s.\" You entered \"%s.\""
379
  msgstr ""
380
  "Параметр %s может принимать только значения \"%s\" или \"%s.\" Вы же ввели "
381
  "\"%s.\""
382
 
383
- #: inc/class-gallery.php:286
384
- msgid "The following ID is invalid: "
385
- msgid_plural "The following IDs are invalid: "
386
- msgstr[0] "Следующий идентификатор (ID) недействительный: "
387
- msgstr[1] "Следующие идентификаторы (IDs) недействительны: "
388
- msgstr[2] "Следующие идентификаторы (IDs) недействительны: "
389
 
390
- #: inc/class-gallery.php:379
391
- msgid "The orderby value entered, \"%s,\" is not valid."
392
- msgstr "Указанное значение параметра orderby, \"%s,\" недопустимо."
393
 
394
- #: inc/class-gallery.php:533
395
  msgid "%s is not a valid term name in %s."
396
  msgstr "Определение %s не является допустимым значением параметра %s."
397
 
398
- #: inc/class-image-editor-imagick.php:37
399
  msgid "Failed to set Imagick page number"
400
  msgstr "Не удалось установить номер страницы в Imagick"
401
 
402
- #: inc/class-thumber.php:59
403
  msgid "Attempting to generate thumbnail for attachment #%d with (%s)"
404
  msgstr "Попытка создания миниатюры для вложения №%d с помощью (%s)"
405
 
406
- #: inc/class-thumber.php:138
407
  msgid "Could not open file: "
408
  msgstr "Не удалось открыть файл: "
409
 
410
- #: inc/class-thumber.php:143
411
  msgid "Could not write file: "
412
  msgstr "Не удалось записать файл: "
413
 
414
- #: inc/class-thumber.php:180
415
  msgid "Failed to open file in Imagick: "
416
  msgstr "Не удалось открыть файл в Imagick: "
417
 
418
- #: inc/class-thumber.php:191
419
  msgid "Failed to save image in Imagick: "
420
  msgstr "Не удалось сохранить изображение в Imagick: "
421
 
422
- #: inc/class-thumber.php:249
423
  msgid "Ghostscript failed: "
424
  msgstr "Сбой в Ghostscript: "
425
 
426
- #: inc/class-thumber.php:387
427
- msgid "Failed to retrieve thumbnail from Google: "
428
- msgstr "Не удалось получить от Google миниатюру: "
429
-
430
- #: inc/class-thumber.php:631
431
  msgid "Thumbnail Generators: "
432
  msgstr "Генераторы миниатюр: "
433
 
434
- #: inc/class-thumber.php:642
435
  msgid "No thumbnail generators enabled."
436
  msgstr "Отсутствуют включенные генераторы миниатюр."
437
 
438
- #: inc/class-thumber.php:685
439
  msgid "Failed to get image editor: "
440
  msgstr ""
441
  "Сбой при попытке использовании графического редактора (WP_Image_Editor): "
442
 
443
- #: inc/class-thumber.php:697
444
  msgid "Failed to save image: "
445
  msgstr "Не удалось сохранить изображение: "
446
 
1
+ # Copyright (C) 2015 Document Gallery
2
  # This file is distributed under the same license as the Document Gallery package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Document Gallery 3.3\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/document-gallery\n"
7
+ "POT-Creation-Date: 2015-06-20 17:03:17+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2015-06-21 11:40-0600\n"
12
  "Last-Translator: Dan Rossiter <dan.rossiters@gmail.com>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
  "X-Generator: Poedit 1.6.9\n"
16
  "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
17
  "Language: ru_RU\n"
18
 
19
+ #: admin/class-admin.php:30
20
  msgid "General"
21
  msgstr "Основные"
22
 
23
+ #: admin/class-admin.php:31
24
  msgid "Thumbnail Management"
25
  msgstr "Управление миниатюрами"
26
 
27
+ #: admin/class-admin.php:32 admin/class-admin.php:1270
28
  msgid "Logging"
29
  msgstr "Журнал событий"
30
 
31
+ #: admin/class-admin.php:33
32
  msgid "Advanced"
33
  msgstr "Для опытных пользователей"
34
 
35
+ #: admin/class-admin.php:45 admin/class-admin.php:99
36
+ #: admin/media-manager-template.php:5
37
+ msgid "Document Gallery Settings"
38
+ msgstr "Настройки Document Gallery"
39
+
40
+ #: admin/class-admin.php:73
41
  msgid "Settings"
42
  msgstr "Настройки"
43
 
44
+ #: admin/class-admin.php:87
45
+ msgid "Donate"
46
+ msgstr "Поддержать"
47
 
48
+ #. #-#-#-#-# plugin.pot (Document Gallery 3.3) #-#-#-#-#
49
  #. Plugin Name of the plugin/theme
50
+ #: admin/class-admin.php:100
51
  msgid "Document Gallery"
52
  msgstr "Document Gallery"
53
 
54
+ #: admin/class-admin.php:127
55
+ msgid "Create Document Gallery"
56
+ msgstr "Создать галерею Document Gallery"
57
+
58
+ #: admin/class-admin.php:128
59
+ msgid "Create a new Document Gallery"
60
+ msgstr "Создать новую галерею Document Gallery"
61
+
62
+ #: admin/class-admin.php:129
63
+ msgid "Cancel Document Gallery"
64
+ msgstr "Отменить создание галереи Document Gallery"
65
+
66
+ #: admin/class-admin.php:130
67
+ msgid "Update Document Gallery"
68
+ msgstr "Обновить галерею Document Gallery"
69
+
70
+ #: admin/class-admin.php:131
71
+ msgid "Insert Document Gallery"
72
+ msgstr "Вставить галерею Document Gallery"
73
+
74
+ #: admin/class-admin.php:132 admin/class-admin.php:133
75
+ msgid "Add to Document Gallery"
76
+ msgstr "Добавить в галерею Document Gallery"
77
+
78
+ #: admin/class-admin.php:134
79
+ msgid "Edit Document Gallery"
80
+ msgstr "Редактировать галерею Document Gallery"
81
+
82
+ #: admin/class-admin.php:178
83
  msgid "Default Settings"
84
  msgstr "Настройки по умолчанию"
85
 
86
+ #: admin/class-admin.php:182
87
  msgid "Thumbnail Generation"
88
  msgstr "Создание миниатюр"
89
 
90
+ #: admin/class-admin.php:186
91
  msgid "Custom CSS"
92
  msgstr "Индивидуальные CSS"
93
 
94
+ #: admin/class-admin.php:198
95
  msgid "Link to attachment page rather than to file"
96
  msgstr "Ссылка на страницу вложения, а не на сам файл"
97
 
98
+ #: admin/class-admin.php:211
99
+ msgid "The number of columns to display when not rendering descriptions."
100
+ msgstr "Количество отображаемых колонок при отключенных описаниях."
101
+
102
+ #: admin/class-admin.php:223 admin/media-manager-template.php:50
103
  msgid "Include document descriptions"
104
  msgstr "Включать описания документов"
105
 
106
+ #: admin/class-admin.php:235 admin/media-manager-template.php:57
107
  msgid "Use auto-generated document thumbnails"
108
  msgstr "Использовать автоматически создаваемые миниатюры документов"
109
 
110
+ #: admin/class-admin.php:248 admin/media-manager-template.php:79
 
 
 
 
 
 
 
 
111
  msgid "Ascending or descending sorting of documents"
112
  msgstr "Сортировка документов по возрастанию или по убыванию"
113
 
114
+ #: admin/class-admin.php:261 admin/media-manager-template.php:64
115
  msgid "Which field to order documents by"
116
  msgstr "По какому полю сортировать документы"
117
 
118
+ #: admin/class-admin.php:274
119
  msgid ""
120
  "Whether matched documents must have all taxa_names (AND) or at least one (OR)"
121
  msgstr ""
123
  "параметре category, также возможно использование taxon_name, или хотя бы "
124
  "одной (OR)"
125
 
126
+ #: admin/class-admin.php:287
127
+ msgid "Limit the number of documents included. -1 means no limit."
128
+ msgstr "Максимальное количество включаемых документов. -1 означает без ограничений."
129
+
130
+ #: admin/class-admin.php:300
131
+ msgid ""
132
+ "Comma-delimited list of <a href=\"http://en.wikipedia.org/wiki/"
133
+ "Internet_media_type#List_of_common_media_types\">MIME types</a>."
134
+ msgstr ""
135
+ "Список <a href=\"http://en.wikipedia.org/wiki/Internet_media_type"
136
+ "#List_of_common_media_types\">MIME-типов</a> разделённых запятыми."
137
+
138
+ #: admin/class-admin.php:312 admin/media-manager-template.php:43
139
+ msgid "Open thumbnail links in new window"
140
+ msgstr "Открывать ссылки миниатюр в новом окне"
141
+
142
+ #: admin/class-admin.php:325
143
+ msgid "Which post status to look for when querying documents."
144
+ msgstr "Какие статусы записей учитывать при поиске документов."
145
+
146
+ #: admin/class-admin.php:338
147
+ msgid "Which post type to look for when querying documents."
148
+ msgstr "Какие типы записей учитывать при поиске документов."
149
+
150
+ #: admin/class-admin.php:342
151
+ msgid "Audio/Video"
152
+ msgstr "Аудио/Видео"
153
+
154
+ #: admin/class-admin.php:350
155
  msgid "Locally generate thumbnails for audio & video files."
156
  msgstr "Локально создавать миниатюры для аудио и видео файлов."
157
 
158
+ #: admin/class-admin.php:363
159
  msgid ""
160
  "Use <a href=\"http://www.ghostscript.com/\" target=\"_blank\">Ghostscript</"
161
  "a> for faster local PDF processing (compared to Imagick)."
164
  "\">Ghostscript</a> для более быстрой локальной обработки PDF (в сравнении с "
165
  "Imagick)."
166
 
167
+ #: admin/class-admin.php:364
168
  msgid ""
169
  "Your server is not configured to run <a href=\"http://www.ghostscript.com/\" "
170
  "target=\"_blank\">Ghostscript</a>."
172
  "Ваш сервер не настроен для использования <a href=\"http://www.ghostscript."
173
  "com/\" target=\"_blank\">Ghostscript</a>."
174
 
175
+ #: admin/class-admin.php:378
176
  msgid ""
177
  "Use <a href=\"http://www.php.net/manual/en/book.imagick.php\" target=\"_blank"
178
  "\">Imagick</a> to handle lots of filetypes locally."
180
  "Использовать <a href=\"http://www.php.net/manual/ru/book.imagick.php\" "
181
  "target=\"_blank\">Imagick</a> для локальной обработки множества типов файлов."
182
 
183
+ #: admin/class-admin.php:379
184
  msgid ""
185
  "Your server is not configured to run <a href=\"http://www.php.net/manual/en/"
186
  "book.imagick.php\" target=\"_blank\">Imagick</a>."
188
  "Ваш сервер не настроен для использования <a href=\"http://www.php.net/manual/"
189
  "ru/book.imagick.php\" target=\"_blank\">Imagick</a>."
190
 
191
+ #: admin/class-admin.php:384
192
+ msgid "Max Thumbnail Dimensions"
193
+ msgstr "Максимальные размеры миниатюр"
 
 
 
 
 
 
 
 
 
 
194
 
195
+ #: admin/class-admin.php:402
196
  msgid "The max width and height (in pixels) that thumbnails will be generated."
197
  msgstr ""
198
  "Максимальная ширина и высота (в пикселях) с которыми будут создаваться "
199
  "миниатюры."
200
 
201
+ #: admin/class-admin.php:432
202
  msgid "Advanced Thumbnail Generation"
203
  msgstr "Создание миниатюр"
204
 
205
+ #: admin/class-admin.php:436
206
+ msgid "Logging Enabled"
207
+ msgstr "Ведение журнала событий"
208
+
209
+ #: admin/class-admin.php:444
210
  msgid "Whether to log debug and error information related to Document Gallery."
211
  msgstr ""
212
  "Вести ли журнал отладки и информации об ошибках имеющих отношение к Document "
213
  "Gallery."
214
 
215
+ #: admin/class-admin.php:448
216
+ msgid "Logging Purge Interval"
217
+ msgstr "Интервал очистки журнала событий"
218
+
219
+ #: admin/class-admin.php:457
220
+ msgid "Number of days to keep old log entries (0 disables purging)."
221
+ msgstr "Количество дней хранения старых записей журнала событий (0 отключает очистку)"
222
+
223
+ #: admin/class-admin.php:461
224
+ msgid "Option Validation"
225
+ msgstr "Проверка правильности настроек"
226
+
227
+ #: admin/class-admin.php:469
228
  msgid ""
229
  "Whether option structure should be validated before save. This is not "
230
  "generally necessary."
232
  "Проверять ли структуру выбранных настроек при сохранении. Как правило в этом "
233
  "нет необходимости."
234
 
235
+ #: admin/class-admin.php:473
236
+ msgid "Thumbnail Generation Timeout"
237
+ msgstr "Время ожидания создания миниатюр"
238
+
239
+ #: admin/class-admin.php:482
240
  msgid ""
241
  "Max number of seconds to wait for thumbnail generation before defaulting to "
242
  "filetype icons."
244
  "Максимальное количество секунд ожидания при создании миниатюры до "
245
  "использования иконки по умолчанию."
246
 
247
+ #: admin/class-admin.php:483
248
  msgid ""
249
  "Note that generation will continue where timeout happened next time the "
250
  "gallery is loaded."
252
  "Обратите внимание, что создание продолжится с того места, где оно было "
253
  "приостановлено, при следующем обращении к галереи."
254
 
255
+ #: admin/class-admin.php:487
256
+ msgid "Ghostscript Absolute Path"
257
+ msgstr "Абсолютный путь к Ghostscript"
258
+
259
+ #: admin/class-admin.php:496
260
  msgid "Successfully auto-detected the location of Ghostscript."
261
  msgstr "Успешно автоматически определено местонахождение Ghostscript."
262
 
263
+ #: admin/class-admin.php:497
264
  msgid "Failed to auto-detect the location of Ghostscript."
265
  msgstr "Не удалось автоматически определить местонахождение Ghostscript."
266
 
267
+ #: admin/class-admin.php:501
268
  msgid "Options Array Dump"
269
  msgstr "Дамп массива установленных настроек"
270
 
271
+ #: admin/class-admin.php:561
272
  msgid "Invalid width given: "
273
  msgstr "Задана неверная ширина: "
274
 
275
+ #: admin/class-admin.php:574
276
  msgid "Invalid height given: "
277
  msgstr "Задана неверная высота: "
278
 
279
+ #: admin/class-admin.php:734
280
+ msgid "File extension doesn't match the MIME type of the image: "
281
+ msgstr "Расширение файла не соответствует MIME-типу изображения: "
282
+
283
+ #: admin/class-admin.php:740
284
+ msgid "Uploaded file size exceeds the allowable limit: "
285
+ msgstr "Размер загруженого файла превышает допустимый предел: "
286
+
287
+ #: admin/class-admin.php:748
288
+ msgid "Uploaded file is not an image: "
289
+ msgstr "Загруженный файл не является изображением: "
290
+
291
+ #: admin/class-admin.php:758
292
+ msgid "Failed to get uploaded file: "
293
+ msgstr "Не удалось получить загруженный файл: "
294
+
295
+ #: admin/class-admin.php:802
296
  msgid "Invalid Ghostscript path given: "
297
  msgstr "Задан неверный путь Ghostscript: "
298
 
299
+ #: admin/class-admin.php:813
300
  msgid "Invalid timeout given: "
301
  msgstr "Задано неверное время ожидания: "
302
 
303
+ #: admin/class-admin.php:828
304
+ msgid "Invalid logging purge interval given: "
305
+ msgstr "Задан неверный период очистки журнала событий: "
306
+
307
+ #: admin/class-admin.php:853
308
  msgid ""
309
  "The following values will be used by default in the shortcode. You can still "
310
  "manually set each of these values in each individual shortcode."
313
  "этом вручную можно установить любые из этих значений в каждом конкретном "
314
  "экземпляре шорткода."
315
 
316
+ #: admin/class-admin.php:860
317
  msgid "Select which tools to use when generating thumbnails."
318
  msgstr "Выберите, какие инструменты использовать для создании миниатюр."
319
 
320
+ #: admin/class-admin.php:869
321
  msgid ""
322
  "Enter custom CSS styling for use with document galleries. To see which ids "
323
  "and classes you can style, take a look at <a href=\"%s\" target=\"_blank"
327
  "Чтобы увидеть, какие селекторы стоит использовать, посмотрите на <a href=\"%s"
328
  "\" target=\"_blank\">style.css</a>."
329
 
330
+ #: admin/class-admin.php:888
331
  msgid ""
332
  "Unless you <em>really</em> know what you're doing, you should not touch "
333
  "these values."
335
  "Не стоит менять этих значений, за исключением случаев когда Вы "
336
  "<em>действительно</em> знаете что делаете."
337
 
338
+ #: admin/class-admin.php:891
339
  msgid ""
340
  "NOTE: <code>exec()</code> is not accessible. Ghostscript will not function."
341
  msgstr ""
342
  "ВНИМАНИЕ: <code>exec()</code> недоступный. Ghostscript не будет работать."
343
 
344
+ #: admin/class-admin.php:902
345
  msgid ""
346
  "The following <em>readonly text</em> should be provided when <a href="
347
  "\"http://wordpress.org/support/plugin/document-gallery\" target=\"_blank"
351
  "предоставлен при <a href=\"http://wordpress.org/support/plugin/document-"
352
  "gallery\" target=\"_blank\">сообщении об ошибке</a>:"
353
 
354
+ #: admin/class-admin.php:1016
355
  msgid "Select All"
356
  msgstr "Выбрать всё"
357
 
358
+ #: admin/class-admin.php:1019
359
  msgid "Thumbnail"
360
  msgstr "Миниатюра"
361
 
362
+ #: admin/class-admin.php:1023
363
  msgid "File name"
364
  msgstr "Имя файла"
365
 
366
+ #: admin/class-admin.php:1024
367
+ msgid "Description"
368
+ msgstr "Описание"
369
+
370
+ #: admin/class-admin.php:1029 admin/class-admin.php:1198
371
  msgid "Date"
372
  msgstr "Дата"
373
 
374
+ #: admin/class-admin.php:1032
375
  msgid "Delete Selected"
376
  msgstr "Удалить выбранные"
377
 
378
+ #: admin/class-admin.php:1034
379
  msgid "item"
380
  msgid_plural "items"
381
  msgstr[0] "запись"
382
  msgstr[1] "записи"
383
  msgstr[2] "записей"
384
 
385
+ #: admin/class-admin.php:1037
386
  msgid "Go to the first page"
387
  msgstr "Перейти к первой странице"
388
 
389
+ #: admin/class-admin.php:1038
390
  msgid "Go to the previous page"
391
  msgstr "Перейти к предыдущей странице"
392
 
393
+ #: admin/class-admin.php:1040
394
  msgid "Current page"
395
  msgstr "Текущая страница"
396
 
397
+ #: admin/class-admin.php:1040
398
  msgid "of"
399
  msgstr "из"
400
 
401
+ #: admin/class-admin.php:1041
402
  msgid "Go to the next page"
403
  msgstr "Перейти к следующей странице"
404
 
405
+ #: admin/class-admin.php:1042
406
  msgid "Go to the last page"
407
  msgstr "Перейти к последней странице"
408
 
409
+ #: admin/class-admin.php:1044
410
  msgid "items per page"
411
  msgstr "записей на странице"
412
 
413
+ #: admin/class-admin.php:1083
414
+ msgid "View '%s' attachment page"
415
+ msgstr "Посмотреть страницу вложения '%s'"
 
416
 
417
+ #: admin/class-admin.php:1083
 
 
 
 
 
418
  msgid "Attachment not found"
419
  msgstr "Вложение не найдено"
420
 
421
+ #: admin/class-admin.php:1112
422
+ msgid "<b>Thumbnail</b> for <i><b>Document Gallery</b></i>"
423
+ msgstr "<b>Миниатюра</b> для <i><b>Document Gallery</b></i>"
424
+
425
+ #: admin/class-admin.php:1199
426
  msgid "Level"
427
  msgstr "Уровень"
428
 
429
+ #: admin/class-admin.php:1200
430
  msgid "Message"
431
  msgstr "Сообщение"
432
 
433
+ #: admin/class-admin.php:1208
434
  msgid "Expand All"
435
  msgstr "Развернуть Все"
436
 
437
+ #: admin/class-admin.php:1211
438
  msgid "Collapse All"
439
  msgstr "Свернуть Все"
440
 
441
+ #: admin/class-admin.php:1263
442
  msgid "Clear Log"
443
  msgstr "Очистить журнал"
444
 
445
+ #: admin/class-admin.php:1270
446
  msgid "There are no log entries at this time."
447
  msgstr "На данный момент журнал пуст."
448
 
449
+ #: admin/class-admin.php:1270
450
  msgid "For Your information:"
451
  msgstr "К Вашему сведению:"
452
 
453
+ #: admin/class-admin.php:1270
454
  msgid "is turned ON"
455
  msgstr "включён"
456
 
457
+ #: admin/class-admin.php:1270
458
  msgid "is turned OFF"
459
  msgstr "выключен"
460
 
461
+ #: admin/media-manager-template.php:9
462
+ msgid "Link To"
463
+ msgstr "Ссылка"
464
+
465
+ #: admin/media-manager-template.php:12
466
+ msgid "Media File"
467
+ msgstr "Медиафайл"
468
+
469
+ #: admin/media-manager-template.php:15
470
+ msgid "Attachment Page"
471
+ msgstr "Страница вложения"
472
+
473
+ #: admin/media-manager-template.php:23
474
+ msgid "Columns"
475
+ msgstr "Колонки"
476
+
477
+ #: inc/class-gallery.php:91
478
  msgid "Generated using Document Gallery. Get yours here: "
479
  msgstr ""
480
  "Сгенерировано используя Document Gallery. Понравилось? Получите Вашу копию "
481
  "здесь: "
482
 
483
+ #: inc/class-gallery.php:93
484
  msgid "No attachments to display. How boring! :("
485
  msgstr "Ни одного вложения для отображения. Скукотища! :("
486
 
487
+ #: inc/class-gallery.php:94
488
+ msgid "The %s value entered, \"%s\", is not valid."
489
+ msgstr "Введенный параметр %s со значением \"%s\" является недопустимым."
490
+
491
+ #: inc/class-gallery.php:95
492
  msgid "The %s parameter may only be \"%s\" or \"%s.\" You entered \"%s.\""
493
  msgstr ""
494
  "Параметр %s может принимать только значения \"%s\" или \"%s.\" Вы же ввели "
495
  "\"%s.\""
496
 
497
+ #: inc/class-gallery.php:226
498
+ msgid "Attempted to call invalid function: "
499
+ msgstr "Попытка вызова недопустимой функции: "
 
 
 
500
 
501
+ #: inc/class-gallery.php:349
502
+ msgid "%s may only be a comma-delimited list of integers."
503
+ msgstr "Параметр %s может быть только списком целых чисел разделенных запятыми."
504
 
505
+ #: inc/class-gallery.php:750
506
  msgid "%s is not a valid term name in %s."
507
  msgstr "Определение %s не является допустимым значением параметра %s."
508
 
509
+ #: inc/class-image-editor-imagick.php:38
510
  msgid "Failed to set Imagick page number"
511
  msgstr "Не удалось установить номер страницы в Imagick"
512
 
513
+ #: inc/class-thumber.php:95
514
  msgid "Attempting to generate thumbnail for attachment #%d with (%s)"
515
  msgstr "Попытка создания миниатюры для вложения №%d с помощью (%s)"
516
 
517
+ #: inc/class-thumber.php:168
518
  msgid "Could not open file: "
519
  msgstr "Не удалось открыть файл: "
520
 
521
+ #: inc/class-thumber.php:174
522
  msgid "Could not write file: "
523
  msgstr "Не удалось записать файл: "
524
 
525
+ #: inc/class-thumber.php:213
526
  msgid "Failed to open file in Imagick: "
527
  msgstr "Не удалось открыть файл в Imagick: "
528
 
529
+ #: inc/class-thumber.php:225
530
  msgid "Failed to save image in Imagick: "
531
  msgstr "Не удалось сохранить изображение в Imagick: "
532
 
533
+ #: inc/class-thumber.php:285
534
  msgid "Ghostscript failed: "
535
  msgstr "Сбой в Ghostscript: "
536
 
537
+ #: inc/class-thumber.php:577
 
 
 
 
538
  msgid "Thumbnail Generators: "
539
  msgstr "Генераторы миниатюр: "
540
 
541
+ #: inc/class-thumber.php:588
542
  msgid "No thumbnail generators enabled."
543
  msgstr "Отсутствуют включенные генераторы миниатюр."
544
 
545
+ #: inc/class-thumber.php:638
546
  msgid "Failed to get image editor: "
547
  msgstr ""
548
  "Сбой при попытке использовании графического редактора (WP_Image_Editor): "
549
 
550
+ #: inc/class-thumber.php:650
551
  msgid "Failed to save image: "
552
  msgstr "Не удалось сохранить изображение: "
553
 
languages/document-gallery-uk.mo ADDED
Binary file
languages/{document-gallery-uk_UA.po → document-gallery-uk.po} RENAMED
@@ -1,14 +1,14 @@
1
- # Copyright (C) 2014 Document Gallery
2
  # This file is distributed under the same license as the Document Gallery package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Document Gallery 2.2.7\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/tag/document-gallery\n"
7
- "POT-Creation-Date: 2014-09-04 02:15:50+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
- "PO-Revision-Date: 2014-12-14 13:33-0600\n"
12
  "Last-Translator: Dan Rossiter <dan.rossiters@gmail.com>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
  "X-Generator: Poedit 1.6.9\n"
@@ -16,76 +16,106 @@ msgstr ""
16
  "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
17
  "Language: uk_UA\n"
18
 
19
- #: admin/class-admin.php:28
20
  msgid "General"
21
  msgstr "Загальні"
22
 
23
- #: admin/class-admin.php:29
24
  msgid "Thumbnail Management"
25
  msgstr "Керування мініатюрами"
26
 
27
- #: admin/class-admin.php:30 admin/class-admin.php:898
28
  msgid "Logging"
29
  msgstr "Журнал подій"
30
 
31
- #: admin/class-admin.php:31
32
  msgid "Advanced"
33
  msgstr "Для досвідчених користувачів"
34
 
35
- #: admin/class-admin.php:68
 
 
 
 
 
36
  msgid "Settings"
37
  msgstr "Параметри"
38
 
39
- #: admin/class-admin.php:78
40
- msgid "Document Gallery Settings"
41
- msgstr "Параметри Document Gallery"
42
 
 
43
  #. Plugin Name of the plugin/theme
44
- #: admin/class-admin.php:79
45
  msgid "Document Gallery"
46
  msgstr "Document Gallery"
47
 
48
- #: admin/class-admin.php:124
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  msgid "Default Settings"
50
  msgstr "Типові параметри"
51
 
52
- #: admin/class-admin.php:128
53
  msgid "Thumbnail Generation"
54
  msgstr "Утворення мініатюр"
55
 
56
- #: admin/class-admin.php:132
57
  msgid "Custom CSS"
58
  msgstr "Особисті CSS"
59
 
60
- #: admin/class-admin.php:144
61
  msgid "Link to attachment page rather than to file"
62
  msgstr "Посилання на сторінку прикріпленого до запису файла, а не на сам файл"
63
 
64
- #: admin/class-admin.php:156
 
 
 
 
65
  msgid "Include document descriptions"
66
  msgstr "Додавати описи документів"
67
 
68
- #: admin/class-admin.php:168
69
  msgid "Use auto-generated document thumbnails"
70
  msgstr "Використовувати автоматично згенеровані мініатюри документів"
71
 
72
- #: admin/class-admin.php:180
73
- msgid "Include image attachments in gallery"
74
- msgstr "Включати прикріплені до посту зображення в галерею"
75
-
76
- #: admin/class-admin.php:192
77
- msgid "Only look for attachments in post where [dg] is used"
78
- msgstr "Шукати прикріплені файли лише у записах, де використовується [dg]"
79
-
80
- #: admin/class-admin.php:205
81
  msgid "Ascending or descending sorting of documents"
82
  msgstr "Сортування документів за зростанням або за спаданням"
83
 
84
- #: admin/class-admin.php:218
85
  msgid "Which field to order documents by"
86
  msgstr "За яким полем сортувати документи"
87
 
88
- #: admin/class-admin.php:231
89
  msgid ""
90
  "Whether matched documents must have all taxa_names (AND) or at least one (OR)"
91
  msgstr ""
@@ -93,11 +123,39 @@ msgstr ""
93
  "параметрі category, також можливе використання taxon_name, або хоча б однієї "
94
  "(OR)"
95
 
96
- #: admin/class-admin.php:243
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  msgid "Locally generate thumbnails for audio & video files."
98
  msgstr "Локально утворювати мініатюри для аудіо та відео файлів."
99
 
100
- #: admin/class-admin.php:256
101
  msgid ""
102
  "Use <a href=\"http://www.ghostscript.com/\" target=\"_blank\">Ghostscript</"
103
  "a> for faster local PDF processing (compared to Imagick)."
@@ -106,7 +164,7 @@ msgstr ""
106
  "\">Ghostscript</a> для більш швидкої локальної обробки PDF (порівняно з "
107
  "Imagick)."
108
 
109
- #: admin/class-admin.php:257
110
  msgid ""
111
  "Your server is not configured to run <a href=\"http://www.ghostscript.com/\" "
112
  "target=\"_blank\">Ghostscript</a>."
@@ -114,7 +172,7 @@ msgstr ""
114
  "Ваш сервер не налаштований для використання <a href=\"http://www.ghostscript."
115
  "com/\" target=\"_blank\">Ghostscript</a>."
116
 
117
- #: admin/class-admin.php:271
118
  msgid ""
119
  "Use <a href=\"http://www.php.net/manual/en/book.imagick.php\" target=\"_blank"
120
  "\">Imagick</a> to handle lots of filetypes locally."
@@ -123,7 +181,7 @@ msgstr ""
123
  "target=\"_blank\">Imagick</a> для локальної обробки великої кількості типів "
124
  "файлів."
125
 
126
- #: admin/class-admin.php:272
127
  msgid ""
128
  "Your server is not configured to run <a href=\"http://www.php.net/manual/en/"
129
  "book.imagick.php\" target=\"_blank\">Imagick</a>."
@@ -131,37 +189,43 @@ msgstr ""
131
  "Ваш сервер не налаштований для використання <a href=\"http://www.php.net/"
132
  "manual/en/book.imagick.php\" target=\"_blank\">Imagick</a>."
133
 
134
- #: admin/class-admin.php:286
135
- msgid ""
136
- "Use <a href=\"https://drive.google.com/viewer\" target=\"_blank\">Google "
137
- "Drive Viewer</a> to generate thumbnails for MS Office files and many other "
138
- "file types remotely."
139
- msgstr ""
140
- "Використовувати <a href=\"https://drive.google.com/viewer\" target=\"_blank"
141
- "\">Google Drive Viewer</a> для утворення мініатюр файлів MS Office та "
142
- "багатьох інших типів файлів віддалено."
143
-
144
- #: admin/class-admin.php:287
145
- msgid "Your server does not allow remote HTTP access."
146
- msgstr "Ваш сервер не дозволяє віддалені HTTP запити."
147
 
148
- #: admin/class-admin.php:309
149
  msgid "The max width and height (in pixels) that thumbnails will be generated."
150
  msgstr ""
151
  "Максимальна ширина і висота (у пікселях) з якими будуть утворюватися "
152
  "мініатюри."
153
 
154
- #: admin/class-admin.php:338
155
  msgid "Advanced Thumbnail Generation"
156
  msgstr "Утворення мініатюр"
157
 
158
- #: admin/class-admin.php:350
 
 
 
 
159
  msgid "Whether to log debug and error information related to Document Gallery."
160
  msgstr ""
161
  "Чи вести журнал налагоджування та інформації щодо помилок які мають "
162
  "відношення до Document Gallery."
163
 
164
- #: admin/class-admin.php:362
 
 
 
 
 
 
 
 
 
 
 
 
165
  msgid ""
166
  "Whether option structure should be validated before save. This is not "
167
  "generally necessary."
@@ -169,7 +233,11 @@ msgstr ""
169
  "Чи перевіряти структуру обраних налаштувань при збереженні. Як правило в "
170
  "цьому нема потреби."
171
 
172
- #: admin/class-admin.php:375
 
 
 
 
173
  msgid ""
174
  "Max number of seconds to wait for thumbnail generation before defaulting to "
175
  "filetype icons."
@@ -177,7 +245,7 @@ msgstr ""
177
  "Максимальна кількість секунд очікування при утворенні мініатюри до "
178
  "використання іконки за замовчуванням."
179
 
180
- #: admin/class-admin.php:376
181
  msgid ""
182
  "Note that generation will continue where timeout happened next time the "
183
  "gallery is loaded."
@@ -185,35 +253,59 @@ msgstr ""
185
  "Зверніть увагу, що утворення продовжиться з того місця, де воно було "
186
  "призупинене, при наступному зверненні до галереї."
187
 
188
- #: admin/class-admin.php:388
 
 
 
 
189
  msgid "Successfully auto-detected the location of Ghostscript."
190
  msgstr "Успішно автоматично визначено місцезнаходження Ghostscript."
191
 
192
- #: admin/class-admin.php:389
193
  msgid "Failed to auto-detect the location of Ghostscript."
194
  msgstr "Не вдалося автоматично визначити місцезнаходження Ghostscript."
195
 
196
- #: admin/class-admin.php:393
197
  msgid "Options Array Dump"
198
  msgstr "Дамп масиву обраних налаштувань"
199
 
200
- #: admin/class-admin.php:440
201
  msgid "Invalid width given: "
202
  msgstr "Задана невірна ширина: "
203
 
204
- #: admin/class-admin.php:453
205
  msgid "Invalid height given: "
206
  msgstr "Задана невірна висота: "
207
 
208
- #: admin/class-admin.php:566
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  msgid "Invalid Ghostscript path given: "
210
  msgstr "Задано невірний шлях Ghostscript: "
211
 
212
- #: admin/class-admin.php:577
213
  msgid "Invalid timeout given: "
214
  msgstr "Задано невірний час очікування: "
215
 
216
- #: admin/class-admin.php:608
 
 
 
 
217
  msgid ""
218
  "The following values will be used by default in the shortcode. You can still "
219
  "manually set each of these values in each individual shortcode."
@@ -222,11 +314,11 @@ msgstr ""
222
  "цьому вручну можна встановити будь-які з цих значень в кожному конкретному "
223
  "екземплярі шорткоду."
224
 
225
- #: admin/class-admin.php:615
226
  msgid "Select which tools to use when generating thumbnails."
227
  msgstr "Оберіть, які інструменти використовувати для утворення мініатюр."
228
 
229
- #: admin/class-admin.php:624
230
  msgid ""
231
  "Enter custom CSS styling for use with document galleries. To see which ids "
232
  "and classes you can style, take a look at <a href=\"%s\" target=\"_blank"
@@ -236,7 +328,7 @@ msgstr ""
236
  "побачити, які селектори варто використовувати, зверніть увагу на <a href=\"%s"
237
  "\" target=\"_blank\">style.css</a>."
238
 
239
- #: admin/class-admin.php:642
240
  msgid ""
241
  "Unless you <em>really</em> know what you're doing, you should not touch "
242
  "these values."
@@ -244,12 +336,12 @@ msgstr ""
244
  "Не варто міняти цих значень, за винятком випадків коли Ви <em>насправді</em> "
245
  "знаєте що робите."
246
 
247
- #: admin/class-admin.php:645
248
  msgid ""
249
  "NOTE: <code>exec()</code> is not accessible. Ghostscript will not function."
250
  msgstr "УВАГА: <code>exec()</code> недосяжний. Ghostscript не працюватиме."
251
 
252
- #: admin/class-admin.php:656
253
  msgid ""
254
  "The following <em>readonly text</em> should be provided when <a href="
255
  "\"http://wordpress.org/support/plugin/document-gallery\" target=\"_blank"
@@ -259,189 +351,203 @@ msgstr ""
259
  "при <a href=\"http://wordpress.org/support/plugin/document-gallery\" target="
260
  "\"_blank\">повідомленні про помилку</a>:"
261
 
262
- #: admin/class-admin.php:757
263
  msgid "Select All"
264
  msgstr "Вибрати всі"
265
 
266
- #: admin/class-admin.php:760
267
  msgid "Thumbnail"
268
  msgstr "Мініатюра"
269
 
270
- #: admin/class-admin.php:761
271
  msgid "File name"
272
  msgstr "Назва файла"
273
 
274
- #: admin/class-admin.php:762 admin/class-admin.php:834
 
 
 
 
275
  msgid "Date"
276
  msgstr "Дата"
277
 
278
- #: admin/class-admin.php:765
279
  msgid "Delete Selected"
280
  msgstr "Вилучити вибрані"
281
 
282
- #: admin/class-admin.php:767
283
  msgid "item"
284
  msgid_plural "items"
285
  msgstr[0] "елемент"
286
  msgstr[1] "елемента"
287
  msgstr[2] "елементів"
288
 
289
- #: admin/class-admin.php:770
290
  msgid "Go to the first page"
291
  msgstr "Перейти до першої сторінки"
292
 
293
- #: admin/class-admin.php:771
294
  msgid "Go to the previous page"
295
  msgstr "Перейти до попередньої сторінки"
296
 
297
- #: admin/class-admin.php:773
298
  msgid "Current page"
299
  msgstr "Поточна сторінка"
300
 
301
- #: admin/class-admin.php:773
302
  msgid "of"
303
  msgstr "з"
304
 
305
- #: admin/class-admin.php:774
306
  msgid "Go to the next page"
307
  msgstr "Перейти до наступної сторінки"
308
 
309
- #: admin/class-admin.php:775
310
  msgid "Go to the last page"
311
  msgstr "Перейти до останньої сторінки"
312
 
313
- #: admin/class-admin.php:777
314
  msgid "items per page"
315
  msgstr "елементів на сторінці"
316
 
317
- # Due to language structure translation of "View" and "attachment page" strings were merged into one
318
- #: admin/class-admin.php:815
319
- msgid "View"
320
- msgstr "Переглянути сторінку вкладення"
321
 
322
- # Due to language structure translation of "View" and "attachment page" strings were merged into one
323
- #: admin/class-admin.php:816
324
- msgid "attachment page"
325
- msgstr ""
326
-
327
- #: admin/class-admin.php:816
328
  msgid "Attachment not found"
329
  msgstr "Вкладення не знайдено"
330
 
331
- #: admin/class-admin.php:835
 
 
 
 
332
  msgid "Level"
333
  msgstr "Рівень"
334
 
335
- #: admin/class-admin.php:836
336
  msgid "Message"
337
  msgstr "Повідомлення"
338
 
339
- #: admin/class-admin.php:845
340
  msgid "Expand All"
341
  msgstr "Розгорнути Все"
342
 
343
- #: admin/class-admin.php:848
344
  msgid "Collapse All"
345
  msgstr "Згорнути Все"
346
 
347
- #: admin/class-admin.php:891
348
  msgid "Clear Log"
349
  msgstr "Очистити журнал"
350
 
351
- #: admin/class-admin.php:898
352
  msgid "There are no log entries at this time."
353
  msgstr "На цю мить журнал порожній."
354
 
355
- #: admin/class-admin.php:898
356
  msgid "For Your information:"
357
  msgstr "До Вашого відома:"
358
 
359
- #: admin/class-admin.php:898
360
  msgid "is turned ON"
361
  msgstr "увімкнуто"
362
 
363
- #: admin/class-admin.php:898
364
  msgid "is turned OFF"
365
  msgstr "вимкнуто"
366
 
367
- #: inc/class-gallery.php:86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
368
  msgid "Generated using Document Gallery. Get yours here: "
369
  msgstr ""
370
  "Згенеровано використовуючи Document Gallery. Сподобалось? Отримайте Вашу "
371
  "копію тут: "
372
 
373
- #: inc/class-gallery.php:88
374
  msgid "No attachments to display. How boring! :("
375
  msgstr "Жодного вкладення для відображення. Нудьга! :("
376
 
377
- #: inc/class-gallery.php:89
378
- #, c-format
 
 
 
379
  msgid "The %s parameter may only be \"%s\" or \"%s.\" You entered \"%s.\""
380
  msgstr ""
381
  "Параметр %s може приймати тільки значення \"%s\" або \"%s.\" Ви ж ввели \"%s."
382
  "\""
383
 
384
- #: inc/class-gallery.php:286
385
- msgid "The following ID is invalid: "
386
- msgid_plural "The following IDs are invalid: "
387
- msgstr[0] "Наступний ідентифікатор (ID) недійсний: "
388
- msgstr[1] "Наступні ідентифікатори (IDs) недійсні: "
389
- msgstr[2] "Наступні ідентифікатори (IDs) недійсні: "
390
 
391
- #: inc/class-gallery.php:379
392
- msgid "The orderby value entered, \"%s,\" is not valid."
393
- msgstr "Задане значення параметра orderby, \"%s,\" неприпустиме."
394
 
395
- #: inc/class-gallery.php:533
396
  msgid "%s is not a valid term name in %s."
397
- msgstr "%s не є припустимим значенням параметра %s."
398
 
399
- #: inc/class-image-editor-imagick.php:37
400
  msgid "Failed to set Imagick page number"
401
  msgstr "Не вдалося встановити номер сторінки в Imagick"
402
 
403
- #: inc/class-thumber.php:59
404
  msgid "Attempting to generate thumbnail for attachment #%d with (%s)"
405
  msgstr "Спроба утворення мініатюри для вкладення №%d за допомоги (%s)"
406
 
407
- #: inc/class-thumber.php:138
408
  msgid "Could not open file: "
409
  msgstr "Не вдалося відкрити файл: "
410
 
411
- #: inc/class-thumber.php:143
412
  msgid "Could not write file: "
413
  msgstr "Не вдалося записати файл: "
414
 
415
- #: inc/class-thumber.php:180
416
  msgid "Failed to open file in Imagick: "
417
  msgstr "Не вдалося відкрити файл в Imagick: "
418
 
419
- #: inc/class-thumber.php:191
420
  msgid "Failed to save image in Imagick: "
421
  msgstr "Не вдалося зберегти зображення в Imagick: "
422
 
423
- #: inc/class-thumber.php:249
424
  msgid "Ghostscript failed: "
425
  msgstr "Ghostscript вийшов із ладу: "
426
 
427
- #: inc/class-thumber.php:387
428
- msgid "Failed to retrieve thumbnail from Google: "
429
- msgstr "Не вдалося отримати від Google мініатюру: "
430
-
431
- #: inc/class-thumber.php:631
432
  msgid "Thumbnail Generators: "
433
  msgstr "Генератори мініатюр: "
434
 
435
- #: inc/class-thumber.php:642
436
  msgid "No thumbnail generators enabled."
437
  msgstr "Відсутні підключені генератори мініатюр."
438
 
439
- #: inc/class-thumber.php:685
440
  msgid "Failed to get image editor: "
441
  msgstr ""
442
  "Невдача при спробі використання графічного редактора (WP_Image_Editor): "
443
 
444
- #: inc/class-thumber.php:697
445
  msgid "Failed to save image: "
446
  msgstr "Не вдалося зберегти зображення: "
447
 
1
+ # Copyright (C) 2015 Document Gallery
2
  # This file is distributed under the same license as the Document Gallery package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Document Gallery 3.3\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/document-gallery\n"
7
+ "POT-Creation-Date: 2015-06-20 17:03:17+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
11
+ "PO-Revision-Date: 2015-06-21 12:10-0600\n"
12
  "Last-Translator: Dan Rossiter <dan.rossiters@gmail.com>\n"
13
  "Language-Team: LANGUAGE <LL@li.org>\n"
14
  "X-Generator: Poedit 1.6.9\n"
16
  "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
17
  "Language: uk_UA\n"
18
 
19
+ #: admin/class-admin.php:30
20
  msgid "General"
21
  msgstr "Загальні"
22
 
23
+ #: admin/class-admin.php:31
24
  msgid "Thumbnail Management"
25
  msgstr "Керування мініатюрами"
26
 
27
+ #: admin/class-admin.php:32 admin/class-admin.php:1270
28
  msgid "Logging"
29
  msgstr "Журнал подій"
30
 
31
+ #: admin/class-admin.php:33
32
  msgid "Advanced"
33
  msgstr "Для досвідчених користувачів"
34
 
35
+ #: admin/class-admin.php:45 admin/class-admin.php:99
36
+ #: admin/media-manager-template.php:5
37
+ msgid "Document Gallery Settings"
38
+ msgstr "Параметри Document Gallery"
39
+
40
+ #: admin/class-admin.php:73
41
  msgid "Settings"
42
  msgstr "Параметри"
43
 
44
+ #: admin/class-admin.php:87
45
+ msgid "Donate"
46
+ msgstr "Підтримати"
47
 
48
+ #. #-#-#-#-# plugin.pot (Document Gallery 3.3) #-#-#-#-#
49
  #. Plugin Name of the plugin/theme
50
+ #: admin/class-admin.php:100
51
  msgid "Document Gallery"
52
  msgstr "Document Gallery"
53
 
54
+ #: admin/class-admin.php:127
55
+ msgid "Create Document Gallery"
56
+ msgstr "Створити галерею Document Gallery"
57
+
58
+ #: admin/class-admin.php:128
59
+ msgid "Create a new Document Gallery"
60
+ msgstr "Створити нову галерею Document Gallery"
61
+
62
+ #: admin/class-admin.php:129
63
+ msgid "Cancel Document Gallery"
64
+ msgstr "Скасувати галерею Document Gallery"
65
+
66
+ #: admin/class-admin.php:130
67
+ msgid "Update Document Gallery"
68
+ msgstr "Оновити галерею Document Gallery"
69
+
70
+ #: admin/class-admin.php:131
71
+ msgid "Insert Document Gallery"
72
+ msgstr "Вставити галерею Document Gallery"
73
+
74
+ #: admin/class-admin.php:132 admin/class-admin.php:133
75
+ msgid "Add to Document Gallery"
76
+ msgstr "Додати в галерею Document Gallery"
77
+
78
+ #: admin/class-admin.php:134
79
+ msgid "Edit Document Gallery"
80
+ msgstr "Редагувати галерею Document Gallery"
81
+
82
+ #: admin/class-admin.php:178
83
  msgid "Default Settings"
84
  msgstr "Типові параметри"
85
 
86
+ #: admin/class-admin.php:182
87
  msgid "Thumbnail Generation"
88
  msgstr "Утворення мініатюр"
89
 
90
+ #: admin/class-admin.php:186
91
  msgid "Custom CSS"
92
  msgstr "Особисті CSS"
93
 
94
+ #: admin/class-admin.php:198
95
  msgid "Link to attachment page rather than to file"
96
  msgstr "Посилання на сторінку прикріпленого до запису файла, а не на сам файл"
97
 
98
+ #: admin/class-admin.php:211
99
+ msgid "The number of columns to display when not rendering descriptions."
100
+ msgstr "Кількість стовпчиків, що відображаються при вимкнених описах."
101
+
102
+ #: admin/class-admin.php:223 admin/media-manager-template.php:50
103
  msgid "Include document descriptions"
104
  msgstr "Додавати описи документів"
105
 
106
+ #: admin/class-admin.php:235 admin/media-manager-template.php:57
107
  msgid "Use auto-generated document thumbnails"
108
  msgstr "Використовувати автоматично згенеровані мініатюри документів"
109
 
110
+ #: admin/class-admin.php:248 admin/media-manager-template.php:79
 
 
 
 
 
 
 
 
111
  msgid "Ascending or descending sorting of documents"
112
  msgstr "Сортування документів за зростанням або за спаданням"
113
 
114
+ #: admin/class-admin.php:261 admin/media-manager-template.php:64
115
  msgid "Which field to order documents by"
116
  msgstr "За яким полем сортувати документи"
117
 
118
+ #: admin/class-admin.php:274
119
  msgid ""
120
  "Whether matched documents must have all taxa_names (AND) or at least one (OR)"
121
  msgstr ""
123
  "параметрі category, також можливе використання taxon_name, або хоча б однієї "
124
  "(OR)"
125
 
126
+ #: admin/class-admin.php:287
127
+ msgid "Limit the number of documents included. -1 means no limit."
128
+ msgstr "Максимальна кількість документів що включаються. -1 означає без обмежень."
129
+
130
+ #: admin/class-admin.php:300
131
+ msgid ""
132
+ "Comma-delimited list of <a href=\"http://en.wikipedia.org/wiki/"
133
+ "Internet_media_type#List_of_common_media_types\">MIME types</a>."
134
+ msgstr ""
135
+ "Перелік <a href=\"http://en.wikipedia.org/wiki/Internet_media_type"
136
+ "#List_of_common_media_types\">MIME-типів</a> розділених комами."
137
+
138
+ #: admin/class-admin.php:312 admin/media-manager-template.php:43
139
+ msgid "Open thumbnail links in new window"
140
+ msgstr "Відкривати посилання мініатюр у новому вікні"
141
+
142
+ #: admin/class-admin.php:325
143
+ msgid "Which post status to look for when querying documents."
144
+ msgstr "Які статуси записів враховувати під час пошуку документів."
145
+
146
+ #: admin/class-admin.php:338
147
+ msgid "Which post type to look for when querying documents."
148
+ msgstr "Які типи записів враховувати під час пошуку документів."
149
+
150
+ #: admin/class-admin.php:342
151
+ msgid "Audio/Video"
152
+ msgstr "Аудіо/Відео"
153
+
154
+ #: admin/class-admin.php:350
155
  msgid "Locally generate thumbnails for audio & video files."
156
  msgstr "Локально утворювати мініатюри для аудіо та відео файлів."
157
 
158
+ #: admin/class-admin.php:363
159
  msgid ""
160
  "Use <a href=\"http://www.ghostscript.com/\" target=\"_blank\">Ghostscript</"
161
  "a> for faster local PDF processing (compared to Imagick)."
164
  "\">Ghostscript</a> для більш швидкої локальної обробки PDF (порівняно з "
165
  "Imagick)."
166
 
167
+ #: admin/class-admin.php:364
168
  msgid ""
169
  "Your server is not configured to run <a href=\"http://www.ghostscript.com/\" "
170
  "target=\"_blank\">Ghostscript</a>."
172
  "Ваш сервер не налаштований для використання <a href=\"http://www.ghostscript."
173
  "com/\" target=\"_blank\">Ghostscript</a>."
174
 
175
+ #: admin/class-admin.php:378
176
  msgid ""
177
  "Use <a href=\"http://www.php.net/manual/en/book.imagick.php\" target=\"_blank"
178
  "\">Imagick</a> to handle lots of filetypes locally."
181
  "target=\"_blank\">Imagick</a> для локальної обробки великої кількості типів "
182
  "файлів."
183
 
184
+ #: admin/class-admin.php:379
185
  msgid ""
186
  "Your server is not configured to run <a href=\"http://www.php.net/manual/en/"
187
  "book.imagick.php\" target=\"_blank\">Imagick</a>."
189
  "Ваш сервер не налаштований для використання <a href=\"http://www.php.net/"
190
  "manual/en/book.imagick.php\" target=\"_blank\">Imagick</a>."
191
 
192
+ #: admin/class-admin.php:384
193
+ msgid "Max Thumbnail Dimensions"
194
+ msgstr "Максимальні розміри мініатюр"
 
 
 
 
 
 
 
 
 
 
195
 
196
+ #: admin/class-admin.php:402
197
  msgid "The max width and height (in pixels) that thumbnails will be generated."
198
  msgstr ""
199
  "Максимальна ширина і висота (у пікселях) з якими будуть утворюватися "
200
  "мініатюри."
201
 
202
+ #: admin/class-admin.php:432
203
  msgid "Advanced Thumbnail Generation"
204
  msgstr "Утворення мініатюр"
205
 
206
+ #: admin/class-admin.php:436
207
+ msgid "Logging Enabled"
208
+ msgstr "Ведення журналу подій"
209
+
210
+ #: admin/class-admin.php:444
211
  msgid "Whether to log debug and error information related to Document Gallery."
212
  msgstr ""
213
  "Чи вести журнал налагоджування та інформації щодо помилок які мають "
214
  "відношення до Document Gallery."
215
 
216
+ #: admin/class-admin.php:448
217
+ msgid "Logging Purge Interval"
218
+ msgstr "Інтервал очищення журналу подій"
219
+
220
+ #: admin/class-admin.php:457
221
+ msgid "Number of days to keep old log entries (0 disables purging)."
222
+ msgstr "Кількість діб зберігання старих записів журналу подій (0 відключити очистку)"
223
+
224
+ #: admin/class-admin.php:461
225
+ msgid "Option Validation"
226
+ msgstr "Перевірка правильності налаштувань"
227
+
228
+ #: admin/class-admin.php:469
229
  msgid ""
230
  "Whether option structure should be validated before save. This is not "
231
  "generally necessary."
233
  "Чи перевіряти структуру обраних налаштувань при збереженні. Як правило в "
234
  "цьому нема потреби."
235
 
236
+ #: admin/class-admin.php:473
237
+ msgid "Thumbnail Generation Timeout"
238
+ msgstr "Час очікування створення мініатюр"
239
+
240
+ #: admin/class-admin.php:482
241
  msgid ""
242
  "Max number of seconds to wait for thumbnail generation before defaulting to "
243
  "filetype icons."
245
  "Максимальна кількість секунд очікування при утворенні мініатюри до "
246
  "використання іконки за замовчуванням."
247
 
248
+ #: admin/class-admin.php:483
249
  msgid ""
250
  "Note that generation will continue where timeout happened next time the "
251
  "gallery is loaded."
253
  "Зверніть увагу, що утворення продовжиться з того місця, де воно було "
254
  "призупинене, при наступному зверненні до галереї."
255
 
256
+ #: admin/class-admin.php:487
257
+ msgid "Ghostscript Absolute Path"
258
+ msgstr "Повний шлях до Ghostscript"
259
+
260
+ #: admin/class-admin.php:496
261
  msgid "Successfully auto-detected the location of Ghostscript."
262
  msgstr "Успішно автоматично визначено місцезнаходження Ghostscript."
263
 
264
+ #: admin/class-admin.php:497
265
  msgid "Failed to auto-detect the location of Ghostscript."
266
  msgstr "Не вдалося автоматично визначити місцезнаходження Ghostscript."
267
 
268
+ #: admin/class-admin.php:501
269
  msgid "Options Array Dump"
270
  msgstr "Дамп масиву обраних налаштувань"
271
 
272
+ #: admin/class-admin.php:561
273
  msgid "Invalid width given: "
274
  msgstr "Задана невірна ширина: "
275
 
276
+ #: admin/class-admin.php:574
277
  msgid "Invalid height given: "
278
  msgstr "Задана невірна висота: "
279
 
280
+ #: admin/class-admin.php:734
281
+ msgid "File extension doesn't match the MIME type of the image: "
282
+ msgstr "Розширення файлу не відповідає MIME-типу зображення: "
283
+
284
+ #: admin/class-admin.php:740
285
+ msgid "Uploaded file size exceeds the allowable limit: "
286
+ msgstr "Розмір завантаженого файлу перевищує допустиму межу: "
287
+
288
+ #: admin/class-admin.php:748
289
+ msgid "Uploaded file is not an image: "
290
+ msgstr "Завантаженний файл не є зображенням: "
291
+
292
+ #: admin/class-admin.php:758
293
+ msgid "Failed to get uploaded file: "
294
+ msgstr "Не вдалося отримати завантажений файл: "
295
+
296
+ #: admin/class-admin.php:802
297
  msgid "Invalid Ghostscript path given: "
298
  msgstr "Задано невірний шлях Ghostscript: "
299
 
300
+ #: admin/class-admin.php:813
301
  msgid "Invalid timeout given: "
302
  msgstr "Задано невірний час очікування: "
303
 
304
+ #: admin/class-admin.php:828
305
+ msgid "Invalid logging purge interval given: "
306
+ msgstr "Задано невірний інтервал очищення журналу подій: "
307
+
308
+ #: admin/class-admin.php:853
309
  msgid ""
310
  "The following values will be used by default in the shortcode. You can still "
311
  "manually set each of these values in each individual shortcode."
314
  "цьому вручну можна встановити будь-які з цих значень в кожному конкретному "
315
  "екземплярі шорткоду."
316
 
317
+ #: admin/class-admin.php:860
318
  msgid "Select which tools to use when generating thumbnails."
319
  msgstr "Оберіть, які інструменти використовувати для утворення мініатюр."
320
 
321
+ #: admin/class-admin.php:869
322
  msgid ""
323
  "Enter custom CSS styling for use with document galleries. To see which ids "
324
  "and classes you can style, take a look at <a href=\"%s\" target=\"_blank"
328
  "побачити, які селектори варто використовувати, зверніть увагу на <a href=\"%s"
329
  "\" target=\"_blank\">style.css</a>."
330
 
331
+ #: admin/class-admin.php:888
332
  msgid ""
333
  "Unless you <em>really</em> know what you're doing, you should not touch "
334
  "these values."
336
  "Не варто міняти цих значень, за винятком випадків коли Ви <em>насправді</em> "
337
  "знаєте що робите."
338
 
339
+ #: admin/class-admin.php:891
340
  msgid ""
341
  "NOTE: <code>exec()</code> is not accessible. Ghostscript will not function."
342
  msgstr "УВАГА: <code>exec()</code> недосяжний. Ghostscript не працюватиме."
343
 
344
+ #: admin/class-admin.php:902
345
  msgid ""
346
  "The following <em>readonly text</em> should be provided when <a href="
347
  "\"http://wordpress.org/support/plugin/document-gallery\" target=\"_blank"
351
  "при <a href=\"http://wordpress.org/support/plugin/document-gallery\" target="
352
  "\"_blank\">повідомленні про помилку</a>:"
353
 
354
+ #: admin/class-admin.php:1016
355
  msgid "Select All"
356
  msgstr "Вибрати всі"
357
 
358
+ #: admin/class-admin.php:1019
359
  msgid "Thumbnail"
360
  msgstr "Мініатюра"
361
 
362
+ #: admin/class-admin.php:1023
363
  msgid "File name"
364
  msgstr "Назва файла"
365
 
366
+ #: admin/class-admin.php:1024
367
+ msgid "Description"
368
+ msgstr "Опис"
369
+
370
+ #: admin/class-admin.php:1029 admin/class-admin.php:1198
371
  msgid "Date"
372
  msgstr "Дата"
373
 
374
+ #: admin/class-admin.php:1032
375
  msgid "Delete Selected"
376
  msgstr "Вилучити вибрані"
377
 
378
+ #: admin/class-admin.php:1034
379
  msgid "item"
380
  msgid_plural "items"
381
  msgstr[0] "елемент"
382
  msgstr[1] "елемента"
383
  msgstr[2] "елементів"
384
 
385
+ #: admin/class-admin.php:1037
386
  msgid "Go to the first page"
387
  msgstr "Перейти до першої сторінки"
388
 
389
+ #: admin/class-admin.php:1038
390
  msgid "Go to the previous page"
391
  msgstr "Перейти до попередньої сторінки"
392
 
393
+ #: admin/class-admin.php:1040
394
  msgid "Current page"
395
  msgstr "Поточна сторінка"
396
 
397
+ #: admin/class-admin.php:1040
398
  msgid "of"
399
  msgstr "з"
400
 
401
+ #: admin/class-admin.php:1041
402
  msgid "Go to the next page"
403
  msgstr "Перейти до наступної сторінки"
404
 
405
+ #: admin/class-admin.php:1042
406
  msgid "Go to the last page"
407
  msgstr "Перейти до останньої сторінки"
408
 
409
+ #: admin/class-admin.php:1044
410
  msgid "items per page"
411
  msgstr "елементів на сторінці"
412
 
413
+ #: admin/class-admin.php:1083
414
+ msgid "View '%s' attachment page"
415
+ msgstr "Переглянути сторінку вкладення '%s'"
 
416
 
417
+ #: admin/class-admin.php:1083
 
 
 
 
 
418
  msgid "Attachment not found"
419
  msgstr "Вкладення не знайдено"
420
 
421
+ #: admin/class-admin.php:1112
422
+ msgid "<b>Thumbnail</b> for <i><b>Document Gallery</b></i>"
423
+ msgstr "<b>Мініатюра</b> для <i><b>Document Gallery</b></i>"
424
+
425
+ #: admin/class-admin.php:1199
426
  msgid "Level"
427
  msgstr "Рівень"
428
 
429
+ #: admin/class-admin.php:1200
430
  msgid "Message"
431
  msgstr "Повідомлення"
432
 
433
+ #: admin/class-admin.php:1208
434
  msgid "Expand All"
435
  msgstr "Розгорнути Все"
436
 
437
+ #: admin/class-admin.php:1211
438
  msgid "Collapse All"
439
  msgstr "Згорнути Все"
440
 
441
+ #: admin/class-admin.php:1263
442
  msgid "Clear Log"
443
  msgstr "Очистити журнал"
444
 
445
+ #: admin/class-admin.php:1270
446
  msgid "There are no log entries at this time."
447
  msgstr "На цю мить журнал порожній."
448
 
449
+ #: admin/class-admin.php:1270
450
  msgid "For Your information:"
451
  msgstr "До Вашого відома:"
452
 
453
+ #: admin/class-admin.php:1270
454
  msgid "is turned ON"
455
  msgstr "увімкнуто"
456
 
457
+ #: admin/class-admin.php:1270
458
  msgid "is turned OFF"
459
  msgstr "вимкнуто"
460
 
461
+ #: admin/media-manager-template.php:9
462
+ msgid "Link To"
463
+ msgstr "Посилання до"
464
+
465
+ #: admin/media-manager-template.php:12
466
+ msgid "Media File"
467
+ msgstr "Медіафайл"
468
+
469
+ #: admin/media-manager-template.php:15
470
+ msgid "Attachment Page"
471
+ msgstr "Сторінка прикріпленого файлу"
472
+
473
+ #: admin/media-manager-template.php:23
474
+ msgid "Columns"
475
+ msgstr "Колонки"
476
+
477
+ #: inc/class-gallery.php:91
478
  msgid "Generated using Document Gallery. Get yours here: "
479
  msgstr ""
480
  "Згенеровано використовуючи Document Gallery. Сподобалось? Отримайте Вашу "
481
  "копію тут: "
482
 
483
+ #: inc/class-gallery.php:93
484
  msgid "No attachments to display. How boring! :("
485
  msgstr "Жодного вкладення для відображення. Нудьга! :("
486
 
487
+ #: inc/class-gallery.php:94
488
+ msgid "The %s value entered, \"%s\", is not valid."
489
+ msgstr "Вказаний параметр %s зі значенням \"%s\" є не допустимим."
490
+
491
+ #: inc/class-gallery.php:95
492
  msgid "The %s parameter may only be \"%s\" or \"%s.\" You entered \"%s.\""
493
  msgstr ""
494
  "Параметр %s може приймати тільки значення \"%s\" або \"%s.\" Ви ж ввели \"%s."
495
  "\""
496
 
497
+ #: inc/class-gallery.php:226
498
+ msgid "Attempted to call invalid function: "
499
+ msgstr "Спроба виклику недійсної функції: "
 
 
 
500
 
501
+ #: inc/class-gallery.php:349
502
+ msgid "%s may only be a comma-delimited list of integers."
503
+ msgstr "Параметр %s може бути лише переліком цілих чисел розділених комами."
504
 
505
+ #: inc/class-gallery.php:750
506
  msgid "%s is not a valid term name in %s."
507
+ msgstr "%s не є допустимим значенням параметра %s."
508
 
509
+ #: inc/class-image-editor-imagick.php:38
510
  msgid "Failed to set Imagick page number"
511
  msgstr "Не вдалося встановити номер сторінки в Imagick"
512
 
513
+ #: inc/class-thumber.php:95
514
  msgid "Attempting to generate thumbnail for attachment #%d with (%s)"
515
  msgstr "Спроба утворення мініатюри для вкладення №%d за допомоги (%s)"
516
 
517
+ #: inc/class-thumber.php:168
518
  msgid "Could not open file: "
519
  msgstr "Не вдалося відкрити файл: "
520
 
521
+ #: inc/class-thumber.php:174
522
  msgid "Could not write file: "
523
  msgstr "Не вдалося записати файл: "
524
 
525
+ #: inc/class-thumber.php:213
526
  msgid "Failed to open file in Imagick: "
527
  msgstr "Не вдалося відкрити файл в Imagick: "
528
 
529
+ #: inc/class-thumber.php:225
530
  msgid "Failed to save image in Imagick: "
531
  msgstr "Не вдалося зберегти зображення в Imagick: "
532
 
533
+ #: inc/class-thumber.php:285
534
  msgid "Ghostscript failed: "
535
  msgstr "Ghostscript вийшов із ладу: "
536
 
537
+ #: inc/class-thumber.php:577
 
 
 
 
538
  msgid "Thumbnail Generators: "
539
  msgstr "Генератори мініатюр: "
540
 
541
+ #: inc/class-thumber.php:588
542
  msgid "No thumbnail generators enabled."
543
  msgstr "Відсутні підключені генератори мініатюр."
544
 
545
+ #: inc/class-thumber.php:638
546
  msgid "Failed to get image editor: "
547
  msgstr ""
548
  "Невдача при спробі використання графічного редактора (WP_Image_Editor): "
549
 
550
+ #: inc/class-thumber.php:650
551
  msgid "Failed to save image: "
552
  msgstr "Не вдалося зберегти зображення: "
553
 
languages/document-gallery-uk_UA.mo DELETED
Binary file
languages/document-gallery.pot CHANGED
@@ -2,9 +2,9 @@
2
  # This file is distributed under the same license as the Document Gallery package.
3
  msgid ""
4
  msgstr ""
5
- "Project-Id-Version: Document Gallery 3.1\n"
6
- "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/document-gallery\n"
7
- "POT-Creation-Date: 2015-05-17 00:41:05+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
@@ -20,7 +20,7 @@ msgstr ""
20
  msgid "Thumbnail Management"
21
  msgstr ""
22
 
23
- #: admin/class-admin.php:32 admin/class-admin.php:1216
24
  msgid "Logging"
25
  msgstr ""
26
 
@@ -28,392 +28,396 @@ msgstr ""
28
  msgid "Advanced"
29
  msgstr ""
30
 
31
- #: admin/class-admin.php:44 admin/class-admin.php:98
32
  #: admin/media-manager-template.php:5
33
  msgid "Document Gallery Settings"
34
  msgstr ""
35
 
36
- #: admin/class-admin.php:72
37
  msgid "Settings"
38
  msgstr ""
39
 
40
- #: admin/class-admin.php:86
41
  msgid "Donate"
42
  msgstr ""
43
 
44
- #. #-#-#-#-# plugin.pot (Document Gallery 3.1) #-#-#-#-#
45
  #. Plugin Name of the plugin/theme
46
- #: admin/class-admin.php:99
47
  msgid "Document Gallery"
48
  msgstr ""
49
 
50
- #: admin/class-admin.php:126
51
  msgid "Create Document Gallery"
52
  msgstr ""
53
 
54
- #: admin/class-admin.php:127
55
  msgid "Create a new Document Gallery"
56
  msgstr ""
57
 
58
- #: admin/class-admin.php:128
59
- msgid "&#8592; Cancel Document Gallery"
60
  msgstr ""
61
 
62
- #: admin/class-admin.php:129
63
  msgid "Update Document Gallery"
64
  msgstr ""
65
 
66
- #: admin/class-admin.php:130
67
  msgid "Insert Document Gallery"
68
  msgstr ""
69
 
70
- #: admin/class-admin.php:131 admin/class-admin.php:132
71
  msgid "Add to Document Gallery"
72
  msgstr ""
73
 
74
- #: admin/class-admin.php:133
75
  msgid "Edit Document Gallery"
76
  msgstr ""
77
 
78
- #: admin/class-admin.php:177
79
  msgid "Default Settings"
80
  msgstr ""
81
 
82
- #: admin/class-admin.php:181
83
  msgid "Thumbnail Generation"
84
  msgstr ""
85
 
86
- #: admin/class-admin.php:185
87
  msgid "Custom CSS"
88
  msgstr ""
89
 
90
- #: admin/class-admin.php:197
91
  msgid "Link to attachment page rather than to file"
92
  msgstr ""
93
 
94
- #: admin/class-admin.php:210
95
  msgid "The number of columns to display when not rendering descriptions."
96
  msgstr ""
97
 
98
- #: admin/class-admin.php:222 admin/media-manager-template.php:43
99
  msgid "Include document descriptions"
100
  msgstr ""
101
 
102
- #: admin/class-admin.php:234 admin/media-manager-template.php:50
103
  msgid "Use auto-generated document thumbnails"
104
  msgstr ""
105
 
106
- #: admin/class-admin.php:247 admin/media-manager-template.php:72
107
  msgid "Ascending or descending sorting of documents"
108
  msgstr ""
109
 
110
- #: admin/class-admin.php:260 admin/media-manager-template.php:57
111
  msgid "Which field to order documents by"
112
  msgstr ""
113
 
114
- #: admin/class-admin.php:273
115
  msgid ""
116
  "Whether matched documents must have all taxa_names (AND) or at least one (OR)"
117
  msgstr ""
118
 
119
- #: admin/class-admin.php:286
120
  msgid "Limit the number of documents included. -1 means no limit."
121
  msgstr ""
122
 
123
- #: admin/class-admin.php:299
124
  msgid ""
125
  "Comma-delimited list of <a href=\"http://en.wikipedia.org/wiki/"
126
  "Internet_media_type#List_of_common_media_types\">MIME types</a>."
127
  msgstr ""
128
 
129
- #: admin/class-admin.php:312
130
- msgid "Which post status to look for when querying documents."
131
  msgstr ""
132
 
133
  #: admin/class-admin.php:325
 
 
 
 
134
  msgid "Which post type to look for when querying documents."
135
  msgstr ""
136
 
137
- #: admin/class-admin.php:329
138
  msgid "Audio/Video"
139
  msgstr ""
140
 
141
- #: admin/class-admin.php:337
142
  msgid "Locally generate thumbnails for audio & video files."
143
  msgstr ""
144
 
145
- #: admin/class-admin.php:350
146
  msgid ""
147
  "Use <a href=\"http://www.ghostscript.com/\" target=\"_blank\">Ghostscript</"
148
  "a> for faster local PDF processing (compared to Imagick)."
149
  msgstr ""
150
 
151
- #: admin/class-admin.php:351
152
  msgid ""
153
  "Your server is not configured to run <a href=\"http://www.ghostscript.com/\" "
154
  "target=\"_blank\">Ghostscript</a>."
155
  msgstr ""
156
 
157
- #: admin/class-admin.php:365
158
  msgid ""
159
  "Use <a href=\"http://www.php.net/manual/en/book.imagick.php\" target=\"_blank"
160
  "\">Imagick</a> to handle lots of filetypes locally."
161
  msgstr ""
162
 
163
- #: admin/class-admin.php:366
164
  msgid ""
165
  "Your server is not configured to run <a href=\"http://www.php.net/manual/en/"
166
  "book.imagick.php\" target=\"_blank\">Imagick</a>."
167
  msgstr ""
168
 
169
- #: admin/class-admin.php:371
170
  msgid "Max Thumbnail Dimensions"
171
  msgstr ""
172
 
173
- #: admin/class-admin.php:388
174
  msgid "The max width and height (in pixels) that thumbnails will be generated."
175
  msgstr ""
176
 
177
- #: admin/class-admin.php:417
178
  msgid "Advanced Thumbnail Generation"
179
  msgstr ""
180
 
181
- #: admin/class-admin.php:421
182
  msgid "Logging Enabled"
183
  msgstr ""
184
 
185
- #: admin/class-admin.php:429
186
  msgid "Whether to log debug and error information related to Document Gallery."
187
  msgstr ""
188
 
189
- #: admin/class-admin.php:433
190
  msgid "Logging Purge Interval"
191
  msgstr ""
192
 
193
- #: admin/class-admin.php:442
194
  msgid "Number of days to keep old log entries (0 disables purging)."
195
  msgstr ""
196
 
197
- #: admin/class-admin.php:446
198
  msgid "Option Validation"
199
  msgstr ""
200
 
201
- #: admin/class-admin.php:454
202
  msgid ""
203
  "Whether option structure should be validated before save. This is not "
204
  "generally necessary."
205
  msgstr ""
206
 
207
- #: admin/class-admin.php:458
208
  msgid "Thumbnail Generation Timeout"
209
  msgstr ""
210
 
211
- #: admin/class-admin.php:467
212
  msgid ""
213
  "Max number of seconds to wait for thumbnail generation before defaulting to "
214
  "filetype icons."
215
  msgstr ""
216
 
217
- #: admin/class-admin.php:468
218
  msgid ""
219
  "Note that generation will continue where timeout happened next time the "
220
  "gallery is loaded."
221
  msgstr ""
222
 
223
- #: admin/class-admin.php:471
224
  msgid "Ghostscript Absolute Path"
225
  msgstr ""
226
 
227
- #: admin/class-admin.php:480
228
  msgid "Successfully auto-detected the location of Ghostscript."
229
  msgstr ""
230
 
231
- #: admin/class-admin.php:481
232
  msgid "Failed to auto-detect the location of Ghostscript."
233
  msgstr ""
234
 
235
- #: admin/class-admin.php:485
236
  msgid "Options Array Dump"
237
  msgstr ""
238
 
239
- #: admin/class-admin.php:541
240
  msgid "Invalid width given: "
241
  msgstr ""
242
 
243
- #: admin/class-admin.php:554
244
  msgid "Invalid height given: "
245
  msgstr ""
246
 
247
- #: admin/class-admin.php:709
248
  msgid "File extension doesn't match the MIME type of the image: "
249
  msgstr ""
250
 
251
- #: admin/class-admin.php:715
252
  msgid "Uploaded file size exceeds the allowable limit: "
253
  msgstr ""
254
 
255
- #: admin/class-admin.php:722
256
  msgid "Uploaded file is not an image: "
257
  msgstr ""
258
 
259
- #: admin/class-admin.php:731
260
  msgid "Failed to get uploaded file: "
261
  msgstr ""
262
 
263
- #: admin/class-admin.php:768
264
  msgid "Invalid Ghostscript path given: "
265
  msgstr ""
266
 
267
- #: admin/class-admin.php:779
268
  msgid "Invalid timeout given: "
269
  msgstr ""
270
 
271
- #: admin/class-admin.php:794
272
  msgid "Invalid logging purge interval given: "
273
  msgstr ""
274
 
275
- #: admin/class-admin.php:819
276
  msgid ""
277
  "The following values will be used by default in the shortcode. You can still "
278
  "manually set each of these values in each individual shortcode."
279
  msgstr ""
280
 
281
- #: admin/class-admin.php:826
282
  msgid "Select which tools to use when generating thumbnails."
283
  msgstr ""
284
 
285
- #: admin/class-admin.php:835
286
  msgid ""
287
  "Enter custom CSS styling for use with document galleries. To see which ids "
288
  "and classes you can style, take a look at <a href=\"%s\" target=\"_blank"
289
  "\">style.css</a>."
290
  msgstr ""
291
 
292
- #: admin/class-admin.php:853
293
  msgid ""
294
  "Unless you <em>really</em> know what you're doing, you should not touch "
295
  "these values."
296
  msgstr ""
297
 
298
- #: admin/class-admin.php:856
299
  msgid ""
300
  "NOTE: <code>exec()</code> is not accessible. Ghostscript will not function."
301
  msgstr ""
302
 
303
- #: admin/class-admin.php:867
304
  msgid ""
305
  "The following <em>readonly text</em> should be provided when <a href="
306
  "\"http://wordpress.org/support/plugin/document-gallery\" target=\"_blank"
307
  "\">reporting a bug</a>:"
308
  msgstr ""
309
 
310
- #: admin/class-admin.php:976
311
  msgid "Select All"
312
  msgstr ""
313
 
314
- #: admin/class-admin.php:979
315
  msgid "Thumbnail"
316
  msgstr ""
317
 
318
- #: admin/class-admin.php:980
319
  msgid "File name"
320
  msgstr ""
321
 
322
- #: admin/class-admin.php:981
323
  msgid "Description"
324
  msgstr ""
325
 
326
- #: admin/class-admin.php:983 admin/class-admin.php:1147
327
  msgid "Date"
328
  msgstr ""
329
 
330
- #: admin/class-admin.php:986
331
  msgid "Delete Selected"
332
  msgstr ""
333
 
334
- #: admin/class-admin.php:988
335
  msgid "item"
336
  msgid_plural "items"
337
  msgstr[0] ""
338
  msgstr[1] ""
339
 
340
- #: admin/class-admin.php:991
341
  msgid "Go to the first page"
342
  msgstr ""
343
 
344
- #: admin/class-admin.php:992
345
  msgid "Go to the previous page"
346
  msgstr ""
347
 
348
- #: admin/class-admin.php:994
349
  msgid "Current page"
350
  msgstr ""
351
 
352
- #: admin/class-admin.php:994
353
  msgid "of"
354
  msgstr ""
355
 
356
- #: admin/class-admin.php:995
357
  msgid "Go to the next page"
358
  msgstr ""
359
 
360
- #: admin/class-admin.php:996
361
  msgid "Go to the last page"
362
  msgstr ""
363
 
364
- #: admin/class-admin.php:998
365
  msgid "items per page"
366
  msgstr ""
367
 
368
- #: admin/class-admin.php:1032
369
  msgid "View"
370
  msgstr ""
371
 
372
- #: admin/class-admin.php:1033
373
  msgid "attachment page"
374
  msgstr ""
375
 
376
- #: admin/class-admin.php:1033
377
  msgid "Attachment not found"
378
  msgstr ""
379
 
380
- #: admin/class-admin.php:1060
381
  msgid "<b>Thumbnail</b> for <i><b>Document Gallery</b></i>"
382
  msgstr ""
383
 
384
- #: admin/class-admin.php:1148
385
  msgid "Level"
386
  msgstr ""
387
 
388
- #: admin/class-admin.php:1149
389
  msgid "Message"
390
  msgstr ""
391
 
392
- #: admin/class-admin.php:1157
393
  msgid "Expand All"
394
  msgstr ""
395
 
396
- #: admin/class-admin.php:1160
397
  msgid "Collapse All"
398
  msgstr ""
399
 
400
- #: admin/class-admin.php:1209
401
  msgid "Clear Log"
402
  msgstr ""
403
 
404
- #: admin/class-admin.php:1216
405
  msgid "There are no log entries at this time."
406
  msgstr ""
407
 
408
- #: admin/class-admin.php:1216
409
  msgid "For Your information:"
410
  msgstr ""
411
 
412
- #: admin/class-admin.php:1216
413
  msgid "is turned ON"
414
  msgstr ""
415
 
416
- #: admin/class-admin.php:1216
417
  msgid "is turned OFF"
418
  msgstr ""
419
 
@@ -433,75 +437,75 @@ msgstr ""
433
  msgid "Columns"
434
  msgstr ""
435
 
436
- #: inc/class-gallery.php:82
437
  msgid "Generated using Document Gallery. Get yours here: "
438
  msgstr ""
439
 
440
- #: inc/class-gallery.php:84
441
  msgid "No attachments to display. How boring! :("
442
  msgstr ""
443
 
444
- #: inc/class-gallery.php:85
445
  msgid "The %s value entered, \"%s\", is not valid."
446
  msgstr ""
447
 
448
- #: inc/class-gallery.php:86
449
  msgid "The %s parameter may only be \"%s\" or \"%s.\" You entered \"%s.\""
450
  msgstr ""
451
 
452
- #: inc/class-gallery.php:213
453
  msgid "Attempted to call invalid function: "
454
  msgstr ""
455
 
456
- #: inc/class-gallery.php:322
457
  msgid "%s may only be a comma-delimited list of integers."
458
  msgstr ""
459
 
460
- #: inc/class-gallery.php:668
461
  msgid "%s is not a valid term name in %s."
462
  msgstr ""
463
 
464
- #: inc/class-image-editor-imagick.php:37
465
  msgid "Failed to set Imagick page number"
466
  msgstr ""
467
 
468
- #: inc/class-thumber.php:87
469
  msgid "Attempting to generate thumbnail for attachment #%d with (%s)"
470
  msgstr ""
471
 
472
- #: inc/class-thumber.php:160
473
  msgid "Could not open file: "
474
  msgstr ""
475
 
476
- #: inc/class-thumber.php:165
477
  msgid "Could not write file: "
478
  msgstr ""
479
 
480
- #: inc/class-thumber.php:202
481
  msgid "Failed to open file in Imagick: "
482
  msgstr ""
483
 
484
- #: inc/class-thumber.php:213
485
  msgid "Failed to save image in Imagick: "
486
  msgstr ""
487
 
488
- #: inc/class-thumber.php:271
489
  msgid "Ghostscript failed: "
490
  msgstr ""
491
 
492
- #: inc/class-thumber.php:571
493
  msgid "Thumbnail Generators: "
494
  msgstr ""
495
 
496
- #: inc/class-thumber.php:582
497
  msgid "No thumbnail generators enabled."
498
  msgstr ""
499
 
500
- #: inc/class-thumber.php:632
501
  msgid "Failed to get image editor: "
502
  msgstr ""
503
 
504
- #: inc/class-thumber.php:644
505
  msgid "Failed to save image: "
506
  msgstr ""
507
 
2
  # This file is distributed under the same license as the Document Gallery package.
3
  msgid ""
4
  msgstr ""
5
+ "Project-Id-Version: Document Gallery 3.3\n"
6
+ "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/document-gallery\n"
7
+ "POT-Creation-Date: 2015-06-20 17:03:17+00:00\n"
8
  "MIME-Version: 1.0\n"
9
  "Content-Type: text/plain; charset=UTF-8\n"
10
  "Content-Transfer-Encoding: 8bit\n"
20
  msgid "Thumbnail Management"
21
  msgstr ""
22
 
23
+ #: admin/class-admin.php:32 admin/class-admin.php:1271
24
  msgid "Logging"
25
  msgstr ""
26
 
28
  msgid "Advanced"
29
  msgstr ""
30
 
31
+ #: admin/class-admin.php:45 admin/class-admin.php:99
32
  #: admin/media-manager-template.php:5
33
  msgid "Document Gallery Settings"
34
  msgstr ""
35
 
36
+ #: admin/class-admin.php:73
37
  msgid "Settings"
38
  msgstr ""
39
 
40
+ #: admin/class-admin.php:87
41
  msgid "Donate"
42
  msgstr ""
43
 
44
+ #. #-#-#-#-# plugin.pot (Document Gallery 3.3) #-#-#-#-#
45
  #. Plugin Name of the plugin/theme
46
+ #: admin/class-admin.php:100
47
  msgid "Document Gallery"
48
  msgstr ""
49
 
50
+ #: admin/class-admin.php:127
51
  msgid "Create Document Gallery"
52
  msgstr ""
53
 
54
+ #: admin/class-admin.php:128
55
  msgid "Create a new Document Gallery"
56
  msgstr ""
57
 
58
+ #: admin/class-admin.php:129
59
+ msgid "Cancel Document Gallery"
60
  msgstr ""
61
 
62
+ #: admin/class-admin.php:130
63
  msgid "Update Document Gallery"
64
  msgstr ""
65
 
66
+ #: admin/class-admin.php:131
67
  msgid "Insert Document Gallery"
68
  msgstr ""
69
 
70
+ #: admin/class-admin.php:132 admin/class-admin.php:133
71
  msgid "Add to Document Gallery"
72
  msgstr ""
73
 
74
+ #: admin/class-admin.php:134
75
  msgid "Edit Document Gallery"
76
  msgstr ""
77
 
78
+ #: admin/class-admin.php:178
79
  msgid "Default Settings"
80
  msgstr ""
81
 
82
+ #: admin/class-admin.php:182
83
  msgid "Thumbnail Generation"
84
  msgstr ""
85
 
86
+ #: admin/class-admin.php:186
87
  msgid "Custom CSS"
88
  msgstr ""
89
 
90
+ #: admin/class-admin.php:198
91
  msgid "Link to attachment page rather than to file"
92
  msgstr ""
93
 
94
+ #: admin/class-admin.php:211
95
  msgid "The number of columns to display when not rendering descriptions."
96
  msgstr ""
97
 
98
+ #: admin/class-admin.php:223 admin/media-manager-template.php:50
99
  msgid "Include document descriptions"
100
  msgstr ""
101
 
102
+ #: admin/class-admin.php:235 admin/media-manager-template.php:57
103
  msgid "Use auto-generated document thumbnails"
104
  msgstr ""
105
 
106
+ #: admin/class-admin.php:248 admin/media-manager-template.php:79
107
  msgid "Ascending or descending sorting of documents"
108
  msgstr ""
109
 
110
+ #: admin/class-admin.php:261 admin/media-manager-template.php:64
111
  msgid "Which field to order documents by"
112
  msgstr ""
113
 
114
+ #: admin/class-admin.php:274
115
  msgid ""
116
  "Whether matched documents must have all taxa_names (AND) or at least one (OR)"
117
  msgstr ""
118
 
119
+ #: admin/class-admin.php:287
120
  msgid "Limit the number of documents included. -1 means no limit."
121
  msgstr ""
122
 
123
+ #: admin/class-admin.php:300
124
  msgid ""
125
  "Comma-delimited list of <a href=\"http://en.wikipedia.org/wiki/"
126
  "Internet_media_type#List_of_common_media_types\">MIME types</a>."
127
  msgstr ""
128
 
129
+ #: admin/class-admin.php:312 admin/media-manager-template.php:43
130
+ msgid "Open thumbnail links in new window"
131
  msgstr ""
132
 
133
  #: admin/class-admin.php:325
134
+ msgid "Which post status to look for when querying documents."
135
+ msgstr ""
136
+
137
+ #: admin/class-admin.php:338
138
  msgid "Which post type to look for when querying documents."
139
  msgstr ""
140
 
141
+ #: admin/class-admin.php:342
142
  msgid "Audio/Video"
143
  msgstr ""
144
 
145
+ #: admin/class-admin.php:350
146
  msgid "Locally generate thumbnails for audio & video files."
147
  msgstr ""
148
 
149
+ #: admin/class-admin.php:363
150
  msgid ""
151
  "Use <a href=\"http://www.ghostscript.com/\" target=\"_blank\">Ghostscript</"
152
  "a> for faster local PDF processing (compared to Imagick)."
153
  msgstr ""
154
 
155
+ #: admin/class-admin.php:364
156
  msgid ""
157
  "Your server is not configured to run <a href=\"http://www.ghostscript.com/\" "
158
  "target=\"_blank\">Ghostscript</a>."
159
  msgstr ""
160
 
161
+ #: admin/class-admin.php:378
162
  msgid ""
163
  "Use <a href=\"http://www.php.net/manual/en/book.imagick.php\" target=\"_blank"
164
  "\">Imagick</a> to handle lots of filetypes locally."
165
  msgstr ""
166
 
167
+ #: admin/class-admin.php:379
168
  msgid ""
169
  "Your server is not configured to run <a href=\"http://www.php.net/manual/en/"
170
  "book.imagick.php\" target=\"_blank\">Imagick</a>."
171
  msgstr ""
172
 
173
+ #: admin/class-admin.php:384
174
  msgid "Max Thumbnail Dimensions"
175
  msgstr ""
176
 
177
+ #: admin/class-admin.php:402
178
  msgid "The max width and height (in pixels) that thumbnails will be generated."
179
  msgstr ""
180
 
181
+ #: admin/class-admin.php:432
182
  msgid "Advanced Thumbnail Generation"
183
  msgstr ""
184
 
185
+ #: admin/class-admin.php:436
186
  msgid "Logging Enabled"
187
  msgstr ""
188
 
189
+ #: admin/class-admin.php:444
190
  msgid "Whether to log debug and error information related to Document Gallery."
191
  msgstr ""
192
 
193
+ #: admin/class-admin.php:448
194
  msgid "Logging Purge Interval"
195
  msgstr ""
196
 
197
+ #: admin/class-admin.php:457
198
  msgid "Number of days to keep old log entries (0 disables purging)."
199
  msgstr ""
200
 
201
+ #: admin/class-admin.php:461
202
  msgid "Option Validation"
203
  msgstr ""
204
 
205
+ #: admin/class-admin.php:469
206
  msgid ""
207
  "Whether option structure should be validated before save. This is not "
208
  "generally necessary."
209
  msgstr ""
210
 
211
+ #: admin/class-admin.php:473
212
  msgid "Thumbnail Generation Timeout"
213
  msgstr ""
214
 
215
+ #: admin/class-admin.php:482
216
  msgid ""
217
  "Max number of seconds to wait for thumbnail generation before defaulting to "
218
  "filetype icons."
219
  msgstr ""
220
 
221
+ #: admin/class-admin.php:483
222
  msgid ""
223
  "Note that generation will continue where timeout happened next time the "
224
  "gallery is loaded."
225
  msgstr ""
226
 
227
+ #: admin/class-admin.php:487
228
  msgid "Ghostscript Absolute Path"
229
  msgstr ""
230
 
231
+ #: admin/class-admin.php:496
232
  msgid "Successfully auto-detected the location of Ghostscript."
233
  msgstr ""
234
 
235
+ #: admin/class-admin.php:497
236
  msgid "Failed to auto-detect the location of Ghostscript."
237
  msgstr ""
238
 
239
+ #: admin/class-admin.php:501
240
  msgid "Options Array Dump"
241
  msgstr ""
242
 
243
+ #: admin/class-admin.php:561
244
  msgid "Invalid width given: "
245
  msgstr ""
246
 
247
+ #: admin/class-admin.php:574
248
  msgid "Invalid height given: "
249
  msgstr ""
250
 
251
+ #: admin/class-admin.php:734
252
  msgid "File extension doesn't match the MIME type of the image: "
253
  msgstr ""
254
 
255
+ #: admin/class-admin.php:740
256
  msgid "Uploaded file size exceeds the allowable limit: "
257
  msgstr ""
258
 
259
+ #: admin/class-admin.php:748
260
  msgid "Uploaded file is not an image: "
261
  msgstr ""
262
 
263
+ #: admin/class-admin.php:758
264
  msgid "Failed to get uploaded file: "
265
  msgstr ""
266
 
267
+ #: admin/class-admin.php:802
268
  msgid "Invalid Ghostscript path given: "
269
  msgstr ""
270
 
271
+ #: admin/class-admin.php:813
272
  msgid "Invalid timeout given: "
273
  msgstr ""
274
 
275
+ #: admin/class-admin.php:828
276
  msgid "Invalid logging purge interval given: "
277
  msgstr ""
278
 
279
+ #: admin/class-admin.php:853
280
  msgid ""
281
  "The following values will be used by default in the shortcode. You can still "
282
  "manually set each of these values in each individual shortcode."
283
  msgstr ""
284
 
285
+ #: admin/class-admin.php:860
286
  msgid "Select which tools to use when generating thumbnails."
287
  msgstr ""
288
 
289
+ #: admin/class-admin.php:869
290
  msgid ""
291
  "Enter custom CSS styling for use with document galleries. To see which ids "
292
  "and classes you can style, take a look at <a href=\"%s\" target=\"_blank"
293
  "\">style.css</a>."
294
  msgstr ""
295
 
296
+ #: admin/class-admin.php:888
297
  msgid ""
298
  "Unless you <em>really</em> know what you're doing, you should not touch "
299
  "these values."
300
  msgstr ""
301
 
302
+ #: admin/class-admin.php:891
303
  msgid ""
304
  "NOTE: <code>exec()</code> is not accessible. Ghostscript will not function."
305
  msgstr ""
306
 
307
+ #: admin/class-admin.php:902
308
  msgid ""
309
  "The following <em>readonly text</em> should be provided when <a href="
310
  "\"http://wordpress.org/support/plugin/document-gallery\" target=\"_blank"
311
  "\">reporting a bug</a>:"
312
  msgstr ""
313
 
314
+ #: admin/class-admin.php:1016
315
  msgid "Select All"
316
  msgstr ""
317
 
318
+ #: admin/class-admin.php:1019
319
  msgid "Thumbnail"
320
  msgstr ""
321
 
322
+ #: admin/class-admin.php:1023
323
  msgid "File name"
324
  msgstr ""
325
 
326
+ #: admin/class-admin.php:1024
327
  msgid "Description"
328
  msgstr ""
329
 
330
+ #: admin/class-admin.php:1029 admin/class-admin.php:1199
331
  msgid "Date"
332
  msgstr ""
333
 
334
+ #: admin/class-admin.php:1032
335
  msgid "Delete Selected"
336
  msgstr ""
337
 
338
+ #: admin/class-admin.php:1034
339
  msgid "item"
340
  msgid_plural "items"
341
  msgstr[0] ""
342
  msgstr[1] ""
343
 
344
+ #: admin/class-admin.php:1037
345
  msgid "Go to the first page"
346
  msgstr ""
347
 
348
+ #: admin/class-admin.php:1038
349
  msgid "Go to the previous page"
350
  msgstr ""
351
 
352
+ #: admin/class-admin.php:1040
353
  msgid "Current page"
354
  msgstr ""
355
 
356
+ #: admin/class-admin.php:1040
357
  msgid "of"
358
  msgstr ""
359
 
360
+ #: admin/class-admin.php:1041
361
  msgid "Go to the next page"
362
  msgstr ""
363
 
364
+ #: admin/class-admin.php:1042
365
  msgid "Go to the last page"
366
  msgstr ""
367
 
368
+ #: admin/class-admin.php:1044
369
  msgid "items per page"
370
  msgstr ""
371
 
372
+ #: admin/class-admin.php:1083
373
  msgid "View"
374
  msgstr ""
375
 
376
+ #: admin/class-admin.php:1084
377
  msgid "attachment page"
378
  msgstr ""
379
 
380
+ #: admin/class-admin.php:1084
381
  msgid "Attachment not found"
382
  msgstr ""
383
 
384
+ #: admin/class-admin.php:1113
385
  msgid "<b>Thumbnail</b> for <i><b>Document Gallery</b></i>"
386
  msgstr ""
387
 
388
+ #: admin/class-admin.php:1200
389
  msgid "Level"
390
  msgstr ""
391
 
392
+ #: admin/class-admin.php:1201
393
  msgid "Message"
394
  msgstr ""
395
 
396
+ #: admin/class-admin.php:1209
397
  msgid "Expand All"
398
  msgstr ""
399
 
400
+ #: admin/class-admin.php:1212
401
  msgid "Collapse All"
402
  msgstr ""
403
 
404
+ #: admin/class-admin.php:1264
405
  msgid "Clear Log"
406
  msgstr ""
407
 
408
+ #: admin/class-admin.php:1271
409
  msgid "There are no log entries at this time."
410
  msgstr ""
411
 
412
+ #: admin/class-admin.php:1271
413
  msgid "For Your information:"
414
  msgstr ""
415
 
416
+ #: admin/class-admin.php:1271
417
  msgid "is turned ON"
418
  msgstr ""
419
 
420
+ #: admin/class-admin.php:1271
421
  msgid "is turned OFF"
422
  msgstr ""
423
 
437
  msgid "Columns"
438
  msgstr ""
439
 
440
+ #: inc/class-gallery.php:91
441
  msgid "Generated using Document Gallery. Get yours here: "
442
  msgstr ""
443
 
444
+ #: inc/class-gallery.php:93
445
  msgid "No attachments to display. How boring! :("
446
  msgstr ""
447
 
448
+ #: inc/class-gallery.php:94
449
  msgid "The %s value entered, \"%s\", is not valid."
450
  msgstr ""
451
 
452
+ #: inc/class-gallery.php:95
453
  msgid "The %s parameter may only be \"%s\" or \"%s.\" You entered \"%s.\""
454
  msgstr ""
455
 
456
+ #: inc/class-gallery.php:226
457
  msgid "Attempted to call invalid function: "
458
  msgstr ""
459
 
460
+ #: inc/class-gallery.php:349
461
  msgid "%s may only be a comma-delimited list of integers."
462
  msgstr ""
463
 
464
+ #: inc/class-gallery.php:750
465
  msgid "%s is not a valid term name in %s."
466
  msgstr ""
467
 
468
+ #: inc/class-image-editor-imagick.php:38
469
  msgid "Failed to set Imagick page number"
470
  msgstr ""
471
 
472
+ #: inc/class-thumber.php:95
473
  msgid "Attempting to generate thumbnail for attachment #%d with (%s)"
474
  msgstr ""
475
 
476
+ #: inc/class-thumber.php:168
477
  msgid "Could not open file: "
478
  msgstr ""
479
 
480
+ #: inc/class-thumber.php:174
481
  msgid "Could not write file: "
482
  msgstr ""
483
 
484
+ #: inc/class-thumber.php:213
485
  msgid "Failed to open file in Imagick: "
486
  msgstr ""
487
 
488
+ #: inc/class-thumber.php:225
489
  msgid "Failed to save image in Imagick: "
490
  msgstr ""
491
 
492
+ #: inc/class-thumber.php:285
493
  msgid "Ghostscript failed: "
494
  msgstr ""
495
 
496
+ #: inc/class-thumber.php:577
497
  msgid "Thumbnail Generators: "
498
  msgstr ""
499
 
500
+ #: inc/class-thumber.php:588
501
  msgid "No thumbnail generators enabled."
502
  msgstr ""
503
 
504
+ #: inc/class-thumber.php:638
505
  msgid "Failed to get image editor: "
506
  msgstr ""
507
 
508
+ #: inc/class-thumber.php:650
509
  msgid "Failed to save image: "
510
  msgstr ""
511