Media Library Assistant - Version 2.94

Version Description

  • New: The "MLA Custom Field Search Example" plugin has been substantially upgraded. The new version has many more parameters and a new plugin settings page. A Documentation tab on the settings page contains all the information you need to understand and use the new version.
  • New: A new example plugin, "MLA Postie Post After Example", adds support for running MLA mapping rules after the "Postie" plugin chron job creates posts and attachments from an email.
  • New: The Library Views/Post MIME Type "Table View" custom field queries have been enhanced to allow searching multiple field names or "all fields" for one or more values.
  • Fix: The "Smart Media Categories" example plugin has been enhanced to handle additional WordPress alternatives for term assignments and a new option is provided to exclude the Default Post Category from sync processing.
  • Fix: Wildcard values for Library Views/Post MIME Type "Table View" custom field queries have been restored.
  • Fix: PDF thumbnail streaming for mla_viewer processing has been restored.
  • Fix: For the [mla_gallery] shortcode, error reporting for the tax_query, date_query and meta_query parameters has been improved.
  • Fix: For the [mla_gallery] shortcode, proper handling of the size= "icon", "icon_only" and "icon_feature" options has been restored.
  • Fix: For the [mla_gallery] shortcode, performance is improved by avoiding a redundant LEFT JOIN database query clause (added by WP_Query).
  • Fix: For the [mla_gallery] shortcode, performance is improved by avoiding LEFT JOIN and WHERE database query clauses added by Real Media Library.
  • Fix: Unnecessary "term meta cache" queries have been removed from the Media/Assistant submenu table generation.
  • Fix: Handling of disimissible admin messages has been restored.
Download this release

Release Info

Developer dglingren
Plugin Icon 128x128 Media Library Assistant
Version 2.94
Comparing to
See all releases

Code changes from version 2.93 to 2.94

examples/plugins/mla-custom-field-search-example.php DELETED
@@ -1,326 +0,0 @@
1
- <?php
2
- /**
3
- * Extends the Media/Assistant "Search Media" box to custom field values
4
- *
5
- * In this example, a "custom:" prefix is detected in the Media/Assistant "search media" text
6
- * box and the search is modified to query a custom field for a specific value, e.g.,
7
- * "custom:photo reference=123456". You can also search for partial values:
8
- *
9
- * - To return all items that have a non-NULL value in the field, simply enter the prefix
10
- * "custom:" followed by the custom field name, for example, custom:File Size. You can also
11
- * enter the custom field name and then "=*", e.g., custom:File Size=*.
12
- * - To return all items that have a NULL value in the field, enter the custom field name and
13
- * then "=", e.g., custom:File Size=.
14
- * - To return all items that match one or more values, enter the prefix "custom:" followed by
15
- * the custom field name and then "=" followed by a list of values. For example, custom:Color=red
16
- * or custom:Color=red,green,blue. Wildcard specifications are also supported; for example, "*post"
17
- * to match anything ending in "post" or "th*da*" to match values like "the date" and "this day".
18
- *
19
- * This example plugin uses four of the many filters available in the Media/Assistant Submenu
20
- * and illustrates some of the techniques you can use to customize the submenu table display.
21
- *
22
- * Created for support topic "Searching on custom fields"
23
- * opened on 5/11/2015 by "BFI-WP".
24
- * https://wordpress.org/support/topic/searching-on-custom-fields/
25
- *
26
- * @package MLA Custom Field Search Example
27
- * @version 1.05
28
- */
29
-
30
- /*
31
- Plugin Name: MLA Custom Field Search Example
32
- Plugin URI: http://davidlingren.com/
33
- Description: Extends the Media/Assistant "Search Media" box to custom field values
34
- Author: David Lingren
35
- Version: 1.05
36
- Author URI: http://davidlingren.com/
37
-
38
- Copyright 2014 - 2015 David Lingren
39
-
40
- This program is free software; you can redistribute it and/or modify
41
- it under the terms of the GNU General Public License as published by
42
- the Free Software Foundation; either version 2 of the License, or
43
- (at your option) any later version.
44
-
45
- This program is distributed in the hope that it will be useful,
46
- but WITHOUT ANY WARRANTY; without even the implied warranty of
47
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
48
- GNU General Public License for more details.
49
-
50
- You can get a copy of the GNU General Public License by writing to the
51
- Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
52
- */
53
-
54
- /**
55
- * Class MLA Custom Field Search Example extends the Media/Assistant "Search Media" box
56
- * to custom field values
57
- *
58
- * @package MLA Custom Field Search Example
59
- * @since 1.00
60
- */
61
- class MLACustomFieldSearchExample {
62
- /**
63
- * Initialization function, similar to __construct()
64
- *
65
- * @since 1.00
66
- *
67
- * @return void
68
- */
69
- public static function initialize() {
70
- // The filters are only useful for the admin section; exit in the front-end posts/pages
71
- if ( ! is_admin() )
72
- return;
73
-
74
- // Defined in /media-library-assistant/includes/class-mla-main.php
75
- add_filter( 'mla_list_table_new_instance', 'MLACustomFieldSearchExample::mla_list_table_new_instance', 10, 1 );
76
-
77
- // Defined in /media-library-assistant/includes/class-mla-data.php
78
- add_filter( 'mla_list_table_query_final_terms', 'MLACustomFieldSearchExample::mla_list_table_query_final_terms', 10, 1 );
79
-
80
- // Defined in /media-library-assistant/includes/class-mla-media-modal.php
81
- add_filter( 'mla_media_modal_query_initial_terms', 'MLACustomFieldSearchExample::mla_media_modal_query_initial_terms', 10, 2 );
82
-
83
- // Defined in /media-library-assistant/includes/class-mla-data.php
84
- add_filter( 'mla_media_modal_query_final_terms', 'MLACustomFieldSearchExample::mla_media_modal_query_final_terms', 10, 1 );
85
- }
86
-
87
- /**
88
- * Extend the MLA_List_Table class
89
- *
90
- * This filter gives you an opportunity to extend the MLA_List_Table class.
91
- * You can also use this filter to inspect or modify any of the $_REQUEST arguments.
92
- *
93
- * @since 1.00
94
- *
95
- * @param object $mla_list_table NULL, to indicate no extension/use the base class.
96
- *
97
- * @return object updated mla_list_table object.
98
- */
99
- public static function mla_list_table_new_instance( $mla_list_table ) {
100
- /*
101
- * Look for the special "custom:" prefix in the Search Media text box,
102
- * after checking for the "debug" prefixes.
103
- */
104
- if ( isset( $_REQUEST['s'] ) ) {
105
- switch ( substr( $_REQUEST['s'], 0, 3 ) ) {
106
- case '>|<':
107
- self::$custom_field_parameters['debug'] = 'console';
108
- $start = 3;
109
- break;
110
- case '<|>':
111
- self::$custom_field_parameters['debug'] = 'log';
112
- $start = 3;
113
- break;
114
- default:
115
- $start = 0;
116
- }
117
-
118
- if ( 'custom:' == substr( $_REQUEST['s'], $start, 7 ) ) {
119
- self::$custom_field_parameters['s'] = substr( $_REQUEST['s'], $start + 7 );
120
- unset( $_REQUEST['s'] );
121
- self::$custom_field_parameters['mla_search_connector'] = $_REQUEST['mla_search_connector'];
122
- unset( $_REQUEST['mla_search_connector'] );
123
- self::$custom_field_parameters['mla_search_fields'] = $_REQUEST['mla_search_fields'];
124
- unset( $_REQUEST['mla_search_fields'] );
125
- } else {
126
- self::$custom_field_parameters = array();
127
- }
128
- } // isset s=custom:
129
-
130
- return $mla_list_table;
131
- } // mla_list_table_new_instance
132
-
133
- /**
134
- * Custom Field Search "parameters"
135
- *
136
- * @since 1.01
137
- *
138
- * @var array
139
- */
140
- public static $custom_field_parameters = array();
141
-
142
- /**
143
- * Filter the WP_Query request parameters for the prepare_items query
144
- *
145
- * Gives you an opportunity to change the terms of the prepare_items query
146
- * after they are processed by the "Prepare List Table Query" handler.
147
- *
148
- * @since 1.01
149
- *
150
- * @param array WP_Query request prepared by "Prepare List Table Query"
151
- *
152
- * @return array updated WP_Query request
153
- */
154
- public static function mla_list_table_query_final_terms( $request ) {
155
- /*
156
- * If $request['offset'] and $request['posts_per_page'] are set, this is the "prepare_items" request.
157
- * If they are NOT set, this is a "view count" request, i.e., to get the count for a custom view.
158
- *
159
- * MLAData::$query_parameters and MLAData::$search_parameters contain
160
- * additional parameters used in some List Table queries.
161
- */
162
- if ( ! ( isset( $request['offset'] ) && isset( $request['posts_per_page'] ) ) ) {
163
- return $request;
164
- }
165
-
166
- if ( empty( self::$custom_field_parameters ) ) {
167
- return $request;
168
- }
169
-
170
- if ( isset( self::$custom_field_parameters['debug'] ) ) {
171
- MLAData::$query_parameters['debug'] = self::$custom_field_parameters['debug'];
172
- MLAData::$search_parameters['debug'] = self::$custom_field_parameters['debug'];
173
- MLACore::mla_debug_mode( self::$custom_field_parameters['debug'] );
174
- }
175
-
176
- // Apply default field name?
177
- if ( '=' == substr( self::$custom_field_parameters['s'], 0, 1 ) ) {
178
- $tokens = array( 'Orientation', substr( self::$custom_field_parameters['s'], 1 ) );
179
- } else {
180
- $tokens = explode( '=', self::$custom_field_parameters['s'] ) ;
181
- }
182
-
183
- // See if the custom field name is present, followed by "=" and a value
184
- if ( 1 < count( $tokens ) ) {
185
- $field = array_shift( $tokens );
186
- $value = implode( '=', $tokens );
187
- } else {
188
- // Supply a default custom field name
189
- $field = 'Orientation';
190
- $value = $tokens[0];
191
- }
192
-
193
- /*
194
- * Parse the query, remove MLA-specific elements, fix numeric and "commas" format fields
195
- */
196
- $tokens = MLAMime::mla_prepare_view_query( 'custom_field_search', 'custom:' . $field . '=' . $value );
197
- $tokens = $tokens['meta_query'];
198
-
199
- /*
200
- * Matching a meta_value to NULL requires a LEFT JOIN to a view and a special WHERE clause;
201
- * MLA filters will handle this case.
202
- */
203
- if ( isset( $tokens['key'] ) ) {
204
- MLAData::$query_parameters['use_postmeta_view'] = true;
205
- MLAData::$query_parameters['postmeta_key'] = $tokens['key'];
206
- MLAData::$query_parameters['postmeta_value'] = NULL;
207
- return $request;
208
- }
209
-
210
- /*
211
- * Process "normal" meta_query
212
- */
213
- $query = array( 'relation' => $tokens['relation'] );
214
- $padded_values = array();
215
- $patterns = array();
216
- foreach ( $tokens as $key => $value ) {
217
- if ( ! is_numeric( $key ) ) {
218
- continue;
219
- }
220
-
221
- if ( in_array( $value['key'], array( 'File Size', 'pixels', 'width', 'height' ) ) ) {
222
- if ( '=' == $value['compare'] ) {
223
- $value['value'] = str_pad( $value['value'], 15, ' ', STR_PAD_LEFT );
224
- $padded_values[ trim( $value['value'] ) ] = $value['value'];
225
- } else {
226
- $value['value'] = '%' . $value['value'];
227
- }
228
- }
229
-
230
- if ( 'LIKE' == $value['compare'] ) {
231
- $patterns[] = $value['value'];
232
- }
233
-
234
- $query[] = $value;
235
- }
236
-
237
- if ( ! empty( $padded_values ) ) {
238
- MLAData::$query_parameters['mla-metavalue'] = $padded_values;
239
- }
240
-
241
- if ( ! empty( $patterns ) ) {
242
- MLAData::$query_parameters['patterns'] = $patterns;
243
- }
244
-
245
- /*
246
- * Combine with an existing "custom view" meta_query, if present
247
- */
248
- if ( isset( $request['meta_query'] ) ) {
249
- $request['meta_query'] = array( 'relation' => 'AND', $request['meta_query'], $query );
250
- } else {
251
- $request['meta_query'] = $query;
252
- }
253
-
254
- return $request;
255
- } // mla_list_table_query_final_terms
256
-
257
- /**
258
- * MLA Edit Media "Query Attachments" initial terms Filter
259
- *
260
- * Gives you an opportunity to change the terms of the
261
- * Media Manager Modal Window "Query Attachments" query
262
- * before they are pre-processed by the MLA handler.
263
- *
264
- * @since 1.03
265
- *
266
- * @param array WP_Query terms supported for "Query Attachments"
267
- * @param array All terms passed in the request
268
- */
269
- public static function mla_media_modal_query_initial_terms( $query, $raw_query ) {
270
- /*
271
- * Look for the special "custom:" prefix in the Search Media text box,
272
- * after checking for the "debug" prefixes.
273
- */
274
- if ( isset( $query['mla_search_value'] ) ) {
275
- switch ( substr( $query['mla_search_value'], 0, 3 ) ) {
276
- case '>|<':
277
- self::$custom_field_parameters['debug'] = 'console';
278
- $start = 3;
279
- break;
280
- case '<|>':
281
- self::$custom_field_parameters['debug'] = 'log';
282
- $start = 3;
283
- break;
284
- default:
285
- $start = 0;
286
- }
287
-
288
- if ( 'custom:' == substr( $query['mla_search_value'], $start, 7 ) ) {
289
- self::$custom_field_parameters['s'] = substr( $query['mla_search_value'], $start + 7 );
290
- unset( $query['mla_search_value'] );
291
- self::$custom_field_parameters['mla_search_connector'] = $query['mla_search_connector'];
292
- unset( $query['mla_search_connector'] );
293
- self::$custom_field_parameters['mla_search_fields'] = $query['mla_search_fields'];
294
- unset( $query['mla_search_fields'] );
295
- } else {
296
- self::$custom_field_parameters = array();
297
- }
298
- } // isset mla_search_value=custom:
299
-
300
- return $query;
301
- }
302
-
303
- /**
304
- * MLA Edit Media "Query Attachments" final terms Filter
305
- *
306
- * Gives you an opportunity to change the terms of the
307
- * Media Manager Modal Window "Query Attachments" query
308
- * after they are processed by the "Prepare List Table Query" handler.
309
- *
310
- * @since 1.03
311
- *
312
- * @param array WP_Query request prepared by "Prepare List Table Query"
313
- */
314
- public static function mla_media_modal_query_final_terms( $request ) {
315
- /*
316
- * The logic used in the Media/Assistant Search Media box will work here as well
317
- */
318
- return MLACustomFieldSearchExample::mla_list_table_query_final_terms( $request );
319
- }
320
- } // Class MLACustomFieldSearchExample
321
-
322
- /*
323
- * Install the filters at an early opportunity
324
- */
325
- add_action('init', 'MLACustomFieldSearchExample::initialize');
326
- ?>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
examples/plugins/mla-custom-field-search-example/admin-settings-page.tpl ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!-- template="page" -->
2
+ <a name="backtotop"></a>
3
+ &nbsp;
4
+ <div class="wrap">
5
+ <h1 class="wp-heading-inline">[+plugin_title+] [+version+] Settings</h1>
6
+ [+messages+]
7
+ [+tablist+]
8
+ [+tab_content+]
9
+ </div><!-- wrap -->
10
+
11
+ <!-- template="tablist" -->
12
+ <h2 class="nav-tab-wrapper">
13
+ [+tablist+]
14
+ </h2>
15
+ <!-- template="tablist-item" -->
16
+ <a data-tab-id="[+data-tab-id+]" class="nav-tab [+nav-tab-active+]" href="?page=[+settings-page+]&amp;mla_tab=[+data-tab-id+]">[+title+]</a>
17
+
18
+ <!-- template="messages" -->
19
+ <div class="[+mla_messages_class+]">
20
+ <p>
21
+ [+messages+]
22
+ </p>
23
+ </div>
24
+
25
+ <!-- template="page-level-options" -->
26
+ <tr valign="top">
27
+ <td class="textright">
28
+ <input name="[+slug_prefix+]_options[media_assistant_support]" id="[+slug_prefix+]_options_media_assistant_support" type="checkbox" [+media_assistant_support_checked+] value="1" />
29
+ </td>
30
+ <td>
31
+ &nbsp;<strong>Enable Media/Assistant searches</strong>
32
+ <div class="mla-settings-help">&nbsp;&nbsp;Check this option to activate support for the Search Media box on the Media/Assistant admin submenu.</div>
33
+ </td>
34
+ </tr>
35
+ <tr valign="top">
36
+ <td class="textright">
37
+ <input name="[+slug_prefix+]_options[mmmw_support]" id="[+slug_prefix+]_options_mmmw_support" type="checkbox" [+mmmw_support_checked+] value="1" />
38
+ </td>
39
+ <td>
40
+ &nbsp;<strong>Enable Media Manager searches</strong>
41
+ <div class="mla-settings-help">&nbsp;&nbsp;Check this option to activate support for the Media Manager Modal (popup) Window.</div>
42
+ </td>
43
+ </tr>
44
+ <tr valign="top">
45
+ <td class="textright">
46
+ <strong>Prefix</strong>
47
+ </td>
48
+ <td>
49
+ <input name="[+slug_prefix+]_options[prefix]" id="[+slug_prefix+]_options_prefix" type="text" size="20" maxlength="20"value="[+prefix+]" />
50
+ <div class="mla-settings-help">&nbsp;&nbsp;Enter the prefix value that signifies a custom field search. Be sure to include something like a colon at the end.</div>
51
+ </td>
52
+ </tr>
53
+ <tr valign="top">
54
+ <td class="textright">
55
+ <strong>Default Field(s)</strong>
56
+ </td>
57
+ <td>
58
+ <input name="[+slug_prefix+]_options[default_fields]" id="[+slug_prefix+]_options_default_fields" type="text" size="40" maxlength="40"value="[+default_fields+]" />
59
+ <div class="mla-settings-help">&nbsp;&nbsp;Enter the (comma-separated) custom field name(s) to be searched by default.</div>
60
+ </td>
61
+ </tr>
62
+ <tr valign="top">
63
+ <td class="textright">
64
+ <strong>All Fields Name</strong>
65
+ </td>
66
+ <td>
67
+ <input name="[+slug_prefix+]_options[all_fields]" id="[+slug_prefix+]_options_all_fields" type="text" size="20" maxlength="20"value="[+all_fields+]" />
68
+ <div class="mla-settings-help">&nbsp;&nbsp;Enter the name that signifies a search of all custom fields.</div>
69
+ </td>
70
+ </tr>
71
+ <tr valign="top">
72
+ <td class="textright">
73
+ <input name="[+slug_prefix+]_options[all_fields_support]" id="[+slug_prefix+]_options_all_fields_support" type="checkbox" [+all_fields_support_checked+] value="1" />
74
+ </td>
75
+ <td>
76
+ &nbsp;<strong>Enable All Fields name substitution</strong>
77
+ <div class="mla-settings-help">&nbsp;&nbsp;Check this option to use the above name for an "All Fields" search.</div>
78
+ </td>
79
+ </tr>
80
+
81
+
82
+ <!-- template="general-tab" -->
83
+ <style type='text/css'>
84
+ .mla-settings-help {
85
+ font-size: 8pt;
86
+ padding-bottom: 5px
87
+ }
88
+
89
+ .mla-page-level-options-form {
90
+ margin-left: 0px;
91
+ margin-top: 10px;
92
+ padding-bottom: 10px;
93
+ border-bottom:thin solid #888888;
94
+ }
95
+
96
+ span.submit.mla-settings-submit,
97
+ p.submit.mla-settings-submit {
98
+ padding-bottom: 0px
99
+ }
100
+ </style>
101
+ <h2>Plugin Options</h2>
102
+ <p>In this tab you can add or remove custom field search support for the Media/Assistant admin submenu and/or the Media Manager Modal (popup) Window (MMMW). The MMMW is used by the Media/Library grid view, the classic "Add Media..." function and the Gutenberg Image and Gallery blocks. You can leave these options checked unless you find a specific problem they cause (unlikely).</p>
103
+ <p>You can also specify one or more custom fields to search by default, i.e., without entering the field name(s) each time you perform a search. Finally, you can replace the default values used for the prefix that signals a custom field search and the special "field name" that specifies a search of all existing custom fields.</p>
104
+ <p>You can find more information about using all of the features of this plugin in the Documentation tab on this page.</p>
105
+ <div class="mla-page-level-options-form">
106
+ <form action="[+form_url+]" method="post" class="mla-display-settings-page" id="[+slug_prefix+]_options_general_tab">
107
+ <table class="optiontable">
108
+ <tbody>
109
+ [+options_list+]
110
+ </tbody>
111
+ </table>
112
+ <span class="submit mla-settings-submit">
113
+ <input name="[+slug_prefix+]_options_save" class="button-primary" id="[+slug_prefix+]_options_save" type="submit" value="Save Changes" />
114
+ <input name="[+slug_prefix+]_options_reset" class="button-primary alignright" id="[+slug_prefix+]_options_reset" type="submit" value="Delete Settings, Restore Defaults" />
115
+ </span>
116
+ [+_wpnonce+]
117
+ </form>
118
+ </div>
119
+
120
+ <!-- template="documentation-tab" -->
121
+ <style type='text/css'>
122
+ .mla-doc-toc-list {
123
+ list-style-position:inside;
124
+ list-style:disc;
125
+ line-height: 15px;
126
+ padding-left: 20px
127
+ }
128
+
129
+ .mla-doc-hook-label {
130
+ text-align: right;
131
+ padding: 0 1em 2em 0;
132
+ vertical-align: top;
133
+ font-weight:bold
134
+ }
135
+
136
+ .mla-doc-hook-definition {
137
+ vertical-align: top;
138
+ }
139
+
140
+ .mla-doc-table-label {
141
+ text-align: right;
142
+ padding-right: 10px;
143
+ vertical-align: top;
144
+ font-weight:bold
145
+ }
146
+
147
+ .mla-doc-table-sublabel {
148
+ padding-right: 10px;
149
+ vertical-align: top
150
+ }
151
+
152
+ .mla-doc-table-reverse {
153
+ text-align: right;
154
+ padding-right: 10px;
155
+ vertical-align:top
156
+ }
157
+
158
+ .mla-doc-table-definition {
159
+ vertical-align: top;
160
+ }
161
+
162
+ .mla-doc-bold-link {
163
+ font-size:14px;
164
+ font-weight:bold
165
+ }
166
+ </style>
167
+ <h2>Plugin Documentation</h2>
168
+ <p>In this tab, jump to:</p>
169
+ <div class="mla-display-settings-page" id="[+slug_prefix+]_options_documentation_tab" style="width:700px">
170
+ <ul class="mla-doc-toc-list">
171
+ <li><a href="#introduction"><strong>Introduction</strong></a></li>
172
+ <li><a href="#processing"><strong>How the Plugin Works</strong></a></li>
173
+ <li><a href="#composing_queries"><strong>Composing a Custom Field Query</strong></a></li>
174
+ <li><a href="#multiple_values"><strong>Searching for Multiple Values</strong></a></li>
175
+ <li><a href="#wildcards"><strong>Searching Partial Values; Wildcards</strong></a></li>
176
+ <li><a href="#multiple_fields"><strong>Searching in Multiple Fields or All Fields</strong></a></li>
177
+ <li><a href="#non_null_searches"><strong>Searching for the Presence of Any Value</strong></a></li>
178
+ <li><a href="#null_searches"><strong>Searching for the Absence of Any Value</strong></a></li>
179
+ <li><a href="#debugging"><strong>Debugging and Troubleshooting</strong></a></li>
180
+ </ul>
181
+ <p>
182
+ &nbsp;
183
+ <a name="introduction"></a>
184
+ </p>
185
+ <p>
186
+ <a href="#backtotop">Go to Top</a>
187
+ </p>
188
+ <h3>Introduction</h3>
189
+ <p>
190
+ The MLA "Search Media" text box lets you search for keywords on the Media/Assistant admin submenu and in the Media Manager Modal (popup) Window. This example plugin lets you search custom field content from those same text boxes. The example plugin was originally developed in response to this MLA support topic:
191
+ </p>
192
+ <ul class="mla-doc-toc-list">
193
+ <li><a href="https://wordpress.org/support/topic/searching-on-custom-fields/" title="View the topic" target="_blank">Searching on custom fields</a></li>
194
+ </ul>
195
+ <p>
196
+ The current version, with enhanced search features and these Settings tabs was inspried by this MLA support topic:
197
+ </p>
198
+ <ul class="mla-doc-toc-list">
199
+ <li><a href="https://wordpress.org/support/topic/search-media-by-custom-field/" title="View the topic" target="_blank">Search Media by Custom Field</a></li>
200
+ </ul>
201
+ <p>
202
+ To use the plugin you must configure the options on the General tab, including the field name(s) to be searched by default. Once the settings are in place you simply use the custom field prefix to specify that the search be handled by this example plugin.
203
+ </p>
204
+ <p>
205
+ More details on composing and running searches are in the sections of this Documentation page.
206
+ <a name="processing"></a>
207
+ </p>
208
+ <p>
209
+ <a href="#backtotop">Go to Top</a>
210
+ </p>
211
+ <h3>How the Plugin Works</h3>
212
+ <p>
213
+ The example plugin makes no changes or additions to the MLA core code; it hooks some of the actions and filters MLA provides. The plugin works by detecting the presence of the <code>custom:</code> prefix (or the prefix set on the General tab) in the Search Media text. When the prefix is present, the plugin replaces the standard keyword(s) search with a custom field query. The example plugin uses MLA's existing "Table View" feature as described in the "Library Views/Post MIME Type Processing" section of the Settings/Media Library Assistant Documentation tab to compose and execute the custom field query.</p>
214
+ <p>
215
+ The outline that follows is somewhat technical, but should give you an idea of the sequence of events and actions that allow the plugin to do its work. When the post/page is loaded this plugin is initialized, setting up to five MLA "hooks" that may be called to start the plugin&rsquo;s processing. If the General tab "Enable Media/Assistant searches" box is checked three hooks are set:
216
+ </p>
217
+ <ul class="mla-doc-toc-list">
218
+ <li><strong>mla_list_table_new_instance</strong> - called at the start of composing the Media/Assistant submenu page. If the custom search prefix is found in the Search Media text, the search specification is saved for processing in the next step.</li>
219
+ <li><strong>mla_list_table_query_final_terms</strong> - called just before the database is queried to find items for the submenu table display. If the search specification is present it is translated to a custom field query and added to the query arguments.</li>
220
+ <li><strong>mla_list_table_submenu_arguments</strong> - called when the submenu table navigation elements are composed. If the search specification is present it is added to the pagination links in the table header and footer areas.</li>
221
+ </ul>
222
+ <p>
223
+ The General tab "Enable Media Manager searches" box adds the plugin's features to the Media/Library Grid view and the Media Manager Modal (popup) Window (MMMW). If the box is checked two hooks are set:
224
+ </p>
225
+ <ul class="mla-doc-toc-list">
226
+ <li><strong>mla_media_modal_query_initial_terms</strong> - called at the start of the AJAX request that will fill the MMMW's item grid. If the custom search prefix is found in the "query_attachments" Search Media text, the search specification is saved for processing in the next step.</li>
227
+ <li><strong>mla_media_modal_query_final_terms</strong> - called just before the database is queried to find items for the MMMW item grid. If the search specification is present it is translated to a custom field query and added to the query arguments.</li>
228
+ </ul>
229
+ <p>
230
+ Once the custom field query is added to the database query arguments the example plugin's job is done and processing proceeds normally.
231
+ <a name="composing_queries"></a>
232
+ </p>
233
+ <p>
234
+ <a href="#backtotop">Go to Top</a>
235
+ </p>
236
+ <h3>Composing a Custom Field Query</h3>
237
+ <p>
238
+ A custom field query has four parts:
239
+ </p>
240
+ <ol>
241
+ <li>A prefix, "custom:" by default, or whatever you set on the General tab.</li>
242
+ <li>A comma-separated list of one or more custom field names.</li>
243
+ <li>An equals sign ("="), to divide the field names from the values</li>
244
+ <li>A comma-separated list of one or more values</li>
245
+ </ol>
246
+ <p>
247
+ So, for example, if you have a custom field named "Kingdom" with values of "Animal", "Mineral" and "Vegetable" you can compose a search like:
248
+ </p>
249
+ <ul class="mla-doc-toc-list">
250
+ <li><code>custom:Kingdom=Animal</code></li>
251
+ </ul>
252
+ <p>
253
+ It is important to note that custom field values are somewhat different from the keywords and phrases used in the standard Search Media searches. There is no "and/or" option or matching on one of the words you enter. If you enter "this example", the search will not match "this" or "example".
254
+ &nbsp;
255
+ <a name="multiple_values"></a>
256
+ </p>
257
+ <p>
258
+ <a href="#backtotop">Go to Top</a>
259
+ </p>
260
+ <h3>Searching for Multiple Values</h3>
261
+ <p>
262
+ If you want to search for more than one value, simply separate each value you want by a comma, such as:
263
+ </p>
264
+ <ul class="mla-doc-toc-list">
265
+ <li><code>custom:Kingdom=Animal,Mineral</code></li>
266
+ </ul>
267
+ <p>
268
+ &nbsp;
269
+ <a name="wildcards"></a>
270
+ </p>
271
+ <p>
272
+ <a href="#backtotop">Go to Top</a>
273
+ </p>
274
+ <h3>Searching Partial Values; Wildcards</h3>
275
+ <p>Wildcard specifications are also supported; for example:
276
+ <ul class="mla-doc-toc-list">
277
+ <li><code>custom:Kingdom=*al</code> to match anything ending in "al", e.g., "Animal" and "Mineral"</li>
278
+ <li><code>custom:Kingdom=*get*</code> to match "Vegetable".</li>
279
+ <li><code>custom:Headline=*this*,*example*</code> will match values containing "this" or "example".</li>
280
+ <li>As explained below, a value of <code>custom:Kingdom=*</code> will match any non-NULL value for a custom field.</li>
281
+ </ul>
282
+ </p>
283
+ <p>
284
+ &nbsp;
285
+ <a name="multiple_fields"></a>
286
+ </p>
287
+ <p>
288
+ <a href="#backtotop">Go to Top</a>
289
+ </p>
290
+ <h3>Searching in Multiple Fields or All Fields</h3>
291
+ If you want to search for the same value(s) in more than one custom field, simply separate each field name you want to search in by a comma, such as:
292
+ </p>
293
+ <ul class="mla-doc-toc-list">
294
+ <li><code>custom:Artist,Patron=smith</code></li>
295
+ <li><code>custom:Artist,Patron=smith,jones</code></li>
296
+ </ul>
297
+ <p>
298
+ &nbsp;
299
+ <a name="non_null_searches"></a>
300
+ </p>
301
+ <p>
302
+ <a href="#backtotop">Go to Top</a>
303
+ </p>
304
+ <h3>Searching for the Presence of Any Value</h3>
305
+ <p>
306
+ To return all items that have a non-NULL value in the field, enter the custom field name and then "=*". You can also enter the prefix "custom:" followed by just the custom field name(s). For example, <code>custom:My Featured Items</code>. For example:
307
+ </p>
308
+ <ul class="mla-doc-toc-list">
309
+ <li><code>custom:Kingdom=*</code></li>
310
+ <li><code>custom:Artist,Patron</code></li>
311
+ </ul>
312
+ <p>
313
+ &nbsp;
314
+ <a name="null_searches"></a>
315
+ </p>
316
+ <p>
317
+ <a href="#backtotop">Go to Top</a>
318
+ </p>
319
+ <h3>Searching for the Absence of Any Value</h3>
320
+ <p>
321
+ To return all items that have a NULL value in the field, enter the prefix "custom:" followed by the custom field name(s) and then "=". You can also enter a single custom field name (exactly one name) and then ",null".</p>
322
+ <ul class="mla-doc-toc-list">
323
+ <li><code>custom:Artist,Patron=</code></li>
324
+ <li><code>custom:Kingdom,null</code></li>
325
+ </ul>
326
+ <p>
327
+ <a name="debugging"></a>
328
+ </p>
329
+ <p>
330
+ <a href="#backtotop">Go to Top</a>
331
+ </p>
332
+ <h3>Debugging and Troubleshooting</h3>
333
+ <p>
334
+ If you are not getting the results you expect from a custom field search carefully inspecting the results of parsing the specification and generating the query can be a valuable exercise. These queries follow different rules than the standard keyword search rules, and you may have to adjust the query a few times to get the expected results.
335
+ </p>
336
+ <p>
337
+ For a quick look at the plugin's operation on a given search you can add a debugging prefix to the search text. There are two debugging prefixes:
338
+ </p>
339
+ <ul class="mla-doc-toc-list">
340
+ <li><strong><code>'}|{'</code></strong> - Write debug information to the console, e.g., <code>}|{custom:Kingdom=*</code>. This option writes log entries as PHP Warnings, which might be displayed in the browser window or written to the error log depending on how your site is configured. It's quick and easy but the results are ugly. Also, it will not work for the Media/Library Grid view or the MMMW; information will go to the log for these cases.</li>
341
+ <li><strong><code>'}|{'</code></strong> - Write debug information to the error log, e.g., <code>}|{custom:Artist,Patron</code>. This option avoids cluttering the display with ugly messages but requires you to find a view the error log file to see the results. The MLA Debug tab may be an easy way to find and view the log.</li>
342
+ </ul>
343
+ <p>
344
+ </p>
345
+ <p>
346
+ If a problem persists you can activate additional MLA debug logging, run a test and inspect the log file for more information about what's going on. To activate MLA&rsquo;s debug logging:
347
+ </p>
348
+ <ol>
349
+ <li>Navigate to the Settings/Media Library Assistant Debug tab.</li>
350
+ <li>Scroll down to the &ldquo;MLA Reporting&rdquo; text box and enter &ldquo;0x3&rdquo;. This will turn on MLA debug logging for the example plugin and AJAX operations (such as "query_attachments").</li>
351
+ <li>Click the Save Changes button to record your new setting.</li>
352
+ <li>Optionally, scroll to the bottom of the screen and click &ldquo;Reset&rdquo; to clear the error log. You may not want to do this depending on how you manage your error log.</li>
353
+ </ol>
354
+ <p>
355
+ Once that&rsquo;s done you can run a test. The debug log can be very detailed, so restricting the test as best you can will be very helpful. One way to do that:
356
+ </p>
357
+ <ol>
358
+ <li>Go to the Media/Assistant admin submenu table.</li>
359
+ <li>Enter your custom field search specification in the Search Media text box. You do not need to add either of the above debugging prefixes for this test.</li>
360
+ <li>Click the Search Media text box to filter the display.</li>
361
+ </ol>
362
+ <p>
363
+ When you&rsquo;ve finished testing, go back to the Debug screen and:
364
+ </p>
365
+ <ol>
366
+ <li>Enter &ldquo;0&rdquo; in the MLA Reporting text box to turn debug logging off.</li>
367
+ <li>Click the Save Changes button to record your new setting.</li>
368
+ <li>Scroll to the bottom and click &ldquo;Download&rdquo; to get the log content in a text file.</li>
369
+ <li>Optionally, scroll to the bottom of the screen and click &ldquo;Reset&rdquo; to clear the error log.</li>
370
+ </ol>
371
+ <p>
372
+ There may be a lot of messages written to the log, so limit the amount of activity during the logging period. You should see messages in the log like these:
373
+ </p>
374
+ <blockquote>
375
+ [28-Dec-2020 05:38:55 UTC] 610 MLACore::mla_plugins_loaded_action() MLA 2.94 mla_debug_level 0x3<br />
376
+ [28-Dec-2020 05:38:55 UTC] 37 MLA_Ajax::initialize( true ) $_REQUEST = array (
377
+ 'action' => 'query-attachments',
378
+ 'post_id' => '7283',
379
+ 'query' => <br />
380
+ </blockquote>
381
+ <p>
382
+ Of course, the details will be different. If you discover a defect in the plugin (or in MLA) you can <a href="http://wordpress.org/support/plugin/media-library-assistant" target="_blank">open a support topic</a> or <a href="http://davidlingren.com/#two" target="_blank">contact me at my web site</a> so it can be investigated further. I may ask for a copy of the log file from your tests.
383
+ </p>
384
+ </div>
examples/plugins/mla-custom-field-search-example/class-mla-example-plugin-settings.php ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Provides option management and Settings/ General and Documentation submenu pages
4
+ *
5
+ * This file might be shared among multiple example plugins, so load it with:
6
+ *
7
+ * if ( ! class_exists( 'MLAExamplePluginSettings' ) ) {
8
+ * require_once( pathinfo( __FILE__, PATHINFO_DIRNAME ) . '/class-mla-example-plugin-settings.php' );
9
+ * }
10
+ *
11
+ * @package Media Library Assistant
12
+ * @since 1.00
13
+ */
14
+
15
+ /**
16
+ * Class MLA Example Settings Menu adds Settings/ General and Documentation submenu pages to an example plugin
17
+ *
18
+ * @package MLA Example Settings Menu
19
+ * @since 1.00
20
+ */
21
+ class MLAExamplePluginSettings {
22
+ /**
23
+ * Default values for the __construct function
24
+ *
25
+ * @since 1.00
26
+ *
27
+ * @var array
28
+ */
29
+ private static $default_arguments = array(
30
+ 'slug_prefix' => 'example-plugin',
31
+ 'plugin_title' => 'The Example Plugin',
32
+ 'menu_title' => 'Example Plugin',
33
+ 'plugin_file_name_only' => 'the-example-plugin',
34
+ 'plugin_version' => '1.00',
35
+ 'template_file' => 'absolute path to the template file', // e.g., dirname( __FILE__ ) . '/admin-settings-page.tpl'
36
+ 'options' => array( 'slug' => array( 'type' => 'text|checkbox', 'default' => 'text|boolean' ) ),
37
+ 'general_tab_values' => array(), // additional page_values for 'page-level-options' template
38
+ 'documentation_tab_values' => array(), // page_values for 'documentation-tab' template
39
+ );
40
+
41
+ /**
42
+ * Current values for this object instance
43
+ *
44
+ * @since 1.00
45
+ *
46
+ * @var array
47
+ */
48
+ private $current_arguments = array();
49
+
50
+ /**
51
+ * This function sets option definitions and installs filters.
52
+ *
53
+ * @since 1.00
54
+ *
55
+ * @param array $attr Option definitions and settings
56
+ */
57
+ public function __construct( $attr ) {
58
+ // Make sure $attr is an array, even if it's empty
59
+ if ( empty( $attr ) ) {
60
+ $attr = array();
61
+ } elseif ( is_string( $attr ) ) {
62
+ $attr = shortcode_parse_atts( $attr );
63
+ }
64
+
65
+ // Accept only the attributes we need and supply defaults
66
+ $this->current_arguments = shortcode_atts( self::$default_arguments, $attr );
67
+
68
+ // Compile the default settings
69
+ foreach ( $this->current_arguments['options'] as $slug => $option ) {
70
+ $this->default_settings[ $slug ] = $option['default'];
71
+ }
72
+
73
+ if ( is_admin() ) {
74
+ // Add submenu page in the "Settings" section
75
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Add submenu page in the "Settings" section
81
+ *
82
+ * @since 1.00
83
+ */
84
+ public function admin_menu() {
85
+ /*
86
+ * We need a tab-specific page ID to manage the screen options on the General tab.
87
+ * Use the URL suffix, if present. If the URL doesn't have a tab suffix, use '-general'.
88
+ * This hack is required to pass the WordPress "referer" validation.
89
+ */
90
+ if ( isset( $_REQUEST['page'] ) && is_string( $_REQUEST['page'] ) && ( $this->current_arguments['slug_prefix'] . '-settings-' == substr( $_REQUEST['page'], 0, strlen( $this->current_arguments['slug_prefix'] . '-settings-' ) ) ) ) {
91
+ $tab = substr( $_REQUEST['page'], strlen( $this->current_arguments['slug_prefix'] . '-settings-' ) );
92
+ } else {
93
+ $tab = 'general';
94
+ }
95
+
96
+ $tab = $this->_get_options_tablist( $tab ) ? '-' . $tab : '-general';
97
+ add_submenu_page( 'options-general.php', $this->current_arguments['plugin_title'], $this->current_arguments['menu_title'], 'manage_options', $this->current_arguments['slug_prefix'] . '-settings' . $tab, array( $this, 'add_submenu_page' ) );
98
+ add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
99
+ }
100
+
101
+ /**
102
+ * Add the "Settings" and "Guide" links to the Plugins section entry
103
+ *
104
+ * @since 1.00
105
+ *
106
+ * @param array array of links for the Plugin, e.g., "Activate"
107
+ * @param string Directory and name of the plugin Index file
108
+ *
109
+ * @return array Updated array of links for the Plugin
110
+ */
111
+ public function plugin_action_links( $links, $file ) {
112
+ if ( $file == $this->current_arguments['plugin_file_name_only'] . '/' . $this->current_arguments['plugin_file_name_only'] . '.php' ) {
113
+ $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'options-general.php?page=' . $this->current_arguments['slug_prefix'] . '-settings-documentation&mla_tab=documentation' ), 'Guide' );
114
+ array_unshift( $links, $settings_link );
115
+ $settings_link = sprintf( '<a href="%s">%s</a>', admin_url( 'options-general.php?page=' . $this->current_arguments['slug_prefix'] . '-settings-general' ), 'Settings' );
116
+ array_unshift( $links, $settings_link );
117
+ }
118
+
119
+ return $links;
120
+ }
121
+
122
+ /**
123
+ * Render (echo) the example plugin's submenu in the Settings section
124
+ *
125
+ * @since 1.00
126
+ *
127
+ * @return void Echoes HTML markup for the submenu page
128
+ */
129
+ public function add_submenu_page() {
130
+ if ( !current_user_can( 'manage_options' ) ) {
131
+ echo '<h2>' . $this->current_arguments['plugin_title'] . " - Error</h2>\n";
132
+ wp_die( 'You do not have permission to manage plugin settings.' );
133
+ }
134
+
135
+ // Load template array and initialize page-level values.
136
+ $this->page_template_array = MLACore::mla_load_template( $this->current_arguments['template_file'], 'path' );
137
+ $current_tab_slug = isset( $_REQUEST['mla_tab'] ) ? $_REQUEST['mla_tab']: 'general';
138
+ $current_tab = $this->_get_options_tablist( $current_tab_slug );
139
+ $page_values = array(
140
+ 'plugin_title' => $this->current_arguments['plugin_title'],
141
+ 'version' => 'v' . $this->current_arguments['plugin_version'],
142
+ 'messages' => '',
143
+ 'tablist' => $this->_compose_settings_tabs( $current_tab_slug ),
144
+ 'tab_content' => '',
145
+ );
146
+
147
+ // Compose tab content
148
+ if ( $current_tab ) {
149
+ if ( isset( $current_tab['render'] ) ) {
150
+ $handler = $current_tab['render'];
151
+ $page_content = call_user_func( array( $this, $handler ) );
152
+ } else {
153
+ $page_content = array( 'message' => "ERROR: Cannot render content tab {$current_tab_slug}", 'body' => '' );
154
+ }
155
+ } else {
156
+ $page_content = array( 'message' => "ERROR: Unknown content tab {$current_tab_slug}", 'body' => '' );
157
+ }
158
+
159
+ if ( ! empty( $page_content['message'] ) ) {
160
+ if ( false !== strpos( $page_content['message'], 'ERROR' ) ) {
161
+ $messages_class = 'updated error';
162
+ } else {
163
+ $messages_class = 'updated notice is-dismissible';
164
+ }
165
+
166
+ $page_values['messages'] = MLAData::mla_parse_template( $this->page_template_array['messages'], array(
167
+ 'mla_messages_class' => $messages_class ,
168
+ 'messages' => $page_content['message'],
169
+ ) );
170
+ }
171
+
172
+ $page_values['tab_content'] = $page_content['body'];
173
+
174
+ echo MLAData::mla_parse_template( $this->page_template_array['page'], $page_values );
175
+ }
176
+
177
+ /**
178
+ * Template file for the Settings page(s) and parts
179
+ *
180
+ * This array contains all of the template parts for the Settings page(s). The array is built once
181
+ * each page load and cached for subsequent use.
182
+ *
183
+ * @since 1.00
184
+ *
185
+ * @var array
186
+ */
187
+ private $page_template_array = NULL;
188
+
189
+ /**
190
+ * Definitions for Settings page tab ids, titles and handlers
191
+ * Each tab is defined by an array with the following elements:
192
+ *
193
+ * array key => HTML id/name attribute and option database key (OMIT MLA_OPTION_PREFIX)
194
+ *
195
+ * title => tab label / heading text
196
+ * render => rendering function for tab messages and content. Usage:
197
+ * $tab_content = ['render']();
198
+ *
199
+ * @since 1.00
200
+ *
201
+ * @var array
202
+ */
203
+ private $mla_tablist = array(
204
+ 'general' => array( 'title' => 'General', 'render' => '_compose_general_tab' ),
205
+ 'documentation' => array( 'title' => 'Documentation', 'render' => '_compose_documentation_tab' ),
206
+ );
207
+
208
+ /**
209
+ * Retrieve the list of options tabs or a specific tab value
210
+ *
211
+ * @since 1.00
212
+ *
213
+ * @param string Tab slug, to retrieve a single entry
214
+ *
215
+ * @return array|false The entire tablist ( $tab = NULL ), a single tab entry or false if not found/not allowed
216
+ */
217
+ private function _get_options_tablist( $tab = NULL ) {
218
+ if ( is_string( $tab ) ) {
219
+ if ( isset( $this->mla_tablist[ $tab ] ) ) {
220
+ $results = $this->mla_tablist[ $tab ];
221
+ } else {
222
+ $results = false;
223
+ }
224
+ } else {
225
+ $results = $this->mla_tablist;
226
+ }
227
+
228
+ return $results;
229
+ }
230
+
231
+ /**
232
+ * Compose the navigation tabs for the Settings subpage
233
+ *
234
+ * @since 1.00
235
+ * @uses $page_template_array contains tablist and tablist-item templates
236
+ *
237
+ * @param string Optional data-tab-id value for the active tab, default 'general'
238
+ *
239
+ * @return string HTML markup for the Settings subpage navigation tabs
240
+ */
241
+ private function _compose_settings_tabs( $active_tab = 'general' ) {
242
+ $tablist_item = $this->page_template_array['tablist-item'];
243
+ $tabs = '';
244
+ foreach ( $this->_get_options_tablist() as $key => $item ) {
245
+ $item_values = array(
246
+ 'data-tab-id' => $key,
247
+ 'nav-tab-active' => ( $active_tab == $key ) ? 'nav-tab-active' : '',
248
+ 'settings-page' => $this->current_arguments['slug_prefix'] . '-settings-' . $key,
249
+ 'title' => $item['title']
250
+ );
251
+
252
+ $tabs .= MLAData::mla_parse_template( $tablist_item, $item_values );
253
+ } // foreach $item
254
+
255
+ $tablist_values = array( 'tablist' => $tabs );
256
+ return MLAData::mla_parse_template( $this->page_template_array['tablist'], $tablist_values );
257
+ }
258
+
259
+ /**
260
+ * Compose the General tab content for the Settings subpage
261
+ *
262
+ * @since 1.00
263
+ * @uses $page_template_array contains tab content template(s)
264
+ *
265
+ * @return array 'message' => status/error messages, 'body' => tab content
266
+ */
267
+ private function _compose_general_tab() {
268
+ $page_content = array( 'message' => '', 'body' => '' );
269
+
270
+ // Check for page-level Save Changes, Restore Defaults
271
+ if ( !empty( $_REQUEST[ $this->current_arguments['slug_prefix'] . '_options_save'] ) ) {
272
+ check_admin_referer( MLACore::MLA_ADMIN_NONCE_ACTION, MLACore::MLA_ADMIN_NONCE_NAME );
273
+ $page_content = $this->_save_setting_changes();
274
+ } elseif ( !empty( $_REQUEST[ $this->current_arguments['slug_prefix'] . '_options_reset'] ) ) {
275
+ check_admin_referer( MLACore::MLA_ADMIN_NONCE_ACTION, MLACore::MLA_ADMIN_NONCE_NAME );
276
+ $page_content = $this->_restore_setting_defaults();
277
+ }
278
+
279
+ if ( !empty( $page_content['body'] ) ) {
280
+ return $page_content;
281
+ }
282
+
283
+ // Display the General tab
284
+ $_SERVER['REQUEST_URI'] = remove_query_arg( array(
285
+ $this->current_arguments['slug_prefix'] . '_options',
286
+ '_wpnonce',
287
+ '_wp_http_referer',
288
+ $this->current_arguments['slug_prefix'] . '_options_save',
289
+ $this->current_arguments['slug_prefix'] . '_options_reset',
290
+ ), $_SERVER['REQUEST_URI'] );
291
+
292
+ // Compose page-level options
293
+ $page_values = $this->current_arguments['general_tab_values'];
294
+
295
+ foreach ( $this->current_arguments['options'] as $slug => $option ) {
296
+ if ( 'checkbox' === $option['type'] ) {
297
+ $page_values[ $slug . '_checked' ] = $this->get_plugin_option( $slug ) ? 'checked="checked" ' : '';
298
+ } else {
299
+ $page_values[ $slug ] = $this->get_plugin_option( $slug );
300
+ }
301
+ }
302
+ //error_log( __LINE__ . ' MLAExamplePluginSettings::_compose_general_tab page_values = ' . var_export( $page_values, true ), 0 );
303
+
304
+ $options_list = MLAData::mla_parse_template( $this->page_template_array['page-level-options'], $page_values );
305
+
306
+ $form_arguments = '?page=' . $this->current_arguments['slug_prefix'] . '-settings-general&mla_tab=general';
307
+
308
+ $page_values = array(
309
+ 'form_url' => admin_url( 'options-general.php' ) . $form_arguments,
310
+ '_wpnonce' => wp_nonce_field( MLACore::MLA_ADMIN_NONCE_ACTION, MLACore::MLA_ADMIN_NONCE_NAME, true, false ),
311
+ 'options_list' => $options_list,
312
+ 'slug_prefix' => $this->current_arguments['slug_prefix'],
313
+ );
314
+
315
+ $page_content['body'] .= MLAData::mla_parse_template( $this->page_template_array['general-tab'], $page_values );
316
+
317
+ return $page_content;
318
+ }
319
+
320
+ /**
321
+ * Compose the Documentation tab content for the Settings subpage
322
+ *
323
+ * @since 1.00
324
+ * @uses $page_template_array contains tab content template(s)
325
+ *
326
+ * @return array 'message' => status/error messages, 'body' => tab content
327
+ */
328
+ private function _compose_documentation_tab() {
329
+ $page_content = array( 'message' => '', 'body' => '' );
330
+ $page_values = array(
331
+ );
332
+
333
+ $page_content['body'] = MLAData::mla_parse_template( $this->page_template_array['documentation-tab'], $this->current_arguments['documentation_tab_values'] );
334
+ return $page_content;
335
+ }
336
+
337
+ /**
338
+ * Save settings as a WordPress wp_options entry
339
+ *
340
+ * @since 1.00
341
+ *
342
+ * @return array 'message' => status/error messages, 'body' => tab content
343
+ */
344
+ private function _save_setting_changes() {
345
+ $page_content = array( 'message' => 'Settings unchanged.', 'body' => '' );
346
+
347
+ $changed = false;
348
+ foreach ( $this->current_arguments['options'] as $slug => $option ) {
349
+ if ( 'checkbox' === $option['type'] ) {
350
+ $changed |= $this->_update_plugin_option( $slug, isset( $_REQUEST[ $this->current_arguments['slug_prefix'] . '_options' ][ $slug ] ) );
351
+ } else {
352
+ if ( isset( $_REQUEST[ $this->current_arguments['slug_prefix'] . '_options' ][ $slug ] ) ) {
353
+ $changed |= $this->_update_plugin_option( $slug, $_REQUEST[ $this->current_arguments['slug_prefix'] . '_options' ][ $slug ] );
354
+ } else {
355
+ $changed |= $this->_update_plugin_option( $slug, $option['default'] );
356
+ }
357
+ }
358
+ } // foreach option
359
+
360
+ if ( $changed ) {
361
+ // No reason to save defaults in the database
362
+ if ( $this->current_settings === $this->default_settings ) {
363
+ delete_option( $this->current_arguments['slug_prefix'] . '-settings' );
364
+ } else {
365
+ $changed = update_option( $this->current_arguments['slug_prefix'] . '-settings', $this->current_settings, false );
366
+ }
367
+
368
+ if ( $changed ) {
369
+ $page_content['message'] = "Settings have been updated.";
370
+ } else {
371
+ $page_content['message'] = "Settings updated failed.";
372
+ }
373
+ }
374
+
375
+ return $page_content;
376
+ } // _save_setting_changes
377
+
378
+ /**
379
+ * Delete the plugin's WordPress wp_options entry, restoring the default settings
380
+ *
381
+ * @since 1.00
382
+ *
383
+ * @return array 'message' => status/error messages, 'body' => tab content
384
+ */
385
+ private function _restore_setting_defaults() {
386
+ $page_content = array( 'message' => 'Settings unchanged.', 'body' => '' );
387
+ $this->current_settings = $this->default_settings;
388
+ $changed = delete_option( $this->current_arguments['slug_prefix'] . '-settings' );
389
+
390
+ if ( $changed ) {
391
+ $page_content['message'] = "Settings have been updated.";
392
+ }
393
+
394
+ return $page_content;
395
+ } // _restore_setting_defaults
396
+
397
+ /**
398
+ * Assemble the in-memory representation of the plugin settings
399
+ *
400
+ * @since 1.00
401
+ *
402
+ * @param boolean $force_refresh Optional. Force a reload of rules. Default false.
403
+ *
404
+ * @return boolean Success (true) or failure (false) of the operation
405
+ */
406
+ private function _get_plugin_settings( $force_refresh = false ) {
407
+ if ( false == $force_refresh && NULL != $this->current_settings ) {
408
+ return true;
409
+ }
410
+
411
+ // Update the plugin options from the wp_options table or set defaults
412
+ $this->current_settings = get_option( $this->current_arguments['slug_prefix'] . '-settings' );
413
+ if ( !is_array( $this->current_settings ) ) {
414
+ $this->current_settings = $this->default_settings;
415
+ }
416
+
417
+ // Initialize any new setting(s) from the default settings
418
+ foreach ( $this->current_arguments['options'] as $slug => $option ) {
419
+ if ( !isset( $this->current_settings[ $slug ] ) ) {
420
+ $this->current_settings[ $slug ] = $option['default'];
421
+ }
422
+ }
423
+
424
+ return true;
425
+ }
426
+
427
+ /**
428
+ * In-memory representation of the option settings
429
+ *
430
+ * @since 1.00
431
+ *
432
+ * @var array
433
+ */
434
+ private $current_settings = NULL;
435
+
436
+ /**
437
+ * Default processing options
438
+ *
439
+ * @since 1.00
440
+ *
441
+ * @var array
442
+ */
443
+ private $default_settings = array();
444
+
445
+ /**
446
+ * Get a plugin option setting
447
+ *
448
+ * @since 1.00
449
+ *
450
+ * @param string $name Option name
451
+ *
452
+ * @return mixed Option value, if it exists else NULL
453
+ */
454
+ public function get_plugin_option( $name ) {
455
+ if ( !$this->_get_plugin_settings() ) {
456
+ return NULL;
457
+ }
458
+
459
+ if ( !isset( $this->current_settings[ $name ] ) ) {
460
+ return NULL;
461
+ }
462
+
463
+ return $this->current_settings[ $name ];
464
+ }
465
+
466
+ /**
467
+ * Update a plugin option setting
468
+ *
469
+ * @since 1.00
470
+ *
471
+ * @param string $name Option name
472
+ * @param mixed $new_value Option value
473
+ *
474
+ * @return mixed True if option value changed, false if value unchanged, NULL if failure
475
+ */
476
+ private function _update_plugin_option( $name, $new_value ) {
477
+ if ( !$this->_get_plugin_settings() ) {
478
+ return NULL;
479
+ }
480
+
481
+ $old_value = isset( $this->current_settings[ $name ] ) ? $this->current_settings[ $name ] : NULL;
482
+
483
+ if ( $new_value === $old_value ) {
484
+ return false;
485
+ }
486
+
487
+ $this->current_settings[ $name ] = $new_value;
488
+ return true;
489
+ }
490
+ } // Class MLAExamplePluginSettings
491
+ ?>
examples/plugins/mla-custom-field-search-example/mla-custom-field-search-example.php ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Extends the Media/Assistant "Search Media" box to custom field values
4
+ *
5
+ * In this example, a "custom:" prefix is detected in the Media/Assistant "search media" text
6
+ * box and the search is modified to query a custom field for a specific value, e.g.,
7
+ * "custom:photo reference=123456". You can also search for partial values:
8
+ *
9
+ * - To return all items that have a non-NULL value in the field, simply enter the prefix
10
+ * "custom:" followed by the custom field name, for example, custom:File Size. You can also
11
+ * enter the custom field name and then "=*", e.g., custom:File Size=*.
12
+ * - To return all items that have a NULL value in the field, enter the custom field name and
13
+ * then "=", e.g., custom:File Size=.
14
+ * - To return all items that match one or more values, enter the prefix "custom:" followed by
15
+ * the custom field name and then "=" followed by a list of values. For example, custom:Color=red
16
+ * or custom:Color=red,green,blue. Wildcard specifications are also supported; for example, "*post"
17
+ * to match anything ending in "post" or "th*da*" to match values like "the date" and "this day".
18
+ *
19
+ * This example plugin uses four of the many filters available in the Media/Assistant Submenu
20
+ * and illustrates some of the techniques you can use to customize the submenu table display.
21
+ *
22
+ * Created for support topic "Searching on custom fields"
23
+ * opened on 5/11/2015 by "BFI-WP".
24
+ * https://wordpress.org/support/topic/searching-on-custom-fields/
25
+ *
26
+ * Enhanced for support topic "Search Media by Custom Field"
27
+ * opened on 12/2/2020 by "icedarkness".
28
+ * https://wordpress.org/support/topic/search-media-by-custom-field/
29
+ *
30
+ * @package MLA Custom Field Search Example
31
+ * @version 1.06
32
+ */
33
+
34
+ /*
35
+ Plugin Name: MLA Custom Field Search Example
36
+ Plugin URI: http://davidlingren.com/
37
+ Description: Extends the Media/Assistant "Search Media" box to custom field values
38
+ Author: David Lingren
39
+ Version: 1.06
40
+ Author URI: http://davidlingren.com/
41
+
42
+ Copyright 2014 - 2020 David Lingren
43
+
44
+ This program is free software; you can redistribute it and/or modify
45
+ it under the terms of the GNU General Public License as published by
46
+ the Free Software Foundation; either version 2 of the License, or
47
+ (at your option) any later version.
48
+
49
+ This program is distributed in the hope that it will be useful,
50
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
51
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
52
+ GNU General Public License for more details.
53
+
54
+ You can get a copy of the GNU General Public License by writing to the
55
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
56
+ */
57
+
58
+ /**
59
+ * Class MLA Custom Field Search Example extends the Media/Assistant "Search Media" box
60
+ * to custom field values
61
+ *
62
+ * @package MLA Custom Field Search Example
63
+ * @since 1.00
64
+ */
65
+ class MLACustomFieldSearchExample {
66
+ /**
67
+ * Plugin version number for debug logging
68
+ *
69
+ * @since 1.01
70
+ *
71
+ * @var integer
72
+ */
73
+ const PLUGIN_VERSION = '1.06';
74
+
75
+ /**
76
+ * Constant to log this plugin's debug activity
77
+ *
78
+ * @since 1.06
79
+ *
80
+ * @var integer
81
+ */
82
+ const MLA_DEBUG_CATEGORY = 0x00008000;
83
+
84
+ /**
85
+ * Slug prefix for registering and enqueueing submenu pages, style sheets, scripts and settings
86
+ *
87
+ * @since 1.06
88
+ *
89
+ * @var string
90
+ */
91
+ const SLUG_PREFIX = 'mlacustomfieldsearch';
92
+
93
+ /**
94
+ * Configuration values for the Settings Management object
95
+ *
96
+ * @since 1.06
97
+ *
98
+ * @var array
99
+ */
100
+ private static $settings_arguments = array(
101
+ 'slug_prefix' => self::SLUG_PREFIX,
102
+ 'plugin_title' => 'MLA Custom Field Search Example',
103
+ 'menu_title' => 'MLA Custom Search',
104
+ 'plugin_file_name_only' => 'mla-custom-field-search-example',
105
+ 'plugin_version' => self::PLUGIN_VERSION,
106
+ 'template_file' => '/admin-settings-page.tpl', // Add the path at runtime, in initialize()
107
+ 'options' => array( // 'slug' => array( 'type' => 'text|checkbox', 'default' => 'text|boolean' )
108
+ 'media_assistant_support' =>array( 'type' => 'checkbox', 'default' => true ),
109
+ 'mmmw_support' =>array( 'type' => 'checkbox', 'default' => true ),
110
+ 'prefix' =>array( 'type' => 'text', 'default' => 'custom:' ),
111
+ 'default_fields' =>array( 'type' => 'text', 'default' => '' ),
112
+ 'all_fields' =>array( 'type' => 'text', 'default' => '*' ),
113
+ 'all_fields_support' =>array( 'type' => 'checkbox', 'default' => true ),
114
+ ),
115
+ 'general_tab_values' => array(), // additional page_values for 'page-level-options' template
116
+ 'documentation_tab_values' => array(
117
+ 'plugin_title' => 'MLA Custom Field Search Example',
118
+ ), // page_values for 'documentation-tab' template
119
+ );
120
+
121
+ /**
122
+ * Settings Management object
123
+ *
124
+ * @since 1.06
125
+ *
126
+ * @var array
127
+ */
128
+ private static $plugin_settings = NULL;
129
+
130
+ /**
131
+ * Initialization function, similar to __construct()
132
+ *
133
+ * @since 1.06
134
+ *
135
+ * @return void
136
+ */
137
+ public static function initialize() {
138
+ // This plugin requires MLA
139
+ if ( ! class_exists( 'MLACore', false ) ) {
140
+ return;
141
+ }
142
+
143
+ // The filters are only useful for the admin section; exit in the front-end posts/pages
144
+ if ( ! is_admin() ) {
145
+ return;
146
+ }
147
+
148
+ // The plugin settings class is shared with other MLA example plugins
149
+ if ( ! class_exists( 'MLAExamplePluginSettings' ) ) {
150
+ require_once( pathinfo( __FILE__, PATHINFO_DIRNAME ) . '/class-mla-example-plugin-settings.php' );
151
+ }
152
+
153
+ // Add the run-time values to the arguments
154
+ self::$settings_arguments['template_file'] = dirname( __FILE__ ) . self::$settings_arguments['template_file'];
155
+
156
+ // Create our own settings object
157
+ self::$plugin_settings = new MLAExamplePluginSettings( self::$settings_arguments );
158
+
159
+ if ( self::$plugin_settings->get_plugin_option( 'media_assistant_support' ) ) {
160
+ // Defined in /media-library-assistant/includes/class-mla-main.php
161
+ add_filter( 'mla_list_table_new_instance', 'MLACustomFieldSearchExample::mla_list_table_new_instance', 10, 1 );
162
+
163
+ // Defined in /media-library-assistant/includes/class-mla-data.php
164
+ add_filter( 'mla_list_table_query_final_terms', 'MLACustomFieldSearchExample::mla_list_table_query_final_terms', 10, 1 );
165
+
166
+ // Defined in /media-library-assistant/includes/class-mla-list-table.php
167
+ add_filter( 'mla_list_table_submenu_arguments', 'MLACustomFieldSearchExample::mla_list_table_submenu_arguments', 10, 2 );
168
+ }
169
+
170
+ if ( self::$plugin_settings->get_plugin_option( 'mmmw_support' ) ) {
171
+ // Defined in /media-library-assistant/includes/class-mla-media-modal.php
172
+ add_filter( 'mla_media_modal_query_initial_terms', 'MLACustomFieldSearchExample::mla_media_modal_query_initial_terms', 10, 2 );
173
+
174
+ // Defined in /media-library-assistant/includes/class-mla-data.php
175
+ add_filter( 'mla_media_modal_query_final_terms', 'MLACustomFieldSearchExample::mla_media_modal_query_final_terms', 10, 1 );
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Filter the URL parameters that will be retained when the submenu page refreshes
181
+ *
182
+ * @since 1.06
183
+ *
184
+ * @param array $submenu_arguments non-empty view, search, filter and sort arguments.
185
+ * @param boolean $include_filters Include the "click filter" values in the results.
186
+ */
187
+ public static function mla_list_table_submenu_arguments( $submenu_arguments, $include_filters ) {
188
+ if ( $include_filters ) {
189
+ if ( !empty( self::$custom_field_parameters['s'] ) ) {
190
+ $prefix = self::$plugin_settings->get_plugin_option( 'prefix' );
191
+ $submenu_arguments['s'] = $prefix . self::$custom_field_parameters['s'];
192
+ }
193
+ }
194
+
195
+ return $submenu_arguments;
196
+ }
197
+
198
+ /**
199
+ * Extend the MLA_List_Table class
200
+ *
201
+ * This filter gives you an opportunity to extend the MLA_List_Table class.
202
+ * You can also use this filter to inspect or modify any of the $_REQUEST arguments.
203
+ *
204
+ * @since 1.00
205
+ *
206
+ * @param object $mla_list_table NULL, to indicate no extension/use the base class.
207
+ *
208
+ * @return object updated mla_list_table object.
209
+ */
210
+ public static function mla_list_table_new_instance( $mla_list_table ) {
211
+ /*
212
+ * Look for the custom field search prefix in the Search Media text box,
213
+ * after checking for the "debug" prefixes.
214
+ */
215
+ if ( isset( $_REQUEST['s'] ) ) {
216
+ switch ( substr( $_REQUEST['s'], 0, 3 ) ) {
217
+ case '}|{':
218
+ self::$custom_field_parameters['debug'] = 'console';
219
+ $start = 3;
220
+ break;
221
+ case '{|}':
222
+ self::$custom_field_parameters['debug'] = 'log';
223
+ $start = 3;
224
+ break;
225
+ default:
226
+ $start = 0;
227
+ }
228
+
229
+ $prefix = self::$plugin_settings->get_plugin_option( 'prefix' );
230
+ if ( $prefix === substr( $_REQUEST['s'], $start, strlen( $prefix ) ) ) {
231
+ self::$custom_field_parameters['s'] = substr( $_REQUEST['s'], $start + strlen( $prefix ) );
232
+ unset( $_REQUEST['s'] );
233
+ //self::$custom_field_parameters['mla_search_connector'] = $_REQUEST['mla_search_connector'];
234
+ unset( $_REQUEST['mla_search_connector'] );
235
+ //self::$custom_field_parameters['mla_search_fields'] = $_REQUEST['mla_search_fields'];
236
+ unset( $_REQUEST['mla_search_fields'] );
237
+ } else {
238
+ self::$custom_field_parameters = array();
239
+ }
240
+ } // isset $_REQUEST['s']
241
+
242
+ return $mla_list_table;
243
+ } // mla_list_table_new_instance
244
+
245
+ /**
246
+ * Custom Field Search "parameters"
247
+ *
248
+ * @since 1.01
249
+ *
250
+ * @var array
251
+ */
252
+ public static $custom_field_parameters = array();
253
+
254
+ /**
255
+ * Filter the WP_Query request parameters for the prepare_items query
256
+ *
257
+ * Gives you an opportunity to change the terms of the prepare_items query
258
+ * after they are processed by the "Prepare List Table Query" handler.
259
+ *
260
+ * @since 1.01
261
+ *
262
+ * @param array WP_Query request prepared by "Prepare List Table Query"
263
+ *
264
+ * @return array updated WP_Query request
265
+ */
266
+ public static function mla_list_table_query_final_terms( $request ) {
267
+ /*
268
+ * If $request['offset'] and $request['posts_per_page'] are set, this is the "prepare_items" request.
269
+ * If they are NOT set, this is a "view count" request, i.e., to get the count for a custom view.
270
+ *
271
+ * MLAQuery::$query_parameters and MLAQuery::$search_parameters contain
272
+ * additional parameters used in some List Table queries.
273
+ */
274
+ if ( ! ( isset( $request['offset'] ) && isset( $request['posts_per_page'] ) ) ) {
275
+ return $request;
276
+ }
277
+
278
+ if ( empty( self::$custom_field_parameters ) ) {
279
+ return $request;
280
+ }
281
+
282
+ if ( isset( self::$custom_field_parameters['debug'] ) ) {
283
+ MLAQuery::$query_parameters['debug'] = self::$custom_field_parameters['debug'];
284
+ MLAQuery::$search_parameters['debug'] = self::$custom_field_parameters['debug'];
285
+ MLACore::mla_debug_mode( self::$custom_field_parameters['debug'] );
286
+ }
287
+
288
+ $specification = self::$custom_field_parameters['s'];
289
+
290
+ // Check for and reformat special "<field>,null" case
291
+ if ( ( strlen( $specification ) >= strlen(',null') ) && ( false !== strpos( $specification, ',null', -strlen(',null') ) ) ) {
292
+ $field = substr( $specification, 0, strlen( $specification ) - strlen(',null') );
293
+ $specification = $field . '=';
294
+ }
295
+
296
+ // Check for and reformat "field names only" case
297
+ if ( false === strpos( $specification, '=' ) ) {
298
+ $specification .= '=*';
299
+ }
300
+
301
+ // Apply default field name?
302
+ $default_fields = self::$plugin_settings->get_plugin_option( 'default_fields' );
303
+ if ( empty( $default_fields ) ) {
304
+ $default_fields = 'ERROR - No Default Field(s) Specified';
305
+ }
306
+
307
+ if ( '=' == substr( $specification, 0, 1 ) ) {
308
+ $tokens = array( $default_fields, substr( $specification, 1 ) );
309
+ } else {
310
+ $tokens = explode( '=', $specification ) ;
311
+ }
312
+
313
+ // See if the custom field name is present, followed by "=" and a value
314
+ if ( 1 < count( $tokens ) ) {
315
+ $field = array_shift( $tokens );
316
+ $value = implode( '=', $tokens );
317
+ } else {
318
+ // Supply a default custom field name
319
+ $field = $default_fields;
320
+ $value = $tokens[0];
321
+ }
322
+
323
+ // Look for substitute All Fields value
324
+ if ( self::$plugin_settings->get_plugin_option( 'all_fields_support' ) ) {
325
+ if ( $field === self::$plugin_settings->get_plugin_option( 'all_fields' ) ) {
326
+ $field = '*';
327
+ }
328
+ }
329
+
330
+ // Parse the query, remove MLA-specific elements, fix numeric and "commas" format fields
331
+ MLACore::mla_debug_add( __LINE__ . " MLACustomFieldSearchExample::mla_list_table_query_final_terms query = custom:{$field}={$value}" );
332
+ $tokens = MLACore::mla_prepare_view_query( 'custom_field_search', 'custom:' . $field . '=' . $value );
333
+ MLACore::mla_debug_add( __LINE__ . ' MLACustomFieldSearchExample::mla_list_table_query_final_terms tokens = ' . var_export( $tokens, true ) );
334
+ $tokens = $tokens['meta_query'];
335
+ MLACore::mla_debug_add( __LINE__ . ' MLACustomFieldSearchExample::mla_list_table_query_final_terms meta_query = ' . var_export( $tokens, true ) );
336
+
337
+ /*
338
+ * Matching a meta_value to NULL requires a LEFT JOIN to a view and a special WHERE clause;
339
+ * MLA filters will handle this case.
340
+ */
341
+ if ( isset( $tokens['key'] ) ) {
342
+ MLAQuery::$query_parameters['use_postmeta_view'] = true;
343
+ MLAQuery::$query_parameters['postmeta_key'] = $tokens['key'];
344
+ MLAQuery::$query_parameters['postmeta_value'] = NULL;
345
+ return $request;
346
+ }
347
+
348
+ // Process "normal" meta_query
349
+ $query = array( 'relation' => $tokens['relation'] );
350
+ $padded_values = array();
351
+ $patterns = array();
352
+ foreach ( $tokens as $key => $value ) {
353
+ // The key/value/compare elements are nested within the query array
354
+ if ( ! is_numeric( $key ) ) {
355
+ continue;
356
+ }
357
+
358
+ if ( !empty( $value['key'] ) && in_array( $value['key'], array( 'File Size', 'pixels', 'width', 'height' ) ) ) {
359
+ if ( '=' == $value['compare'] ) {
360
+ $value['value'] = str_pad( $value['value'], 15, ' ', STR_PAD_LEFT );
361
+ $padded_values[ trim( $value['value'] ) ] = $value['value'];
362
+ } else {
363
+ $value['value'] = '%' . $value['value'];
364
+ }
365
+ }
366
+
367
+ if ( 'LIKE' == $value['compare'] ) {
368
+ $patterns[] = $value['value'];
369
+ }
370
+
371
+ $query[] = $value;
372
+ }
373
+
374
+ if ( ! empty( $padded_values ) ) {
375
+ MLAQuery::$query_parameters['mla-metavalue'] = $padded_values;
376
+ }
377
+
378
+ if ( ! empty( $patterns ) ) {
379
+ MLAQuery::$query_parameters['patterns'] = $patterns;
380
+ }
381
+
382
+ // Combine with an existing "custom view" meta_query, if present
383
+ if ( isset( $request['meta_query'] ) ) {
384
+ $request['meta_query'] = array( 'relation' => 'AND', $request['meta_query'], $query );
385
+ } else {
386
+ $request['meta_query'] = $query;
387
+ }
388
+
389
+ MLACore::mla_debug_add( __LINE__ . ' mla_list_table_query_final_terms request = ' . var_export( $request, true ) );
390
+ return $request;
391
+ } // mla_list_table_query_final_terms
392
+
393
+ /**
394
+ * MLA Edit Media "Query Attachments" initial terms Filter
395
+ *
396
+ * Gives you an opportunity to change the terms of the
397
+ * Media Manager Modal Window "Query Attachments" query
398
+ * before they are pre-processed by the MLA handler.
399
+ *
400
+ * @since 1.03
401
+ *
402
+ * @param array WP_Query terms supported for "Query Attachments"
403
+ * @param array All terms passed in the request
404
+ */
405
+ public static function mla_media_modal_query_initial_terms( $query, $raw_query ) {
406
+ /*
407
+ * Look for the custom field search prefix in the Search Media text box,
408
+ * after checking for the "debug" prefixes.
409
+ */
410
+ if ( isset( $query['mla_search_value'] ) ) {
411
+ switch ( substr( $query['mla_search_value'], 0, 3 ) ) {
412
+ case '}|{':
413
+ self::$custom_field_parameters['debug'] = 'log'; // = 'console'; won't work for AJAX queries
414
+ $start = 3;
415
+ break;
416
+ case '{|}':
417
+ self::$custom_field_parameters['debug'] = 'log';
418
+ $start = 3;
419
+ break;
420
+ default:
421
+ $start = 0;
422
+ }
423
+
424
+ $prefix = self::$plugin_settings->get_plugin_option( 'prefix' );
425
+ if ( $prefix === substr( $query['mla_search_value'], $start, strlen( $prefix ) ) ) {
426
+ self::$custom_field_parameters['s'] = substr( $query['mla_search_value'], $start + strlen( $prefix ) );
427
+ unset( $query['mla_search_value'] );
428
+ //self::$custom_field_parameters['mla_search_connector'] = $query['mla_search_connector'];
429
+ unset( $query['mla_search_connector'] );
430
+ //self::$custom_field_parameters['mla_search_fields'] = $query['mla_search_fields'];
431
+ unset( $query['mla_search_fields'] );
432
+ } else {
433
+ self::$custom_field_parameters = array();
434
+ }
435
+ } // isset mla_search_value=custom:
436
+
437
+ return $query;
438
+ }
439
+
440
+ /**
441
+ * MLA Edit Media "Query Attachments" final terms Filter
442
+ *
443
+ * Gives you an opportunity to change the terms of the
444
+ * Media Manager Modal Window "Query Attachments" query
445
+ * after they are processed by the "Prepare List Table Query" handler.
446
+ *
447
+ * @since 1.03
448
+ *
449
+ * @param array WP_Query request prepared by "Prepare List Table Query"
450
+ */
451
+ public static function mla_media_modal_query_final_terms( $request ) {
452
+ // The logic used in the Media/Assistant Search Media box will work here as well
453
+ return MLACustomFieldSearchExample::mla_list_table_query_final_terms( $request );
454
+ }
455
+
456
+ } // Class MLACustomFieldSearchExample
457
+
458
+ // Install the filters at an early opportunity
459
+ add_action('init', 'MLACustomFieldSearchExample::initialize');
460
+ ?>
examples/plugins/mla-parent-custom-field-mapping/admin-settings-page.tpl CHANGED
@@ -340,35 +340,36 @@ You can leave the default (checked) setting in place unless you are having a spe
340
  </p>
341
  <h3>Debugging and Troubleshooting</h3>
342
  <p>
343
- When you are creating a new rule testing it out on one or a few posts/pages and carefully inspecting the results can be a valuable exercise. YOu may have to delete the field values, modify the rule and reapply it a few times to get the expected results.
344
  </p>
345
  <p>
346
- If a problem persists you can activate the debug logging, run a test and insoect the log file for more information about what's going on. To activate MLAs debug logging:
347
  </p>
348
  <ol>
349
  <li>Navigate to the Settings/Media Library Assistant Debug tab.</li>
350
- <li>Scroll down to the MLA Reporting text box and enter 0x8013”. This will turn on MLA debug logging for the example plugin, AJAX operations (such as WP/LR Sync) and the IPTC/EXIF metadata mapping rules.</li>
351
  <li>Click the Save Changes button to record your new setting.</li>
352
- <li>Optionally, scroll to the bottom of the screen and click Reset to clear the error log. You may not want to do this depending on how you manage your error log.</li>
353
  </ol>
354
  <p>
355
- Once thats done you can run a test. The debug log will be very detailed, so restricting the test as best you can will be very helpful. To can often update one post and then collect the test results. One way to do that:
356
  </p>
357
  <ol>
358
  <li>Manually delete the Repeater Field content for one of your posts.</li>
359
  <li>Go to the Media/Assistant admin submenu table and find one of the attachments for that post.</li>
360
- <li>Click on the (Parent:xxxx) link in the ID/Parent column to filter the display showing all the attachments for that one post.</li>
361
  <li>Click the box next to the ID/Parent column title to select all the attachments.</li>
362
- <li>Select Edit from the Bulk Actions dropdown and click Apply”.</li>
363
- <li>Click the Map IPTC/EXIF metadata button in the bottom-right corner of the Bulk Edit area.</li>
364
  </ol>
365
  <p>
366
- When youve finished testing, go back to the Debug screen and:
367
  </p>
368
  <ol>
369
- <li>Enter 0 in the MLA Reporting text box to turn debug logic off.</li>
370
  <li>Click the Save Changes button to record your new setting.</li>
371
- <li>Scroll to the bottom and click Download to get the log content in a text file.</li>
 
372
  </ol>
373
  <p>
374
  There should be a lot of messages written to the log, so limit the amount of activity during the logging period. You should see messages in the log like these:
340
  </p>
341
  <h3>Debugging and Troubleshooting</h3>
342
  <p>
343
+ When you are creating a new rule testing it out on one or a few posts/pages and carefully inspecting the results can be a valuable exercise. You may have to delete the field values, modify the rule and reapply it a few times to get the expected results.
344
  </p>
345
  <p>
346
+ If a problem persists you can activate the debug logging, run a test and inspect the log file for more information about what's going on. To activate MLA&rsquo;s debug logging:
347
  </p>
348
  <ol>
349
  <li>Navigate to the Settings/Media Library Assistant Debug tab.</li>
350
+ <li>Scroll down to the &ldquo;MLA Reporting&rdquo; text box and enter &ldquo;0x8013&rdquo;. This will turn on MLA debug logging for the example plugin, AJAX operations (such as WP/LR Sync) and the IPTC/EXIF metadata mapping rules.</li>
351
  <li>Click the Save Changes button to record your new setting.</li>
352
+ <li>Optionally, scroll to the bottom of the screen and click &ldquo;Reset&rdquo; to clear the error log. You may not want to do this depending on how you manage your error log.</li>
353
  </ol>
354
  <p>
355
+ Once that&rsquo;s done you can run a test. The debug log will be very detailed, so restricting the test as best you can will be very helpful. You can often update just one post and then collect the test results. One way to do that:
356
  </p>
357
  <ol>
358
  <li>Manually delete the Repeater Field content for one of your posts.</li>
359
  <li>Go to the Media/Assistant admin submenu table and find one of the attachments for that post.</li>
360
+ <li>Click on the &ldquo;(Parent:xxxx)&rdquo; link in the ID/Parent column to filter the display showing all the attachments for that one post.</li>
361
  <li>Click the box next to the ID/Parent column title to select all the attachments.</li>
362
+ <li>Select &ldquo;Edit&rdquo; from the Bulk Actions dropdown and click &ldquo;Apply&rdquo;.</li>
363
+ <li>Click the &ldquo;Map IPTC/EXIF metadata&rdquo; button in the bottom-right corner of the Bulk Edit area.</li>
364
  </ol>
365
  <p>
366
+ When you&rsquo;ve finished testing, go back to the Debug screen and:
367
  </p>
368
  <ol>
369
+ <li>Enter &ldquo;0&rdquo; in the MLA Reporting text box to turn debug logging off.</li>
370
  <li>Click the Save Changes button to record your new setting.</li>
371
+ <li>Scroll to the bottom and click &ldquo;Download&rdquo; to get the log content in a text file.</li>
372
+ <li>Optionally, scroll to the bottom of the screen and click &ldquo;Reset&rdquo; to clear the error log.</li>
373
  </ol>
374
  <p>
375
  There should be a lot of messages written to the log, so limit the amount of activity during the logging period. You should see messages in the log like these:
examples/plugins/mla-parent-search-example.php CHANGED
@@ -94,11 +94,11 @@ class MLAParentSearchExample {
94
  */
95
  if ( isset( $_REQUEST['s'] ) ) {
96
  switch ( substr( $_REQUEST['s'], 0, 3 ) ) {
97
- case '>|<':
98
  self::$parent_search_parameters['debug'] = 'console';
99
  $start = 3;
100
  break;
101
- case '<|>':
102
  self::$parent_search_parameters['debug'] = 'log';
103
  $start = 3;
104
  break;
@@ -216,11 +216,11 @@ class MLAParentSearchExample {
216
  */
217
  if ( isset( $query['mla_search_value'] ) ) {
218
  switch ( substr( $query['mla_search_value'], 0, 3 ) ) {
219
- case '>|<':
220
  self::$parent_search_parameters['debug'] = 'console';
221
  $start = 3;
222
  break;
223
- case '<|>':
224
  self::$parent_search_parameters['debug'] = 'log';
225
  $start = 3;
226
  break;
94
  */
95
  if ( isset( $_REQUEST['s'] ) ) {
96
  switch ( substr( $_REQUEST['s'], 0, 3 ) ) {
97
+ case '}|{':
98
  self::$parent_search_parameters['debug'] = 'console';
99
  $start = 3;
100
  break;
101
+ case '{|}':
102
  self::$parent_search_parameters['debug'] = 'log';
103
  $start = 3;
104
  break;
216
  */
217
  if ( isset( $query['mla_search_value'] ) ) {
218
  switch ( substr( $query['mla_search_value'], 0, 3 ) ) {
219
+ case '}|{':
220
  self::$parent_search_parameters['debug'] = 'console';
221
  $start = 3;
222
  break;
223
+ case '{|}':
224
  self::$parent_search_parameters['debug'] = 'log';
225
  $start = 3;
226
  break;
examples/plugins/mla-postie-post-after-example.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Applies MLA mapping rules to child attachments after Postie creates a parent post from an email
4
+ *
5
+ * Created for support topic "Plugin ‘MLA Postie Post After Example’"
6
+ * opened on 12/7/2020 by "ernstwg":
7
+ * https://wordpress.org/support/topic/plugin-mla-simple-mapping-hooks-example/
8
+ *
9
+ * @package MLA Postie Post After Example
10
+ * @version 1.00
11
+ */
12
+
13
+ /*
14
+ Plugin Name: MLA Postie Post After Example
15
+ Plugin URI: http://davidlingren.com/
16
+ Description: Applies MLA mapping rules to child attachments after Postie creates a parent post from an email
17
+ Author: David Lingren
18
+ Version: 1.00
19
+ Author URI: http://davidlingren.com/
20
+
21
+ Copyright 2020 David Lingren
22
+
23
+ This program is free software; you can redistribute it and/or modify
24
+ it under the terms of the GNU General Public License as published by
25
+ the Free Software Foundation; either version 2 of the License, or
26
+ (at your option) any later version.
27
+
28
+ This program is distributed in the hope that it will be useful,
29
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
30
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
+ GNU General Public License for more details.
32
+
33
+ You can get a copy of the GNU General Public License by writing to the
34
+ Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
35
+ */
36
+
37
+ /**
38
+ * Class MLA Postie Post After Example hooks the Postie 'postie_post_after' action
39
+ *
40
+ * @package MLA Postie Post After Example
41
+ * @since 1.00
42
+ */
43
+ class MLAPostiePostAfterExample {
44
+ /**
45
+ * Initialization function, similar to __construct()
46
+ *
47
+ * Installs filters and actions that handle the MLA hooks for uploading and mapping.
48
+ *
49
+ * @since 1.00
50
+ *
51
+ * @return void
52
+ */
53
+ public static function initialize() {
54
+ // This plugin requires both MLA and Postie
55
+ if ( class_exists( 'MLACore', false ) && class_exists( 'Postie', false ) ) {
56
+ add_action( 'postie_post_after', 'MLAPostiePostAfterExample::postie_post_after', 10, 1 );
57
+ }
58
+ }
59
+
60
+ /**
61
+ * PostieMessage::save_post() action
62
+ *
63
+ * Signals end of Postie post insertion.
64
+ *
65
+ * @since 1.00
66
+ *
67
+ * @param array $details An array of slashed, sanitized, and processed attachment post data.
68
+ */
69
+ public static function postie_post_after( $details ) {
70
+ global $wpdb;
71
+
72
+ // Build an array of SQL clauses to find Parent/Child relationships
73
+ $query = array();
74
+ $query_parameters = array();
75
+
76
+ $query[] = "SELECT p.ID FROM {$wpdb->posts} AS p";
77
+
78
+ // INNER JOIN removes posts with no attachments
79
+ $query[] = "INNER JOIN {$wpdb->posts} as p2";
80
+ $query[] = "ON (p.post_parent = p2.ID)";
81
+
82
+ $query[] = "WHERE p2.post_type = '" . $details['post_type'] . "'";
83
+ $query[] = "AND p2.post_status = '" . $details['post_status'] . "'";
84
+
85
+ $placeholders = array( '%d' );;
86
+ $query_parameters[] = $details['ID'];
87
+ $query[] = 'AND ( p.post_parent IN (' . join( ',', $placeholders ) . ') )';
88
+
89
+ $query[] = "AND p.post_type = 'attachment'";
90
+ $query[] = "AND p.post_status = 'inherit'";
91
+
92
+ $query = join(' ', $query);
93
+ $results = $wpdb->get_results( $wpdb->prepare( $query, $query_parameters ) );
94
+
95
+ foreach ( $results as $result ) {
96
+ $item_id = (integer) $result->ID;
97
+
98
+ do_action( 'mla_begin_mapping', 'single_custom', $item_id );
99
+ $updates = MLAOptions::mla_evaluate_custom_field_mapping( $item_id, 'single_attachment_mapping' );
100
+ do_action( 'mla_end_mapping' );
101
+
102
+ if ( !empty( $updates ) ) {
103
+ $item_content = MLAData::mla_update_single_item( $item_id, $updates );
104
+ }
105
+
106
+ $item = get_post( $item_id );
107
+ do_action( 'mla_begin_mapping', 'single_iptc_exif', $item_id );
108
+ $updates = MLAOptions::mla_evaluate_iptc_exif_mapping( $item, 'iptc_exif_mapping' );
109
+ do_action( 'mla_end_mapping' );
110
+
111
+ if ( !empty( $updates ) ) {
112
+ $item_content = MLAData::mla_update_single_item( $item_id, $updates );
113
+ }
114
+ } // foreach child
115
+ } // postie_post_after
116
+ } //MLAPostiePostAfterExample
117
+
118
+ // Install the filters at an early opportunity
119
+ add_action('init', 'MLAPostiePostAfterExample::initialize');
120
+ ?>
examples/plugins/smart-media-categories/admin/includes/class-smc-automatic-support.php CHANGED
@@ -38,11 +38,23 @@ class SMC_Automatic_Support {
38
  * @return void
39
  */
40
  public static function initialize() {
 
 
 
 
 
 
 
 
 
41
  //error_log( __LINE__ . ' SMC_Automatic_Support::initialize() $_REQUEST = ' . var_export( $_REQUEST, true), 0 );
42
 
43
  add_filter( 'wp_handle_upload_prefilter', 'SMC_Automatic_Support::filter_wp_handle_upload_prefilter', 0x7FFFFFFE, 1 );
44
  add_filter( 'wp_handle_upload', 'SMC_Automatic_Support::filter_wp_handle_upload_filter', 0x7FFFFFFE, 2 );
45
 
 
 
 
46
  add_action( 'pre_post_update', 'SMC_Automatic_Support::action_pre_post_update', 0x7FFFFFFE, 2 );
47
  add_action( 'edit_attachment', 'SMC_Automatic_Support::action_edit_attachment', 0x7FFFFFFE, 1 );
48
  add_action( 'add_attachment', 'SMC_Automatic_Support::action_add_attachment', 0x7FFFFFFE, 1 );
@@ -102,6 +114,54 @@ class SMC_Automatic_Support {
102
  return $upload;
103
  } // filter_wp_handle_upload_filter
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  /**
106
  * Fires immediately before an existing post/attachment is updated in the database
107
  *
@@ -122,6 +182,11 @@ class SMC_Automatic_Support {
122
  SMC_Automatic_support::rule_update_post_terms( $post_id, NULL, NULL, NULL, 'before' );
123
  }
124
 
 
 
 
 
 
125
  if ( (boolean) SMC_Settings_Support::get_option( 'attach_orphan' ) ) {
126
  SMC_Automatic_support::rule_attach_orphan( $data['post_parent'], $post_id, true );
127
  }
@@ -170,7 +235,7 @@ class SMC_Automatic_Support {
170
  //error_log( __LINE__ . ' SMC_Automatic_Support::action_add_attachment $post_id = ' . var_export( $post_id, true), 0 );
171
  //error_log( __LINE__ . ' SMC_Automatic_Support::action_add_attachment $post = ' . var_export( get_post( $post_id ), true), 0 );
172
 
173
- // Flush the cache of parent/child term assignments toforce resynch
174
  SMC_Sync_Support::get_posts_per_view( NULL, true );
175
 
176
  if ( (boolean) SMC_Settings_Support::get_option( 'upload_item' ) ) {
@@ -391,12 +456,9 @@ class SMC_Automatic_Support {
391
  * @param array $old_tt_ids Old array of term taxonomy IDs.
392
  */
393
  public static function action_set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
394
- //error_log( __LINE__ . ' SMC_Automatic_Support::action_set_object_terms $object_id = ' . var_export( $object_id, true), 0 );
395
- //error_log( __LINE__ . ' SMC_Automatic_Support::action_set_object_terms $terms = ' . var_export( $terms, true), 0 );
396
- //error_log( __LINE__ . ' SMC_Automatic_Support::action_set_object_terms $tt_ids = ' . var_export( $tt_ids, true), 0 );
397
- //error_log( __LINE__ . ' SMC_Automatic_Support::action_set_object_terms $taxonomy = ' . var_export( $taxonomy, true), 0 );
398
- //error_log( __LINE__ . ' SMC_Automatic_Support::action_set_object_terms $append = ' . var_export( $append, true), 0 );
399
- //error_log( __LINE__ . ' SMC_Automatic_Support::action_set_object_terms $old_tt_ids = ' . var_export( $old_tt_ids, true), 0 );
400
 
401
  if ( (boolean) SMC_Settings_Support::get_option( 'update_post_terms' ) ) {
402
  SMC_Automatic_support::rule_update_post_terms( $object_id, $taxonomy, $tt_ids, $old_tt_ids, 'during' );
@@ -674,9 +736,9 @@ class SMC_Automatic_Support {
674
  *
675
  * @since 1.0.6
676
  *
677
- * @param integer Post ID.
 
678
  * @param array An array of new term taxonomy IDs.
679
- * @param string Taxonomy slug.
680
  * @param array Old array of term taxonomy IDs.
681
  * @param string Update phase; 'before', 'during', 'after'.
682
  *
@@ -686,35 +748,34 @@ class SMC_Automatic_Support {
686
  //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms( {$post_id}, {$taxonomy}, {$phase} ) \$tt_ids = " . var_export( $tt_ids, true), 0 );
687
  //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms( {$post_id}, {$taxonomy}, {$phase} ) \$old_tt_ids = " . var_export( $old_tt_ids, true), 0 );
688
  static $update_post = NULL, $update_type = NULL, $active_taxonomies = NULL, $terms_changed = false;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
 
690
  switch ( $phase ) {
691
  case 'before':
692
  $terms_changed = false;
693
-
694
- // Make sure the object is a supported Post Type
695
- $post = get_post( $post_id );
696
- //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms $post = ' . var_export( $post, true), 0 );
697
- if ( !SMC_Settings_Support::is_smc_post_type( $post->post_type ) ) {
698
- $update_post = NULL;
699
- $update_type = NULL;
700
- return;
701
- }
702
-
703
  $update_post = $post_id;
704
- $update_type = $post->post_type;
705
- if ( is_null( $active_taxonomies ) ) {
706
- $active_taxonomies = SMC_Sync_Support::get_active_taxonomies( $update_type );
707
- }
708
- //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms $active_taxonomies = ' . var_export( $active_taxonomies, true), 0 );
709
  break;
710
  case 'during':
711
  //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms during $update_post = ' . var_export( $update_post, true), 0 );
712
- // Make sure it's the right Post
713
- if ( is_null( $update_post ) || ( $update_post != $post_id ) ) {
714
- return;
715
- }
716
- //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms during $taxonomy = ' . var_export( $taxonomy, true), 0 );
717
-
718
  // Make sure it's an active taxonomy
719
  if ( ! array_key_exists( $taxonomy, $active_taxonomies ) ) {
720
  return;
@@ -723,22 +784,50 @@ class SMC_Automatic_Support {
723
  // tt_ids are strings on input, old_tt_ids are integers
724
  $tt_ids = array_map( 'absint', $tt_ids );
725
  //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms mapped $tt_ids = ' . var_export( $tt_ids, true), 0 );
726
- if ( $tt_ids != $old_tt_ids ) {
727
- $terms_changed = true;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
728
  }
729
 
 
730
  //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms ({$taxonomy}) \$terms_changed = " . var_export( $terms_changed, true), 0 );
731
  break;
732
  case 'after':
733
  // Make sure terms have changed and it's the right Post
734
- if ( $terms_changed && $update_post == $post_id ) {
 
735
  $all_assignments = SMC_Sync_Support::get_posts_per_view( array( 'post_type' => $update_type, 'smc_status' => 'unsync', 'post_parents' => array( $post_id ), 'fields' => 'all' ) );
736
- //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms $all_assignments = ' . var_export( $all_assignments, true ), 0 );
737
  $results = SMC_Sync_Support::sync_all( $all_assignments );
738
- //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms $results = ' . var_export( $results, true ), 0 );
739
  }
740
 
741
  $update_post = NULL;
 
 
742
  $terms_changed = false;
743
  break;
744
  } // phase
38
  * @return void
39
  */
40
  public static function initialize() {
41
+ // Look for Postie chron job
42
+ if ( isset( $_REQUEST['doing_wp_cron'] ) && class_exists( 'Postie', false ) ) {
43
+ if ( false === (boolean) SMC_Settings_Support::get_option( 'postie_sync' ) ) {
44
+ //error_log( __LINE__ . ' SMC_Automatic_Support::initialize() no Postie Support', 0 );
45
+ return;
46
+ }
47
+ //error_log( __LINE__ . ' SMC_Automatic_Support::initialize() Postie Support in Cron Job', 0 );
48
+ }
49
+
50
  //error_log( __LINE__ . ' SMC_Automatic_Support::initialize() $_REQUEST = ' . var_export( $_REQUEST, true), 0 );
51
 
52
  add_filter( 'wp_handle_upload_prefilter', 'SMC_Automatic_Support::filter_wp_handle_upload_prefilter', 0x7FFFFFFE, 1 );
53
  add_filter( 'wp_handle_upload', 'SMC_Automatic_Support::filter_wp_handle_upload_filter', 0x7FFFFFFE, 2 );
54
 
55
+ add_filter( 'wp_insert_post_data', 'SMC_Automatic_Support::filter_wp_insert_post_data', 0x7FFFFFFE, 3 );
56
+ add_action( 'wp_insert_post', 'SMC_Automatic_Support::action_wp_insert_post', 0x7FFFFFFE, 3 );
57
+
58
  add_action( 'pre_post_update', 'SMC_Automatic_Support::action_pre_post_update', 0x7FFFFFFE, 2 );
59
  add_action( 'edit_attachment', 'SMC_Automatic_Support::action_edit_attachment', 0x7FFFFFFE, 1 );
60
  add_action( 'add_attachment', 'SMC_Automatic_Support::action_add_attachment', 0x7FFFFFFE, 1 );
114
  return $upload;
115
  } // filter_wp_handle_upload_filter
116
 
117
+ /**
118
+ * Filters slashed post data just before it is inserted into the database.
119
+ *
120
+ * Called from /wp-includes/post.php, function wp_insert_post
121
+ *
122
+ * @since 1.1.6
123
+ *
124
+ * @param array $data An array of slashed, sanitized, and processed post data.
125
+ * @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data.
126
+ * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
127
+ * originally passed to wp_insert_post().
128
+ */
129
+ public static function filter_wp_insert_post_data( $data, $postarr, $unsanitized_postarr ) {
130
+ //error_log( __LINE__ . ' SMC_Automatic_Support::filter_wp_insert_post_data $data = ' . var_export( $data, true), 0 );
131
+ //error_log( __LINE__ . ' SMC_Automatic_Support::filter_wp_insert_post_data $postarr = ' . var_export( $postarr, true), 0 );
132
+ //error_log( __LINE__ . ' SMC_Automatic_Support::filter_wp_insert_post_data $unsanitized_postarr = ' . var_export( $unsanitized_postarr, true), 0 );
133
+
134
+ // Handle the case of setting terms during original post insert
135
+ if ( empty( $postarr['ID'] ) ) {
136
+ if ( (boolean) SMC_Settings_Support::get_option( 'update_post_terms' ) ) {
137
+ $post_type = isset( $postarr['post_type'] ) ? $postarr['post_type'] : 'post';
138
+ SMC_Automatic_support::rule_update_post_terms( 0, $post_type, NULL, NULL, 'before' );
139
+ }
140
+ }
141
+
142
+ return $data;
143
+ } // filter_wp_insert_post_data
144
+
145
+ /**
146
+ * Fires once a post has been saved.
147
+ *
148
+ * Called from /wp-includes/post.php, function wp_insert_post
149
+ *
150
+ * @since 1.1.6
151
+ *
152
+ * @param int $post_ID Post ID.
153
+ * @param WP_Post $post Post object.
154
+ * @param bool $update Whether this is an existing post being updated.
155
+ */
156
+ public static function action_wp_insert_post( $post_ID, $post, $update ) {
157
+ //error_log( __LINE__ . " SMC_Automatic_Support::filter_wp_insert_post( {$post_ID}, {$update} ) \$post = " . var_export( $post, true), 0 );
158
+
159
+ // Handle the case of setting terms during original post insert
160
+ if ( (boolean) SMC_Settings_Support::get_option( 'update_post_terms' ) ) {
161
+ SMC_Automatic_support::rule_update_post_terms( $post_ID, NULL, NULL, NULL, 'after' );
162
+ }
163
+ } // action_wp_insert_post
164
+
165
  /**
166
  * Fires immediately before an existing post/attachment is updated in the database
167
  *
182
  SMC_Automatic_support::rule_update_post_terms( $post_id, NULL, NULL, NULL, 'before' );
183
  }
184
 
185
+ // This action can be called for any post_type, e.g., post or page in addition to attachment
186
+ if ( 'attachment' !== $data['post_type'] ) {
187
+ return;
188
+ }
189
+
190
  if ( (boolean) SMC_Settings_Support::get_option( 'attach_orphan' ) ) {
191
  SMC_Automatic_support::rule_attach_orphan( $data['post_parent'], $post_id, true );
192
  }
235
  //error_log( __LINE__ . ' SMC_Automatic_Support::action_add_attachment $post_id = ' . var_export( $post_id, true), 0 );
236
  //error_log( __LINE__ . ' SMC_Automatic_Support::action_add_attachment $post = ' . var_export( get_post( $post_id ), true), 0 );
237
 
238
+ // Flush the cache of parent/child term assignments to force resynch
239
  SMC_Sync_Support::get_posts_per_view( NULL, true );
240
 
241
  if ( (boolean) SMC_Settings_Support::get_option( 'upload_item' ) ) {
456
  * @param array $old_tt_ids Old array of term taxonomy IDs.
457
  */
458
  public static function action_set_object_terms( $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids ) {
459
+ //error_log( __LINE__ . " SMC_Automatic_Support::action_set_object_terms( {$object_id}, {$taxonomy}, {$append} ) \$terms = " . var_export( $terms, true), 0 );
460
+ //error_log( __LINE__ . " SMC_Automatic_Support::action_set_object_terms( {$object_id}, {$taxonomy}, {$append} ) \$tt_ids = " . var_export( $tt_ids, true), 0 );
461
+ //error_log( __LINE__ . " SMC_Automatic_Support::action_set_object_terms( {$object_id}, {$taxonomy}, {$append} ) \$old_tt_ids = " . var_export( $old_tt_ids, true), 0 );
 
 
 
462
 
463
  if ( (boolean) SMC_Settings_Support::get_option( 'update_post_terms' ) ) {
464
  SMC_Automatic_support::rule_update_post_terms( $object_id, $taxonomy, $tt_ids, $old_tt_ids, 'during' );
736
  *
737
  * @since 1.0.6
738
  *
739
+ * @param integer Post ID or zero (0) in 'before' phase when inserting a new post.
740
+ * @param string Taxonomy slug or post_type in 'before' phase when inserting a new post.
741
  * @param array An array of new term taxonomy IDs.
 
742
  * @param array Old array of term taxonomy IDs.
743
  * @param string Update phase; 'before', 'during', 'after'.
744
  *
748
  //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms( {$post_id}, {$taxonomy}, {$phase} ) \$tt_ids = " . var_export( $tt_ids, true), 0 );
749
  //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms( {$post_id}, {$taxonomy}, {$phase} ) \$old_tt_ids = " . var_export( $old_tt_ids, true), 0 );
750
  static $update_post = NULL, $update_type = NULL, $active_taxonomies = NULL, $terms_changed = false;
751
+
752
+ // Make sure the object is a supported Post Type
753
+ if ( 0 === $post_id ) {
754
+ // Inserting a new post
755
+ $update_type = $taxonomy;
756
+ } else {
757
+ $post = get_post( $post_id );
758
+ $update_type = $post->post_type;
759
+ }
760
+ //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms( {$post_id}, {$update_type} )", 0 );
761
+
762
+ if ( is_null( $active_taxonomies ) ) {
763
+ $active_taxonomies = SMC_Sync_Support::get_active_taxonomies( $update_type );
764
+ //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms $active_taxonomies = ' . var_export( $active_taxonomies, true), 0 );
765
+ }
766
+
767
+ if ( !SMC_Settings_Support::is_smc_post_type( $update_type ) ) {
768
+ return;
769
+ }
770
 
771
  switch ( $phase ) {
772
  case 'before':
773
  $terms_changed = false;
 
 
 
 
 
 
 
 
 
 
774
  $update_post = $post_id;
 
 
 
 
 
775
  break;
776
  case 'during':
777
  //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms during $update_post = ' . var_export( $update_post, true), 0 );
778
+
 
 
 
 
 
779
  // Make sure it's an active taxonomy
780
  if ( ! array_key_exists( $taxonomy, $active_taxonomies ) ) {
781
  return;
784
  // tt_ids are strings on input, old_tt_ids are integers
785
  $tt_ids = array_map( 'absint', $tt_ids );
786
  //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms mapped $tt_ids = ' . var_export( $tt_ids, true), 0 );
787
+ $current_terms_changed = ( $tt_ids != $old_tt_ids );
788
+ //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms ({$taxonomy}) \$current_terms_changed = " . var_export( $current_terms_changed, true), 0 );
789
+
790
+ // Check for stand-alone terms update, i.e., not part of an insert/update post event
791
+ if ( is_null( $update_post ) ) {
792
+ // If terms have changed go ahead and sync
793
+ if ( $current_terms_changed ) {
794
+ SMC_Sync_Support::get_posts_per_view( NULL, true );
795
+ $all_assignments = SMC_Sync_Support::get_posts_per_view( array( 'post_type' => $update_type, 'smc_status' => 'unsync', 'post_parents' => array( $post_id ), 'fields' => 'all' ) );
796
+ //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms standalone $all_assignments = ' . var_export( $all_assignments, true ), 0 );
797
+ $results = SMC_Sync_Support::sync_all( $all_assignments );
798
+ //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms standalone $results = ' . var_export( $results, true ), 0 );
799
+ }
800
+
801
+ return;
802
+ } // is_null
803
+
804
+ // Inserting a new post?
805
+ if ( 0 === $update_post ) {
806
+ $update_post = $post_id;
807
+ }
808
+
809
+ // Make sure it's the right Post
810
+ if ( $update_post !== $post_id ) {
811
+ //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms( {$post_id}, {$update_post} ) wrong post", 0 );
812
+ return;
813
  }
814
 
815
+ $terms_changed |= $current_terms_changed;
816
  //error_log( __LINE__ . " SMC_Automatic_Support::rule_update_post_terms ({$taxonomy}) \$terms_changed = " . var_export( $terms_changed, true), 0 );
817
  break;
818
  case 'after':
819
  // Make sure terms have changed and it's the right Post
820
+ if ( $terms_changed && $update_post === $post_id ) {
821
+ SMC_Sync_Support::get_posts_per_view( NULL, true );
822
  $all_assignments = SMC_Sync_Support::get_posts_per_view( array( 'post_type' => $update_type, 'smc_status' => 'unsync', 'post_parents' => array( $post_id ), 'fields' => 'all' ) );
823
+ //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms after $all_assignments = ' . var_export( $all_assignments, true ), 0 );
824
  $results = SMC_Sync_Support::sync_all( $all_assignments );
825
+ //error_log( __LINE__ . ' SMC_Automatic_Support::rule_update_post_terms after $results = ' . var_export( $results, true ), 0 );
826
  }
827
 
828
  $update_post = NULL;
829
+ $update_type = NULL;
830
+ $active_taxonomies = NULL;
831
  $terms_changed = false;
832
  break;
833
  } // phase
examples/plugins/smart-media-categories/admin/includes/class-smc-settings-support.php CHANGED
@@ -127,6 +127,20 @@ class SMC_Settings_Support {
127
  'title' => __( 'Reattach Item', 'smart-media-categories' ),
128
  'description' => __( "When an items new parent is a <strong>Sync Post Type</strong>, it will inherit the parent's terms.", 'smart-media-categories' ),
129
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  ),
131
  'smc_manual_options' => array(
132
  ),
127
  'title' => __( 'Reattach Item', 'smart-media-categories' ),
128
  'description' => __( "When an items new parent is a <strong>Sync Post Type</strong>, it will inherit the parent's terms.", 'smart-media-categories' ),
129
  ),
130
+ 'exclude_default' => array(
131
+ 'type' => 'checkbox',
132
+ 'default' => '0',
133
+ 'id' => 'smc_automatic_exclude_default',
134
+ 'title' => __( 'Exclude Post Default', 'smart-media-categories' ),
135
+ 'description' => __( "Exclude the Settings/Wriing 'Default Post Category' term from Attachments.", 'smart-media-categories' ),
136
+ ),
137
+ 'postie_sync' => array(
138
+ 'type' => 'checkbox',
139
+ 'default' => '1',
140
+ 'id' => 'smc_automatic_postie_sync',
141
+ 'title' => __( 'Sync Postie Emails', 'smart-media-categories' ),
142
+ 'description' => __( "Apply syncronization options to posts created by Postie email processing.", 'smart-media-categories' ),
143
+ ),
144
  ),
145
  'smc_manual_options' => array(
146
  ),
examples/plugins/smart-media-categories/admin/includes/class-smc-sync-support.php CHANGED
@@ -284,10 +284,36 @@ class SMC_Sync_Support {
284
  return array( 'sync' => 0, 'unsync' => 0 );
285
  }
286
 
 
 
 
 
 
 
 
 
 
 
 
287
  // Compute sync status
288
  foreach ( $assignments as $parent_id => $assignment ) {
289
- //error_log( __LINE__ ." SMC_Sync_Support::get_posts_per_view (parent {$parent_id}) assignment = " . var_export( $assignment, true ), 0 );
290
  $parent_terms = $assignment['ttids'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  unset( $assignment['ttids'] );
292
  unset( $assignment['terms'] );
293
  $assignments[ $parent_id ]['smc_sync'] = true;
@@ -295,8 +321,8 @@ class SMC_Sync_Support {
295
  $smc_sync = $parent_terms == $child_terms;
296
  $assignments[ $parent_id ][ $child_id ]['smc_sync'] = $smc_sync;
297
  $assignments[ $parent_id ]['smc_sync'] &= $smc_sync;
298
- }
299
- }
300
  //error_log( __LINE__ . ' SMC_Sync_Support::get_posts_per_view final $assignments = ' . var_export( $assignments, true ), 0 );
301
 
302
  switch ( $smc_status ) {
@@ -350,7 +376,8 @@ class SMC_Sync_Support {
350
  * @param integer ID of the parent post
351
  * @param array IDs of children
352
  *
353
- * @return array ( [object_id] => array( [taxonomy] => array( [term_taxonomy_id] => array( 'id' => term_id, 'slug' => term_slug )... 'smc_sync' => true/false )... 'smc_sync' => true/false )
 
354
  */
355
  public static function get_terms( $parent_id, $children ) {
356
  global $wpdb;
@@ -389,6 +416,21 @@ class SMC_Sync_Support {
389
  //error_log( __LINE__ . ' SMC_Sync_Support::get_terms $taxonomies = ' . var_export( $taxonomies, true ), 0 );
390
  //error_log( __LINE__ . ' SMC_Sync_Support::get_terms $results = ' . var_export( $results, true ), 0 );
391
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
392
  // Add synchronization state information
393
  foreach ( $taxonomies as $taxonomy ) {
394
  if ( isset( $results[ $parent_id ][ $taxonomy ] ) ) {
@@ -397,13 +439,12 @@ class SMC_Sync_Support {
397
  $parent_terms = $results[ $parent_id ][ $taxonomy ] = array();
398
  }
399
  //error_log( __LINE__ ." SMC_Sync_Support::get_terms ({$taxonomy}) \$parent_terms = " . var_export( $parent_terms, true ), 0 );
400
-
401
  foreach( $children as $child ) {
402
  if ( ! isset( $results[ $child ][ $taxonomy ] ) ) {
403
  $results[ $child ][ $taxonomy ] = array();
404
  }
405
 
406
- $results[ $child ][ $taxonomy ]['smc_sync'] = ( $parent_terms == $results[ $child ][ $taxonomy ] );
407
 
408
  // All taxonomies must be synched for the overall child to be synched
409
  if ( isset( $results[ $child ]['smc_sync'] ) ) {
@@ -440,15 +481,36 @@ class SMC_Sync_Support {
440
  $tax_action[ $tax_name ] = 'sync';
441
  $initial_tax_input[ $tax_name ] = array();
442
  }
443
- //error_log( __LINE__ . ' SMC_Sync_Support::sync_all $active_taxonomies = ' . var_export( $active_taxonomies, true ), 0 );
444
  //error_log( __LINE__ . ' SMC_Sync_Support::sync_all $tax_action = ' . var_export( $tax_action, true ), 0 );
445
 
 
 
 
 
 
 
 
446
  foreach( $assignments as $parent_id => $assignment ) {
447
  $taxonomy_terms = $assignment[ 'terms' ];
448
  unset( $assignment['terms'] );
449
  unset( $assignment['ttids'] );
450
  unset( $assignment['smc_sync'] );
451
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  $tax_input = $initial_tax_input;
453
  foreach( $taxonomy_terms as $taxonomy => $terms ) {
454
  foreach( $terms as $ttid => $term ) {
@@ -560,13 +622,12 @@ class SMC_Sync_Support {
560
  continue;
561
  }
562
 
563
- $taxonomy_obj = get_taxonomy( $taxonomy );
564
- //error_log( __LINE__ ." SMC_Sync_Support::sync_terms \$taxonomy_obj = " . var_export( $taxonomy_obj, true ), 0 );
565
-
566
  // Check if logged-in user can assign terms
567
  $current_user = wp_get_current_user();
568
  if ( $current_user->ID ) {
 
569
  if ( ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
 
570
  continue;
571
  }
572
  }
@@ -595,7 +656,9 @@ class SMC_Sync_Support {
595
  sort( $terms_before );
596
  }
597
 
598
- $terms_after = wp_set_post_terms( $parent_id, $terms, $taxonomy );
 
 
599
  sort( $terms_after );
600
  if ( $terms_after != $terms_before ) {
601
  $parent_changed = true;
284
  return array( 'sync' => 0, 'unsync' => 0 );
285
  }
286
 
287
+ // Check for Default Post Category exclusion
288
+ $default_tt_id = 0;
289
+ if ( (boolean) SMC_Settings_Support::get_option( 'exclude_default' ) ) {
290
+ // Get the actual TTID, just to be safe
291
+ $default_term = get_term( (integer) get_option( 'default_category' ), 'category' );
292
+ //error_log( __LINE__ ." SMC_Sync_Support::get_posts_per_view ID term = " . var_export( $default_term, true ), 0 );
293
+ if ( $default_term instanceof WP_Term ) {
294
+ $default_tt_id = $default_term->term_taxonomy_id;
295
+ }
296
+ } // exclude default
297
+
298
  // Compute sync status
299
  foreach ( $assignments as $parent_id => $assignment ) {
300
+ //error_log( __LINE__ ." SMC_Sync_Support::get_posts_per_view {$default_tt_id} (parent {$parent_id}) assignment = " . var_export( $assignment, true ), 0 );
301
  $parent_terms = $assignment['ttids'];
302
+
303
+ // Check for Default Post Category exclusion
304
+ if ( $default_tt_id ) {
305
+ $parent = get_post( $parent_id );
306
+ //error_log( __LINE__ ." SMC_Sync_Support::get_posts_per_view {$default_tt_id} parent = " . var_export( $parent, true ), 0 );
307
+ if ( 'post' === $parent->post_type && 'auto-draft' !== $parent->post_status ) {
308
+ foreach ( $parent_terms as $index => $ttid ) {
309
+ if ( $default_tt_id === $ttid ) {
310
+ unset( $parent_terms[ $index ] );
311
+ }
312
+ } // foreach term
313
+ } // type === post
314
+ } // exclude category
315
+ //error_log( __LINE__ ." SMC_Sync_Support::get_posts_per_view revised \$parent_terms = " . var_export( $parent_terms, true ), 0 );
316
+
317
  unset( $assignment['ttids'] );
318
  unset( $assignment['terms'] );
319
  $assignments[ $parent_id ]['smc_sync'] = true;
321
  $smc_sync = $parent_terms == $child_terms;
322
  $assignments[ $parent_id ][ $child_id ]['smc_sync'] = $smc_sync;
323
  $assignments[ $parent_id ]['smc_sync'] &= $smc_sync;
324
+ } // foreach child
325
+ } // foreach parent assignment
326
  //error_log( __LINE__ . ' SMC_Sync_Support::get_posts_per_view final $assignments = ' . var_export( $assignments, true ), 0 );
327
 
328
  switch ( $smc_status ) {
376
  * @param integer ID of the parent post
377
  * @param array IDs of children
378
  *
379
+ * @return array ( [object_id] => array( [taxonomy] =>
380
+ * array( [term_taxonomy_id] => array( 'id' => term_id, 'slug' => term_slug )... 'smc_sync' => true/false )... 'smc_sync' => true/false )
381
  */
382
  public static function get_terms( $parent_id, $children ) {
383
  global $wpdb;
416
  //error_log( __LINE__ . ' SMC_Sync_Support::get_terms $taxonomies = ' . var_export( $taxonomies, true ), 0 );
417
  //error_log( __LINE__ . ' SMC_Sync_Support::get_terms $results = ' . var_export( $results, true ), 0 );
418
 
419
+ // Check for Default Post Category exclusion
420
+ if ( ( ( boolean) SMC_Settings_Support::get_option( 'exclude_default' ) ) && ( 'category' === $term->taxonomy ) ) {
421
+ if ( 'post' === $parent->post_type && 'auto-draft' !== $parent->post_status ) {
422
+ $default_term_id = (integer) get_option( 'default_category' );
423
+ //error_log( __LINE__ ." SMC_Sync_Support::get_terms ({$term->taxonomy}) \$default_term_id = " . var_export( $default_term_id, true ), 0 );
424
+ foreach ( $results[ $parent_id ]['category'] as $index => $term ) {
425
+ //error_log( __LINE__ ." SMC_Sync_Support::get_terms ( category, {$index} ) \$term = " . var_export( $term, true ), 0 );
426
+ if ( $default_term_id === $index ) {
427
+ unset( $results[ $parent_id ]['category'][ $index ] );
428
+ }
429
+ }
430
+ } // type === post
431
+ //error_log( __LINE__ ." SMC_Sync_Support::get_terms ( category ) revised \$results = " . var_export( $results, true ), 0 );
432
+ } // exclude category
433
+
434
  // Add synchronization state information
435
  foreach ( $taxonomies as $taxonomy ) {
436
  if ( isset( $results[ $parent_id ][ $taxonomy ] ) ) {
439
  $parent_terms = $results[ $parent_id ][ $taxonomy ] = array();
440
  }
441
  //error_log( __LINE__ ." SMC_Sync_Support::get_terms ({$taxonomy}) \$parent_terms = " . var_export( $parent_terms, true ), 0 );
 
442
  foreach( $children as $child ) {
443
  if ( ! isset( $results[ $child ][ $taxonomy ] ) ) {
444
  $results[ $child ][ $taxonomy ] = array();
445
  }
446
 
447
+ $results[ $child ][ $taxonomy ]['smc_sync'] = ( $parent_terms === $results[ $child ][ $taxonomy ] );
448
 
449
  // All taxonomies must be synched for the overall child to be synched
450
  if ( isset( $results[ $child ]['smc_sync'] ) ) {
481
  $tax_action[ $tax_name ] = 'sync';
482
  $initial_tax_input[ $tax_name ] = array();
483
  }
484
+ //error_log( __LINE__ . ' SMC_Sync_Support::sync_all $active_taxonomies = ' . var_export( array_keys( $active_taxonomies ), true ), 0 );
485
  //error_log( __LINE__ . ' SMC_Sync_Support::sync_all $tax_action = ' . var_export( $tax_action, true ), 0 );
486
 
487
+ // Check for Default Post Category exclusion
488
+ $default_term_id = 0;
489
+ if ( (boolean) SMC_Settings_Support::get_option( 'exclude_default' ) ) {
490
+ $default_term_id = (integer) get_option( 'default_category' );
491
+ //error_log( __LINE__ ." SMC_Sync_Support::sync_all \$default_term_id = " . var_export( $default_term_id, true ), 0 );
492
+ } // exclude category
493
+
494
  foreach( $assignments as $parent_id => $assignment ) {
495
  $taxonomy_terms = $assignment[ 'terms' ];
496
  unset( $assignment['terms'] );
497
  unset( $assignment['ttids'] );
498
  unset( $assignment['smc_sync'] );
499
 
500
+ // Check for Default Post Category exclusion
501
+ if ( $default_term_id ) {
502
+ $parent = get_post( $parent_id );
503
+ if ( 'post' === $parent->post_type && 'auto-draft' !== $parent->post_status && isset( $taxonomy_terms['category'] ) ) {
504
+ foreach ( $taxonomy_terms['category'] as $index => $term ) {
505
+ //error_log( __LINE__ ." SMC_Sync_Support::sync_all ( category, {$index} ) \$term = " . var_export( $term, true ), 0 );
506
+ if ( $default_term_id === $term['term_id'] ) {
507
+ unset( $taxonomy_terms['category'][ $index ] );
508
+ }
509
+ } // type === post
510
+ } // exclude category
511
+ //error_log( __LINE__ ." SMC_Sync_Support::get_terms ( category ) revised \$taxonomy_terms = " . var_export( $taxonomy_terms, true ), 0 );
512
+ } // foreach assignment
513
+
514
  $tax_input = $initial_tax_input;
515
  foreach( $taxonomy_terms as $taxonomy => $terms ) {
516
  foreach( $terms as $ttid => $term ) {
622
  continue;
623
  }
624
 
 
 
 
625
  // Check if logged-in user can assign terms
626
  $current_user = wp_get_current_user();
627
  if ( $current_user->ID ) {
628
+ $taxonomy_obj = get_taxonomy( $taxonomy );
629
  if ( ! current_user_can( $taxonomy_obj->cap->assign_terms ) ) {
630
+ //error_log( __LINE__ ." SMC_Sync_Support::sync_terms User( {$current_user->ID} can't assign {$taxonomy} terms", 0 );
631
  continue;
632
  }
633
  }
656
  sort( $terms_before );
657
  }
658
 
659
+ // 1.1.6 - updating the parent's terms conflicts with the requirements
660
+ // $terms_after = wp_set_post_terms( $parent_id, $terms, $taxonomy );
661
+ $terms_after = $terms_before;
662
  sort( $terms_after );
663
  if ( $terms_after != $terms_before ) {
664
  $parent_changed = true;
examples/plugins/smart-media-categories/public/class-smart-media-categories.php CHANGED
@@ -25,7 +25,7 @@ class Smart_Media_Categories {
25
  *
26
  * @var string
27
  */
28
- const VERSION = '1.1.5';
29
 
30
  /**
31
  * Unique identifier for your plugin.
25
  *
26
  * @var string
27
  */
28
+ const VERSION = '1.1.6';
29
 
30
  /**
31
  * Unique identifier for your plugin.
examples/plugins/smart-media-categories/smart-media-categories.php CHANGED
@@ -14,7 +14,7 @@
14
  * Plugin Name: Smart Media Categories
15
  * Plugin URI: http://davidlingren.com/
16
  * Description: Assigns taxonomy terms to Media Library items based on the terms of their parent post/page.
17
- * Version: 1.1.5
18
  * Author: David Lingren
19
  * Author URI: http://davidlingren.com/
20
  * Text Domain: smart-media-categories
@@ -49,9 +49,7 @@ if ( ! defined( 'WPINC' ) ) {
49
  * Public-Facing Functionality
50
  *----------------------------------------------------------------------------*/
51
 
52
- /*
53
- * Plugin class for the public-facing side of the WordPress site.
54
- */
55
  require_once( plugin_dir_path( __FILE__ ) . 'public/class-smart-media-categories.php' );
56
 
57
  /*
@@ -61,9 +59,7 @@ require_once( plugin_dir_path( __FILE__ ) . 'public/class-smart-media-categories
61
  register_activation_hook( __FILE__, array( 'Smart_Media_Categories', 'activate' ) );
62
  register_deactivation_hook( __FILE__, array( 'Smart_Media_Categories', 'deactivate' ) );
63
 
64
- /*
65
- * Create an instance of the public-facing class.
66
- */
67
  add_action( 'plugins_loaded', array( 'Smart_Media_Categories', 'get_instance' ) );
68
 
69
  /*----------------------------------------------------------------------------*
@@ -82,9 +78,11 @@ add_action( 'plugins_loaded', array( 'Smart_Media_Categories', 'get_instance' )
82
  *
83
  * The code below is intended to to give the lightest footprint possible.
84
  */
 
85
  if ( is_admin() /* && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) */ ) {
86
  require_once( plugin_dir_path( __FILE__ ) . 'admin/class-smart-media-categories-admin.php' );
87
  add_action( 'plugins_loaded', array( 'Smart_Media_Categories_Admin', 'get_instance' ) );
 
88
  }
89
 
90
  // Look for Postie chron job
14
  * Plugin Name: Smart Media Categories
15
  * Plugin URI: http://davidlingren.com/
16
  * Description: Assigns taxonomy terms to Media Library items based on the terms of their parent post/page.
17
+ * Version: 1.1.6
18
  * Author: David Lingren
19
  * Author URI: http://davidlingren.com/
20
  * Text Domain: smart-media-categories
49
  * Public-Facing Functionality
50
  *----------------------------------------------------------------------------*/
51
 
52
+ // Plugin class for the public-facing side of the WordPress site.
 
 
53
  require_once( plugin_dir_path( __FILE__ ) . 'public/class-smart-media-categories.php' );
54
 
55
  /*
59
  register_activation_hook( __FILE__, array( 'Smart_Media_Categories', 'activate' ) );
60
  register_deactivation_hook( __FILE__, array( 'Smart_Media_Categories', 'deactivate' ) );
61
 
62
+ // Create an instance of the public-facing class.
 
 
63
  add_action( 'plugins_loaded', array( 'Smart_Media_Categories', 'get_instance' ) );
64
 
65
  /*----------------------------------------------------------------------------*
78
  *
79
  * The code below is intended to to give the lightest footprint possible.
80
  */
81
+
82
  if ( is_admin() /* && ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) */ ) {
83
  require_once( plugin_dir_path( __FILE__ ) . 'admin/class-smart-media-categories-admin.php' );
84
  add_action( 'plugins_loaded', array( 'Smart_Media_Categories_Admin', 'get_instance' ) );
85
+ //error_log( __LINE__ . ' smart-media-categories.php is_admin() Support _REQUEST = ' . var_export( $_REQUEST, true ), 0 );
86
  }
87
 
88
  // Look for Postie chron job
includes/class-mla-core.php CHANGED
@@ -21,7 +21,7 @@ class MLACore {
21
  *
22
  * @var string
23
  */
24
- const CURRENT_MLA_VERSION = '2.93';
25
 
26
  /**
27
  * Slug for registering and enqueueing plugin style sheets (moved from class-mla-main.php)
@@ -472,16 +472,16 @@ class MLACore {
472
  // Template file and database access functions.
473
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-data-query.php' );
474
  MLAQuery::initialize();
475
-
476
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-data.php' );
477
  MLAData::initialize();
478
-
479
  // Shortcode shim functions
480
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-shortcodes.php' );
481
  MLAShortcodes::initialize();
482
-
483
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-shortcode-support.php' );
484
-
485
  // Plugin settings management
486
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-options.php' );
487
  MLAOptions::initialize();
@@ -693,7 +693,7 @@ class MLACore {
693
  if ( '_wpnonce' !== $name ) {
694
  $actionurl = str_replace( '_wpnonce', $name, $actionurl );
695
  }
696
-
697
  return $actionurl;
698
  }
699
 
@@ -1245,7 +1245,7 @@ class MLACore {
1245
  'update_post_meta_cache' => 'false',
1246
  'update_post_term_cache' => 'false',
1247
  );
1248
-
1249
  $specification = self::mla_parse_view_specification( $specification );
1250
  if ( 'mime' == $specification['prefix'] ) {
1251
  $query['post_mime_type'] = $specification['value'];
@@ -1255,13 +1255,14 @@ class MLACore {
1255
  case 'match':
1256
  $patterns = array_map( 'trim', explode( ',', $specification['value'] ) );
1257
  foreach ( (array) $patterns as $pattern ) {
 
1258
  $pattern = preg_replace( '/\*+/', '%', $pattern );
1259
  if ( false !== strpos( $pattern, '%' ) ) {
1260
  // Preserve the pattern - it will be used in the "where" filter
1261
  $meta_query['patterns'][] = $pattern;
1262
- $meta_query[] = array( 'key' => $specification['name'], 'value' => $pattern, 'compare' => 'LIKE' );
1263
  } else {
1264
- $meta_query[] = array( 'key' => $specification['name'], 'value' => $pattern, 'compare' => '=' );
1265
  }
1266
  } // foreach pattern
1267
 
@@ -1271,16 +1272,24 @@ class MLACore {
1271
 
1272
  break;
1273
  case 'null':
1274
- $meta_query['key'] = $specification['name'];
 
 
 
1275
  $meta_query['value'] = 'NULL';
1276
  break;
1277
  default: // '', 'any'
1278
- $meta_query[] = array( 'key' => $specification['name'], 'value' => NULL, 'compare' => '!=' );
 
 
 
 
1279
  }
1280
 
1281
  $query['meta_query'] = $meta_query;
1282
  } // custom field specification
1283
 
 
1284
  return $query;
1285
  }
1286
 
@@ -1297,16 +1306,34 @@ class MLACore {
1297
  if ( is_array( $specification ) ) {
1298
  $specification = @implode( ',', $specification );
1299
  }
 
1300
 
1301
  $result = array( 'prefix' => '', 'name' => '', 'value' => '', 'option' => '' );
1302
  $match_count = preg_match( '/^(.+):(.+)/', $specification, $matches );
1303
  if ( 1 == $match_count ) {
1304
  $result['prefix'] = trim( strtolower( $matches[1] ) );
1305
  $tail = $matches[2];
 
 
 
 
 
 
 
 
 
 
 
 
 
1306
 
1307
  $match_count = preg_match( '/([^,=]+)((,|=)(.*))$/', $tail, $matches );
1308
  if ( 1 == $match_count ) {
1309
- $result['name'] = $matches[1];
 
 
 
 
1310
 
1311
  if ( ',' == $matches[3] ) {
1312
  $result['option'] = trim( strtolower( $matches[4] ));
@@ -1329,9 +1356,7 @@ class MLACore {
1329
  $result['value'] = $specification;
1330
  }
1331
 
1332
- /*
1333
- * Validate the results
1334
- */
1335
  if ( 'mime' == $result['prefix'] ) {
1336
  $mime_types = array_map( 'trim', explode( ',', $result['value'] ) );
1337
  foreach ( (array) $mime_types as $raw_mime_type ) {
@@ -1345,13 +1370,14 @@ class MLACore {
1345
  } elseif ( 'custom' == $result['prefix'] ) {
1346
  if ( ! in_array( $result['option'], array( '', 'any', 'match', 'null' ) ) ) {
1347
  /* translators: 1: ERROR tag 2: option, e.g., any, match, null */
1348
- $result['error'] = '<br>' . sprintf( __( '%1$s: Bad specification option "%2$s"', 'media-library-assistant' ), __( 'ERROR', 'media-library-assistant' ), $specification['option'] );
1349
  }
1350
  } else {
1351
  /* translators: 1: ERROR tag 2: prefix, e.g., custom */
1352
- $result['error'] = '<br>' . sprintf( __( '%1$s: Bad specification prefix "%2$s"', 'media-library-assistant' ), __( 'ERROR', 'media-library-assistant' ), $specification['prefix'] );
1353
  }
1354
 
 
1355
  return $result;
1356
  }
1357
 
@@ -1539,7 +1565,7 @@ class MLACore {
1539
  } else {
1540
  $callback_array = $wp_filter[ $filter ];
1541
  }
1542
-
1543
  foreach ( $callback_array as $priority => $callbacks ) {
1544
  $hook_list .= "Priority: {$priority}\n";
1545
  foreach ( $callbacks as $tag => $reference ) {
@@ -1552,7 +1578,7 @@ class MLACore {
1552
  } else {
1553
  $hook_list .= $reference['function'][0] . '::';
1554
  }
1555
-
1556
  $hook_list .= $reference['function'][1] . "()\n";
1557
  } else {
1558
  $hook_list .= 'unknown reference type: ' . gettype( $reference['function'] ) . "\n";
@@ -1560,7 +1586,7 @@ class MLACore {
1560
  } // foreach tag
1561
  } // foreach proprity
1562
  } // filters exist
1563
-
1564
  return $hook_list;
1565
  }
1566
 
21
  *
22
  * @var string
23
  */
24
+ const CURRENT_MLA_VERSION = '2.94';
25
 
26
  /**
27
  * Slug for registering and enqueueing plugin style sheets (moved from class-mla-main.php)
472
  // Template file and database access functions.
473
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-data-query.php' );
474
  MLAQuery::initialize();
475
+
476
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-data.php' );
477
  MLAData::initialize();
478
+
479
  // Shortcode shim functions
480
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-shortcodes.php' );
481
  MLAShortcodes::initialize();
482
+
483
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-shortcode-support.php' );
484
+
485
  // Plugin settings management
486
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-options.php' );
487
  MLAOptions::initialize();
693
  if ( '_wpnonce' !== $name ) {
694
  $actionurl = str_replace( '_wpnonce', $name, $actionurl );
695
  }
696
+
697
  return $actionurl;
698
  }
699
 
1245
  'update_post_meta_cache' => 'false',
1246
  'update_post_term_cache' => 'false',
1247
  );
1248
+
1249
  $specification = self::mla_parse_view_specification( $specification );
1250
  if ( 'mime' == $specification['prefix'] ) {
1251
  $query['post_mime_type'] = $specification['value'];
1255
  case 'match':
1256
  $patterns = array_map( 'trim', explode( ',', $specification['value'] ) );
1257
  foreach ( (array) $patterns as $pattern ) {
1258
+ $meta_key = ( !empty( $specification['name'] ) ) ? array( 'key' => $specification['name'] ) : array();
1259
  $pattern = preg_replace( '/\*+/', '%', $pattern );
1260
  if ( false !== strpos( $pattern, '%' ) ) {
1261
  // Preserve the pattern - it will be used in the "where" filter
1262
  $meta_query['patterns'][] = $pattern;
1263
+ $meta_query[] = array_merge( $meta_key, array( 'value' => $pattern, 'compare' => 'LIKE' ) );
1264
  } else {
1265
+ $meta_query[] = array_merge( $meta_key, array( 'value' => $pattern, 'compare' => '=' ) );
1266
  }
1267
  } // foreach pattern
1268
 
1272
 
1273
  break;
1274
  case 'null':
1275
+ if ( !empty( $specification['name'] ) ) {
1276
+ $meta_query['key'] = $specification['name'];
1277
+ }
1278
+
1279
  $meta_query['value'] = 'NULL';
1280
  break;
1281
  default: // '', 'any'
1282
+ if ( !empty( $specification['name'] ) ) {
1283
+ $meta_query[] = array( 'key' => $specification['name'], 'value' => NULL, 'compare' => '!=' );
1284
+ } else {
1285
+ $meta_query[] = array( 'value' => NULL, 'compare' => '!=' );
1286
+ }
1287
  }
1288
 
1289
  $query['meta_query'] = $meta_query;
1290
  } // custom field specification
1291
 
1292
+ //error_log( __LINE__ . ' MLACore::mla_prepare_view_query query = ' . var_export( $query, true ), 0 );
1293
  return $query;
1294
  }
1295
 
1306
  if ( is_array( $specification ) ) {
1307
  $specification = @implode( ',', $specification );
1308
  }
1309
+ //error_log( __LINE__ . ' MLACore::mla_parse_view_specification specification = ' . var_export( $specification, true ), 0 );
1310
 
1311
  $result = array( 'prefix' => '', 'name' => '', 'value' => '', 'option' => '' );
1312
  $match_count = preg_match( '/^(.+):(.+)/', $specification, $matches );
1313
  if ( 1 == $match_count ) {
1314
  $result['prefix'] = trim( strtolower( $matches[1] ) );
1315
  $tail = $matches[2];
1316
+ //error_log( __LINE__ . ' MLACore::mla_parse_view_specification tail = ' . var_export( $tail, true ), 0 );
1317
+
1318
+ // Look for field name(s)
1319
+ $flag = '<found multiple names in the tail>';
1320
+ $match_count = preg_match( '/([^=]+)((=)(.*))$/', $tail, $matches );
1321
+ if ( 1 == $match_count ) {
1322
+ //error_log( __LINE__ . ' MLACore::mla_parse_view_specification matches = ' . var_export( $matches, true ), 0 );
1323
+ $result['name'] = explode( ',', $matches[1] );
1324
+ // Flag multiple field names for preservation
1325
+ if ( 1 < count( $result['name'] ) ) {
1326
+ $tail = $flag . $matches[2];
1327
+ }
1328
+ }
1329
 
1330
  $match_count = preg_match( '/([^,=]+)((,|=)(.*))$/', $tail, $matches );
1331
  if ( 1 == $match_count ) {
1332
+ //error_log( __LINE__ . ' MLACore::mla_parse_view_specification matches = ' . var_export( $matches, true ), 0 );
1333
+ // Preserve multiple field names, handle "any field"; name = *
1334
+ if ( $flag !== $matches[1] ) {
1335
+ $result['name'] = ( '*' === $matches[1] ) ? '' : $matches[1];
1336
+ }
1337
 
1338
  if ( ',' == $matches[3] ) {
1339
  $result['option'] = trim( strtolower( $matches[4] ));
1356
  $result['value'] = $specification;
1357
  }
1358
 
1359
+ // Validate the results
 
 
1360
  if ( 'mime' == $result['prefix'] ) {
1361
  $mime_types = array_map( 'trim', explode( ',', $result['value'] ) );
1362
  foreach ( (array) $mime_types as $raw_mime_type ) {
1370
  } elseif ( 'custom' == $result['prefix'] ) {
1371
  if ( ! in_array( $result['option'], array( '', 'any', 'match', 'null' ) ) ) {
1372
  /* translators: 1: ERROR tag 2: option, e.g., any, match, null */
1373
+ $result['error'] = '<br>' . sprintf( __( '%1$s: Bad specification option "%2$s"', 'media-library-assistant' ), __( 'ERROR', 'media-library-assistant' ), $result['option'] );
1374
  }
1375
  } else {
1376
  /* translators: 1: ERROR tag 2: prefix, e.g., custom */
1377
+ $result['error'] = '<br>' . sprintf( __( '%1$s: Bad specification prefix "%2$s"', 'media-library-assistant' ), __( 'ERROR', 'media-library-assistant' ), $result['prefix'] );
1378
  }
1379
 
1380
+ //error_log( __LINE__ . ' MLACore::mla_parse_view_specification result = ' . var_export( $result, true ), 0 );
1381
  return $result;
1382
  }
1383
 
1565
  } else {
1566
  $callback_array = $wp_filter[ $filter ];
1567
  }
1568
+
1569
  foreach ( $callback_array as $priority => $callbacks ) {
1570
  $hook_list .= "Priority: {$priority}\n";
1571
  foreach ( $callbacks as $tag => $reference ) {
1578
  } else {
1579
  $hook_list .= $reference['function'][0] . '::';
1580
  }
1581
+
1582
  $hook_list .= $reference['function'][1] . "()\n";
1583
  } else {
1584
  $hook_list .= 'unknown reference type: ' . gettype( $reference['function'] ) . "\n";
1586
  } // foreach tag
1587
  } // foreach proprity
1588
  } // filters exist
1589
+
1590
  return $hook_list;
1591
  }
1592
 
includes/class-mla-data-query.php CHANGED
@@ -995,10 +995,10 @@ class MLAQuery {
995
  */
996
  case 's':
997
  switch ( substr( $value, 0, 3 ) ) {
998
- case '>|<':
999
  $clean_request['debug'] = 'console';
1000
  break;
1001
- case '<|>':
1002
  $clean_request['debug'] = 'log';
1003
  break;
1004
  }
@@ -1920,9 +1920,16 @@ class MLAQuery {
1920
 
1921
  // WordPress modifies the LIKE clause - which we must reverse
1922
  if ( isset( self::$query_parameters['patterns'] ) ) {
 
 
 
 
 
 
 
1923
  foreach ( self::$query_parameters['patterns'] as $pattern ) {
1924
- $pattern = str_replace( '_', '\\\\_', $pattern );
1925
- $match_clause = '%' . str_replace( '%', '\\\\%', $pattern ) . '%';
1926
  $where_clause = str_replace( "LIKE '{$match_clause}'", "LIKE '{$pattern}'", $where_clause );
1927
  }
1928
  }
995
  */
996
  case 's':
997
  switch ( substr( $value, 0, 3 ) ) {
998
+ case '}|{':
999
  $clean_request['debug'] = 'console';
1000
  break;
1001
+ case '{|}':
1002
  $clean_request['debug'] = 'log';
1003
  break;
1004
  }
1920
 
1921
  // WordPress modifies the LIKE clause - which we must reverse
1922
  if ( isset( self::$query_parameters['patterns'] ) ) {
1923
+ // WP after 4.8.3 replaces "%" with a long, complex placeholder
1924
+ if ( method_exists( $wpdb, 'placeholder_escape' ) ) {
1925
+ $placeholder = $wpdb->placeholder_escape();
1926
+ } else {
1927
+ $placeholder = '%';
1928
+ }
1929
+
1930
  foreach ( self::$query_parameters['patterns'] as $pattern ) {
1931
+ $pattern = str_replace( array( '_', '%' ), array( '\\\\_', $placeholder ), $pattern );
1932
+ $match_clause = $placeholder . str_replace( $placeholder, '\\\\' . $placeholder, $pattern ) . $placeholder;
1933
  $where_clause = str_replace( "LIKE '{$match_clause}'", "LIKE '{$pattern}'", $where_clause );
1934
  }
1935
  }
includes/class-mla-image-processor.php CHANGED
@@ -36,9 +36,7 @@ class MLAImageProcessor {
36
  private static function _get_temp_file( $extension = '.tmp' ) {
37
  static $temp = NULL;
38
 
39
- /*
40
- * Find a temp directory
41
- */
42
  if ( NULL == $temp ) {
43
  if ( function_exists('sys_get_temp_dir') ) {
44
  $temp = sys_get_temp_dir();
@@ -59,9 +57,7 @@ class MLAImageProcessor {
59
  }
60
  }
61
 
62
- /*
63
- * Create a unique file
64
- */
65
  $path = $temp . uniqid( mt_rand() ) . $extension;
66
  $f = @fopen( $path, 'a' );
67
  if ( $f === false ) {
@@ -97,18 +93,14 @@ class MLAImageProcessor {
97
  * @return boolean true if conversion succeeds else false
98
  */
99
  private static function _ghostscript_convert( $file, $frame, $resolution, $output_type, $explicit_path = '' ) {
100
- /*
101
- * Look for exec() - from http://stackoverflow.com/a/12980534/866618
102
- */
103
  $blacklist = preg_split( '/,\s*/', ini_get('disable_functions') . ',' . ini_get('suhosin.executor.func.blacklist') );
104
  if ( in_array('exec', $blacklist) ) {
105
  self::_mla_debug_add( 'MLAImageProcessor::_ghostscript_convert blacklist failure' );
106
  return false;
107
  }
108
 
109
- /*
110
- * Look for the Ghostscript executable
111
- */
112
  $ghostscript_path = NULL;
113
  do {
114
 
@@ -178,9 +170,7 @@ class MLAImageProcessor {
178
  $extension = '.png';
179
  }
180
 
181
- /*
182
- * Generate a unique temporary file
183
- */
184
  $output_file = self::_get_temp_file( $extension );
185
 
186
  $cmd = escapeshellarg( $ghostscript_path ) . ' -sDEVICE=%1$s -r%2$dx%2$d -dFirstPage=%3$d -dLastPage=%3$d -dFitPage -o %4$s %5$s 2>&1';
@@ -475,7 +465,7 @@ class MLAImageProcessor {
475
  ini_set( 'zlib.output_compression', 'Off' );
476
  }
477
 
478
- $file = wp_kses( isset( $_REQUEST['mla_stream_file'] ) ? wp_unslash( $_REQUEST['mla_stream_file'] ) : '', 'post' );
479
  if ( ! is_file( $file ) ) {
480
  self::_mla_die( 'File not found', __LINE__, 404 );
481
  }
@@ -491,7 +481,7 @@ class MLAImageProcessor {
491
  * If mla_ghostscript_path is present, a non-standard GS location can be found in a file written by
492
  * the [mla_gallery] shortcode processor.
493
  */
494
- $ghostscript_path = wp_kses( isset( $_REQUEST['mla_ghostscript_path'] ) ? wp_unslash( $_REQUEST['mla_ghostscript_path'] ) : '', 'post' );
495
  if ( ! empty( $ghostscript_path ) ) {
496
  $ghostscript_path = @file_get_contents( dirname( __FILE__ ) . '/' . 'mla-ghostscript-path.txt' );
497
  }
@@ -507,9 +497,7 @@ class MLAImageProcessor {
507
  self::_mla_debug_add( 'MLAImageProcessor::mla_process_stream_image begin file = ' . var_export( $file, true ) );
508
  }
509
 
510
- /*
511
- * Convert the file to an image format and load it
512
- */
513
  try {
514
  self::$image = new Imagick();
515
 
@@ -549,9 +537,7 @@ class MLAImageProcessor {
549
  self::_mla_die( 'Image load exception: ' . $e->getMessage(), __LINE__, 404 );
550
  }
551
 
552
- /*
553
- * Prepare the output image; resize and flatten, if necessary
554
- */
555
  try {
556
  if ( isset( $_REQUEST['mla_stream_fit'] ) ) {
557
  $best_fit = ( '1' == $_REQUEST['mla_stream_fit'] );
@@ -565,9 +551,7 @@ class MLAImageProcessor {
565
  self::_mla_die( '_prepare_image exception: ' . $e->getMessage(), __LINE__, 500 );
566
  }
567
 
568
- /*
569
- * Stream the image back to the requestor
570
- */
571
  try {
572
  header( "Content-Type: $type" );
573
  echo self::$image->getImageBlob(); // phpcs:ignore
@@ -649,9 +633,7 @@ class MLAMutex {
649
  * @return void
650
  */
651
  function __construct( $use_lock = false ) {
652
- /*
653
- * If sem_ functions are not available require file locking
654
- */
655
  if ( ! is_callable( 'sem_get' ) ) {
656
  $use_lock = true;
657
  }
36
  private static function _get_temp_file( $extension = '.tmp' ) {
37
  static $temp = NULL;
38
 
39
+ // Find a temp directory
 
 
40
  if ( NULL == $temp ) {
41
  if ( function_exists('sys_get_temp_dir') ) {
42
  $temp = sys_get_temp_dir();
57
  }
58
  }
59
 
60
+ // Create a unique file
 
 
61
  $path = $temp . uniqid( mt_rand() ) . $extension;
62
  $f = @fopen( $path, 'a' );
63
  if ( $f === false ) {
93
  * @return boolean true if conversion succeeds else false
94
  */
95
  private static function _ghostscript_convert( $file, $frame, $resolution, $output_type, $explicit_path = '' ) {
96
+ // Look for exec() - from http://stackoverflow.com/a/12980534/866618
 
 
97
  $blacklist = preg_split( '/,\s*/', ini_get('disable_functions') . ',' . ini_get('suhosin.executor.func.blacklist') );
98
  if ( in_array('exec', $blacklist) ) {
99
  self::_mla_debug_add( 'MLAImageProcessor::_ghostscript_convert blacklist failure' );
100
  return false;
101
  }
102
 
103
+ // Look for the Ghostscript executable
 
 
104
  $ghostscript_path = NULL;
105
  do {
106
 
170
  $extension = '.png';
171
  }
172
 
173
+ // Generate a unique temporary file
 
 
174
  $output_file = self::_get_temp_file( $extension );
175
 
176
  $cmd = escapeshellarg( $ghostscript_path ) . ' -sDEVICE=%1$s -r%2$dx%2$d -dFirstPage=%3$d -dLastPage=%3$d -dFitPage -o %4$s %5$s 2>&1';
465
  ini_set( 'zlib.output_compression', 'Off' );
466
  }
467
 
468
+ $file = isset( $_REQUEST['mla_stream_file'] ) ? $_REQUEST['mla_stream_file'] : ''; // phpcs:ignore
469
  if ( ! is_file( $file ) ) {
470
  self::_mla_die( 'File not found', __LINE__, 404 );
471
  }
481
  * If mla_ghostscript_path is present, a non-standard GS location can be found in a file written by
482
  * the [mla_gallery] shortcode processor.
483
  */
484
+ $ghostscript_path = isset( $_REQUEST['mla_ghostscript_path'] ) ? $_REQUEST['mla_ghostscript_path'] : ''; // phpcs:ignore
485
  if ( ! empty( $ghostscript_path ) ) {
486
  $ghostscript_path = @file_get_contents( dirname( __FILE__ ) . '/' . 'mla-ghostscript-path.txt' );
487
  }
497
  self::_mla_debug_add( 'MLAImageProcessor::mla_process_stream_image begin file = ' . var_export( $file, true ) );
498
  }
499
 
500
+ // Convert the file to an image format and load it
 
 
501
  try {
502
  self::$image = new Imagick();
503
 
537
  self::_mla_die( 'Image load exception: ' . $e->getMessage(), __LINE__, 404 );
538
  }
539
 
540
+ // Prepare the output image; resize and flatten, if necessary
 
 
541
  try {
542
  if ( isset( $_REQUEST['mla_stream_fit'] ) ) {
543
  $best_fit = ( '1' == $_REQUEST['mla_stream_fit'] );
551
  self::_mla_die( '_prepare_image exception: ' . $e->getMessage(), __LINE__, 500 );
552
  }
553
 
554
+ // Stream the image back to the requestor
 
 
555
  try {
556
  header( "Content-Type: $type" );
557
  echo self::$image->getImageBlob(); // phpcs:ignore
633
  * @return void
634
  */
635
  function __construct( $use_lock = false ) {
636
+ // If sem_ functions are not available require file locking
 
 
637
  if ( ! is_callable( 'sem_get' ) ) {
638
  $use_lock = true;
639
  }
includes/class-mla-list-table.php CHANGED
@@ -268,7 +268,8 @@ class MLA_List_Table extends WP_List_Table {
268
  'hierarchical' => true,
269
  'pad_counts' => false,
270
  'taxonomy' => $tax_filter,
271
- 'hide_if_empty' => false
 
272
  ), $dropdown_options );
273
 
274
  ob_start();
@@ -590,7 +591,6 @@ class MLA_List_Table extends WP_List_Table {
590
  * Use "@" because embedded arrays throw PHP Warnings from implode.
591
  */
592
  if ( is_array( $value ) ) {
593
- // $list[] = var_export( $value, true ); // Verbose output!
594
  $list[] = 'array( ' . @implode( ', ', $value ) . ' )';
595
  } elseif ( $is_meta ) {
596
  $list[] = $value;
268
  'hierarchical' => true,
269
  'pad_counts' => false,
270
  'taxonomy' => $tax_filter,
271
+ 'hide_if_empty' => false,
272
+ 'update_term_meta_cache' => false,
273
  ), $dropdown_options );
274
 
275
  ob_start();
591
  * Use "@" because embedded arrays throw PHP Warnings from implode.
592
  */
593
  if ( is_array( $value ) ) {
 
594
  $list[] = 'array( ' . @implode( ', ', $value ) . ' )';
595
  } elseif ( $is_meta ) {
596
  $list[] = $value;
includes/class-mla-main.php CHANGED
@@ -1714,9 +1714,9 @@ class MLA {
1714
  if ( !empty( $page_content['body'] ) ) {
1715
  if ( !empty( $page_content['message'] ) ) {
1716
  if ( false !== strpos( $page_content['message'], __( 'ERROR', 'media-library-assistant' ) ) ) {
1717
- $messages_class = 'mla_errors';
1718
  } else {
1719
- $messages_class = 'mla_messages';
1720
  }
1721
 
1722
  echo " <div class=\"" . esc_html( $messages_class ) . "\"><p>\n";
@@ -1733,7 +1733,7 @@ class MLA {
1733
  echo ' - ' . esc_html__( 'term search results for', 'media-library-assistant' ) . ' "' . esc_html( trim( sanitize_text_field( wp_unslash( $_REQUEST['mla_terms_search']['phrases'] ), 'post' ) ) ). "\"" . wp_kses( $heading_tail, 'post' );
1734
  } elseif ( !empty( $_REQUEST['s'] ) ) {
1735
  if ( empty( $_REQUEST['mla_search_fields'] ) ) {
1736
- echo ' - ' . esc_html__( 'post/parent results for', 'media-library-assistant' ) . ' "' . esc_html( trim( wp_kses( wp_unslash( $_REQUEST['s'] , 'post' ) ) ) ) . "\"" . wp_kses( $heading_tail, 'post' );
1737
  } else {
1738
  echo ' - ' . esc_html__( 'search results for', 'media-library-assistant' ) . ' "' . esc_html( trim( wp_kses( wp_unslash( $_REQUEST['s'] ), 'post' ) ) ) . "\"" . wp_kses( $heading_tail, 'post' );
1739
  }
@@ -1743,9 +1743,9 @@ class MLA {
1743
 
1744
  if ( !empty( $page_content['message'] ) ) {
1745
  if ( false !== strpos( $page_content['message'], __( 'ERROR', 'media-library-assistant' ) ) ) {
1746
- $messages_class = 'mla_errors';
1747
  } else {
1748
- $messages_class = 'mla_messages';
1749
  }
1750
 
1751
  echo " <div class=\"" . esc_html( $messages_class ) . "\"><p>\n";
@@ -2155,6 +2155,19 @@ class MLA {
2155
  return $time_edit_form;
2156
  }
2157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2158
  /**
2159
  * Build the hidden row templates for inline editing (quick and bulk edit)
2160
  *
@@ -2217,7 +2230,9 @@ class MLA {
2217
  foreach ( $hierarchical_taxonomies as $tax_name => $tax_object ) {
2218
  if ( current_user_can( $tax_object->cap->assign_terms ) ) {
2219
  ob_start();
 
2220
  wp_terms_checklist( NULL, array( 'taxonomy' => $tax_name, 'popular_cats' => array(), ) );
 
2221
  $tax_checklist = ob_get_contents();
2222
  ob_end_clean();
2223
 
@@ -2226,7 +2241,7 @@ class MLA {
2226
  'tax_attr' => esc_attr( $tax_name ),
2227
  'Add New Term' => __( '+&nbsp;Add&nbsp;New&nbsp;Term', 'media-library-assistant' ),
2228
  'Add Reader' => __( 'Add New', 'media-library-assistant' ) . ' ' . esc_html( $tax_object->labels->singular_name ),
2229
- 'tax_parents' => wp_dropdown_categories( array( 'taxonomy' => $tax_name, 'hide_empty' => 0, 'name' => "new{$tax_name}_parent", 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '&mdash; ' . $tax_object->labels->parent_item . ' &mdash;', 'echo' => 0 ) ),
2230
  'Add Button' => esc_html( $tax_object->labels->add_new_item ),
2231
  'ajax_nonce_field' => wp_nonce_field( 'add-'.$tax_name, '_ajax_nonce-add-'.$tax_name, false ),
2232
  );
@@ -2281,7 +2296,9 @@ class MLA {
2281
  if ( current_user_can( $tax_object->cap->assign_terms ) ) {
2282
  if ( MLACore::mla_taxonomy_support( $tax_name, 'flat-checklist' ) ) {
2283
  ob_start();
 
2284
  wp_terms_checklist( NULL, array( 'taxonomy' => $tax_name, 'popular_cats' => array(), ) );
 
2285
  $tax_checklist = ob_get_contents();
2286
  ob_end_clean();
2287
 
1714
  if ( !empty( $page_content['body'] ) ) {
1715
  if ( !empty( $page_content['message'] ) ) {
1716
  if ( false !== strpos( $page_content['message'], __( 'ERROR', 'media-library-assistant' ) ) ) {
1717
+ $messages_class = 'updated error';
1718
  } else {
1719
+ $messages_class = 'updated notice is-dismissible';
1720
  }
1721
 
1722
  echo " <div class=\"" . esc_html( $messages_class ) . "\"><p>\n";
1733
  echo ' - ' . esc_html__( 'term search results for', 'media-library-assistant' ) . ' "' . esc_html( trim( sanitize_text_field( wp_unslash( $_REQUEST['mla_terms_search']['phrases'] ), 'post' ) ) ). "\"" . wp_kses( $heading_tail, 'post' );
1734
  } elseif ( !empty( $_REQUEST['s'] ) ) {
1735
  if ( empty( $_REQUEST['mla_search_fields'] ) ) {
1736
+ echo ' - ' . esc_html__( 'post/parent results for', 'media-library-assistant' ) . ' "' . esc_html( trim( wp_kses( wp_unslash( $_REQUEST['s'] ) , 'post' ) ) ) . "\"" . wp_kses( $heading_tail, 'post' );
1737
  } else {
1738
  echo ' - ' . esc_html__( 'search results for', 'media-library-assistant' ) . ' "' . esc_html( trim( wp_kses( wp_unslash( $_REQUEST['s'] ), 'post' ) ) ) . "\"" . wp_kses( $heading_tail, 'post' );
1739
  }
1743
 
1744
  if ( !empty( $page_content['message'] ) ) {
1745
  if ( false !== strpos( $page_content['message'], __( 'ERROR', 'media-library-assistant' ) ) ) {
1746
+ $messages_class = 'updated error';
1747
  } else {
1748
+ $messages_class = 'updated notice is-dismissible';
1749
  }
1750
 
1751
  echo " <div class=\"" . esc_html( $messages_class ) . "\"><p>\n";
2155
  return $time_edit_form;
2156
  }
2157
 
2158
+ /**
2159
+ * Suppress term_meta cache update for taxonomy checklists
2160
+ *
2161
+ * @since 2.94
2162
+ *
2163
+ * @param array $args An array of get_terms() arguments.
2164
+ * @param string[] $taxonomies An array of taxonomy names.
2165
+ */
2166
+ public static function mla_get_terms_args_filter( $args, $taxonomies ) {
2167
+ $args['update_term_meta_cache'] = false;
2168
+ return $args;
2169
+ }
2170
+
2171
  /**
2172
  * Build the hidden row templates for inline editing (quick and bulk edit)
2173
  *
2230
  foreach ( $hierarchical_taxonomies as $tax_name => $tax_object ) {
2231
  if ( current_user_can( $tax_object->cap->assign_terms ) ) {
2232
  ob_start();
2233
+ add_filter( 'get_terms_args', 'MLA::mla_get_terms_args_filter', 10, 2 );
2234
  wp_terms_checklist( NULL, array( 'taxonomy' => $tax_name, 'popular_cats' => array(), ) );
2235
+ remove_filter( 'get_terms_args', 'MLA::mla_get_terms_args_filter', 10, 2 );
2236
  $tax_checklist = ob_get_contents();
2237
  ob_end_clean();
2238
 
2241
  'tax_attr' => esc_attr( $tax_name ),
2242
  'Add New Term' => __( '+&nbsp;Add&nbsp;New&nbsp;Term', 'media-library-assistant' ),
2243
  'Add Reader' => __( 'Add New', 'media-library-assistant' ) . ' ' . esc_html( $tax_object->labels->singular_name ),
2244
+ 'tax_parents' => wp_dropdown_categories( array( 'taxonomy' => $tax_name, 'hide_empty' => 0, 'name' => "new{$tax_name}_parent", 'orderby' => 'name', 'hierarchical' => 1, 'show_option_none' => '&mdash; ' . $tax_object->labels->parent_item . ' &mdash;', 'echo' => 0, 'update_term_meta_cache' => false ) ),
2245
  'Add Button' => esc_html( $tax_object->labels->add_new_item ),
2246
  'ajax_nonce_field' => wp_nonce_field( 'add-'.$tax_name, '_ajax_nonce-add-'.$tax_name, false ),
2247
  );
2296
  if ( current_user_can( $tax_object->cap->assign_terms ) ) {
2297
  if ( MLACore::mla_taxonomy_support( $tax_name, 'flat-checklist' ) ) {
2298
  ob_start();
2299
+ add_filter( 'get_terms_args', 'MLA::mla_get_terms_args_filter', 10, 2 );
2300
  wp_terms_checklist( NULL, array( 'taxonomy' => $tax_name, 'popular_cats' => array(), ) );
2301
+ remove_filter( 'get_terms_args', 'MLA::mla_get_terms_args_filter', 10, 2 );
2302
  $tax_checklist = ob_get_contents();
2303
  ob_end_clean();
2304
 
includes/class-mla-settings.php CHANGED
@@ -1486,7 +1486,8 @@ class MLASettings {
1486
  $dismiss_button = '';
1487
  } else {
1488
  $messages_class = 'updated notice is-dismissible';
1489
- $dismiss_button = " <button class=\"notice-dismiss\" type=\"button\"><span class=\"screen-reader-text\">[+dismiss_text+].</span></button>\n";
 
1490
  }
1491
 
1492
  $page_values['messages'] = MLAData::mla_parse_template( self::$page_template_array['messages'], array(
1486
  $dismiss_button = '';
1487
  } else {
1488
  $messages_class = 'updated notice is-dismissible';
1489
+ // $dismiss_button = " <button class=\"notice-dismiss\" type=\"button\"><span class=\"screen-reader-text\">[+dismiss_text+].</span></button>\n";
1490
+ $dismiss_button = ''; // /wp-admin/js/common.js function makeNoticesDismissible() since WP 4.4.0
1491
  }
1492
 
1493
  $page_values['messages'] = MLAData::mla_parse_template( self::$page_template_array['messages'], array(
includes/class-mla-shortcode-support.php CHANGED
@@ -6,9 +6,7 @@
6
  * @since 2.20
7
  */
8
 
9
- /*
10
- * The MLA database access functions aren't available to "front end" posts/pages
11
- */
12
  if ( !class_exists( 'MLAQuery' ) ) {
13
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-data-query.php' );
14
  MLAQuery::initialize();
@@ -178,7 +176,19 @@ class MLAShortcode_Support {
178
  public static function _get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
179
  static $nested_call = false;
180
 
181
- if ( $nested_call || ( $image && ( 'icon_only' !== self::$size_parameter ) && ( 'icon_feature' !== self::$size_parameter ) ) ) {
 
 
 
 
 
 
 
 
 
 
 
 
182
  return $image;
183
  }
184
 
@@ -197,16 +207,19 @@ class MLAShortcode_Support {
197
  }
198
  } // enable_featured_image
199
 
200
- if ( $src = wp_mime_type_icon( $attachment_id ) ) {
201
- /** This filter is documented in wp-includes/post.php */
202
- $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
203
-
204
- $src_file = $icon_dir . '/' . wp_basename( $src );
205
- @list( $width, $height ) = getimagesize( $src_file );
206
- }
207
-
208
- if ( $src && $width && $height ) {
209
- $image = array( $src, $width, $height );
 
 
 
210
  }
211
 
212
  return $image;
@@ -480,6 +493,7 @@ class MLAShortcode_Support {
480
  'mla_margin' => MLACore::mla_get_option('mla_gallery_margin'),
481
  'mla_target' => '',
482
  'mla_debug' => false,
 
483
 
484
  'mla_named_transfer' => false,
485
  'mla_viewer' => false,
@@ -853,16 +867,12 @@ class MLAShortcode_Support {
853
  }
854
  } // mla_alt_shortcode
855
 
856
- if ( 'icon' == strtolower( $size) ) {
857
  if ( 'checked' == MLACore::mla_get_option( MLACoreOptions::MLA_ENABLE_MLA_ICONS ) ) {
858
  $size = array( 64, 64 );
859
  } else {
860
  $size = array( 60, 60 );
861
  }
862
-
863
- $show_icon = true;
864
- } else {
865
- $show_icon = false;
866
  }
867
 
868
  // Feeds such as RSS, Atom or RDF do not require styled and formatted output
@@ -1324,8 +1334,9 @@ class MLAShortcode_Support {
1324
  } else {
1325
  add_filter( 'wp_get_attachment_image_src', 'MLAShortcode_Support::_get_attachment_image_src', 10, 4 );
1326
 
1327
- $item_values['pagelink'] = wp_get_attachment_link($attachment->ID, $size, true, $show_icon, $link_text);
1328
- $item_values['filelink'] = wp_get_attachment_link($attachment->ID, $size, false, $show_icon, $link_text);
 
1329
 
1330
  remove_filter( 'wp_get_attachment_image_src', 'MLAShortcode_Support::_get_attachment_image_src' );
1331
  }
@@ -4369,6 +4380,15 @@ class MLAShortcode_Support {
4369
  */
4370
  private static $query_parameters = array();
4371
 
 
 
 
 
 
 
 
 
 
4372
  /**
4373
  * Checks for valid, perhaps nested PHP array specification
4374
  *
@@ -4380,9 +4400,12 @@ class MLAShortcode_Support {
4380
  */
4381
  private static function _validate_array_specification( $specification ) {
4382
  //error_log( __LINE__ . " _validate_array_specification() specification = " . var_export( $specification, true ), 0 );
 
4383
 
4384
  // Check for outer array specification(s) and reject anything else.
4385
  if ( 1 !== preg_match( '/^array\s*\((.*)\)[\s\,]*$/', $specification, $matches ) ) {
 
 
4386
  return false;
4387
  }
4388
 
@@ -4395,6 +4418,8 @@ class MLAShortcode_Support {
4395
  foreach ( $matches[0] as $search ) {
4396
  // Replace valid arrays with a harmless literal value
4397
  if ( false === self::_validate_array_specification( $search ) ) {
 
 
4398
  return false;
4399
  }
4400
 
@@ -4431,6 +4456,7 @@ class MLAShortcode_Support {
4431
  //error_log( __LINE__ . " _validate_array_specification() boolean and array() matches = " . var_export( $matches[10], true ), 0 );
4432
  if ( false === in_array( strtolower( $matches[11] ), array( 'false', 'true', 'array' ) ) ) {
4433
  //error_log( __LINE__ . " _validate_array_specification() FAILED boolean matches = " . var_export( $matches[7], true ), 0 );
 
4434
  return false;
4435
  }
4436
  }
@@ -4443,11 +4469,12 @@ class MLAShortcode_Support {
4443
  if ( 1 === preg_match( '/^(([\'\"](.+?)[\'\"])|(\d+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))(.*)$/', $interior, $matches ) ) {
4444
  //error_log( __LINE__ . " _validate_array_specification() simple matches = " . var_export( $matches, true ), 0 );
4445
 
4446
- $interior = trim( $matches[5], ' ,' );
4447
  continue;
4448
  }
4449
 
4450
  //error_log( __LINE__ . " _validate_array_specification() FAILED interior = " . var_export( $interior, true ), 0 );
 
4451
  return false;
4452
  }
4453
 
@@ -4477,11 +4504,12 @@ class MLAShortcode_Support {
4477
  //error_log( __LINE__ . " _sanitize_query_specification() candidate = " . var_export( $candidate, true ), 0 );
4478
 
4479
  // Check for nested array specification(s) and reject anything else.
4480
- if ( self::_validate_array_specification( $candidate ) ) {
 
4481
  return $candidate;
4482
  }
4483
 
4484
- //error_log( __LINE__ . " _sanitize_query_specification() FAILED", 0 );
4485
  return 'false';
4486
  }
4487
 
@@ -4691,6 +4719,7 @@ class MLAShortcode_Support {
4691
  'cache_results' => NULL,
4692
  'update_post_meta_cache' => NULL,
4693
  'update_post_term_cache' => NULL,
 
4694
  );
4695
 
4696
  /**
@@ -4791,6 +4820,9 @@ class MLAShortcode_Support {
4791
  $mla_page_parameter = $arguments['mla_page_parameter'];
4792
  unset( $arguments['mla_page_parameter'] );
4793
 
 
 
 
4794
  /*
4795
  * $mla_page_parameter, if set, doesn't make it through the shortcode_atts filter,
4796
  * so we handle it separately
@@ -4840,6 +4872,11 @@ class MLAShortcode_Support {
4840
  // Replace invalid queries from "where-used" callers with a harmless equivalent
4841
  if ( $where_used_query && ( ( 'false' === $value ) || ( false !== strpos( $value, '{+' ) ) ) ) {
4842
  $value = "array( array( 'taxonomy' => 'none', 'field' => 'slug', 'terms' => 'none' ) )";
 
 
 
 
 
4843
  }
4844
 
4845
  try {
@@ -5274,6 +5311,11 @@ class MLAShortcode_Support {
5274
  // Replace invalid queries from "where-used" callers with a harmless equivalent
5275
  if ( $where_used_query && ( ( 'false' === $value ) || ( false !== strpos( $value, '{+' ) ) ) ) {
5276
  $value = "array( array( 'key' => 'unlikely', 'value' => 'none or otherwise unlikely' ) )";
 
 
 
 
 
5277
  }
5278
 
5279
  try {
@@ -5305,6 +5347,11 @@ class MLAShortcode_Support {
5305
  // Replace invalid queries from "where-used" callers with a harmless equivalent
5306
  if ( $where_used_query && ( ( 'false' === $value ) || ( false !== strpos( $value, '{+' ) ) ) ) {
5307
  $value = "array( array( 'key' => 'unlikely', 'value' => 'none or otherwise unlikely' ) )";
 
 
 
 
 
5308
  }
5309
 
5310
  try {
@@ -5563,6 +5610,16 @@ class MLAShortcode_Support {
5563
  add_filter( 'pmpro_search_filter_post_types', 'MLAShortcode_Support::mla_pmp_hide_attachments_filter' );
5564
  }
5565
 
 
 
 
 
 
 
 
 
 
 
5566
  MLAShortcodes::$mla_gallery_wp_query_object = new WP_Query;
5567
  $attachments = MLAShortcodes::$mla_gallery_wp_query_object->query( $query_arguments );
5568
 
@@ -5643,10 +5700,11 @@ class MLAShortcode_Support {
5643
  * Set for taxonomy queries unless post_parent=current. If true, we must disable
5644
  * the LEFT JOIN clause that get_posts() adds to taxonomy queries.
5645
  * We leave the clause in because the WHERE clauses refer to "p2.".
5646
- */
 
5647
  if ( self::$query_parameters['disable_tax_join'] ) {
5648
  $join_clause = str_replace( " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) ", " LEFT JOIN {$wpdb->posts} AS p2 ON (p2.ID = p2.ID) ", $join_clause );
5649
- }
5650
 
5651
  // These joins support the 'terms' search_field
5652
  if ( isset( MLAQuery::$search_parameters['tax_terms_count'] ) ) {
@@ -5704,6 +5762,14 @@ class MLAShortcode_Support {
5704
  MLACore::mla_debug_add( '<strong>' . __( 'mla_debug WHERE filter', 'media-library-assistant' ) . '</strong> = ' . var_export( $where_clause, true ) );
5705
  }
5706
 
 
 
 
 
 
 
 
 
5707
  if ( strpos( $where_clause, "post_type = 'attachment'" ) ) {
5708
  $where_clause = str_replace( "post_type = 'attachment'", "post_type = 'attachment'", $where_clause );
5709
  }
@@ -5944,7 +6010,7 @@ class MLAShortcode_Support {
5944
 
5945
  // Support Simple Taxonomy Ordering plugin
5946
  if ( 'tax_position' === strtolower( $arguments['orderby'] ) ) {
5947
- if ( class_exists( 'Yikes_Custom_Taxonomy_Order' ) ) {
5948
  $field_array[] = ' term_meta.meta_value AS tax_position';
5949
  } else {
5950
  $arguments['orderby'] = 'name';
@@ -5953,7 +6019,7 @@ class MLAShortcode_Support {
5953
 
5954
  // Support Simple Custom Post Order plugin
5955
  if ( 'term_order' === strtolower( $arguments['orderby'] ) ) {
5956
- if ( class_exists( 'SCPO_Engine' ) ) {
5957
  $field_array[] = ' t.term_order';
5958
  } else {
5959
  $arguments['orderby'] = 'name';
@@ -6109,13 +6175,13 @@ class MLAShortcode_Support {
6109
 
6110
  // Add sort order
6111
  if ( 'none' !== strtolower( $arguments['orderby'] ) ) {
6112
- if ( ( 'tax_position' === strtolower( $arguments['orderby'] ) ) && class_exists( 'Yikes_Custom_Taxonomy_Order' ) ) {
6113
  // Support Simple Taxonomy Ordering plugin
6114
  $yikes_custom_taxonomy_order = Yikes_Custom_Taxonomy_Order::get_instance();
6115
  $clauses = $yikes_custom_taxonomy_order->set_tax_order( $clauses, $taxonomies, array() );
6116
  // Adjust the orderby clause to account for the subquery and the alias in the fields[] clause
6117
  $clauses['orderby'] = 'ORDER BY CAST( tax_position AS UNSIGNED )';
6118
- } elseif ( ( 'term_order' === strtolower( $arguments['orderby'] ) ) && class_exists( 'SCPO_Engine' ) ) {
6119
  // Support Simple Custom Post Order plugin
6120
  $clauses['orderby'] = 'ORDER BY term_order';
6121
  } else {
@@ -6366,7 +6432,7 @@ class MLAShortcode_Support {
6366
 
6367
  if ( $exclude_tree ) {
6368
  self::_remove_exclude_tree( $term_tree[ $taxonomy ], $exclude_tree );
6369
- } // $include_tree
6370
 
6371
  $term_count = 0;
6372
  $root_limit = count( $term_tree[ $taxonomy ] );
6
  * @since 2.20
7
  */
8
 
9
+ // The MLA database access functions aren't available to "front end" posts/pages
 
 
10
  if ( !class_exists( 'MLAQuery' ) ) {
11
  require_once( MLA_PLUGIN_PATH . 'includes/class-mla-data-query.php' );
12
  MLAQuery::initialize();
176
  public static function _get_attachment_image_src( $image, $attachment_id, $size, $icon ) {
177
  static $nested_call = false;
178
 
179
+ if ( $nested_call ) {
180
+ return $image;
181
+ }
182
+
183
+ if ( 'none' === self::$size_parameter ) {
184
+ return false;
185
+ } elseif ( ( 'icon_only' === self::$size_parameter ) || ( 'icon_feature' === self::$size_parameter ) ) {
186
+ // No native images allowed
187
+ $image = false;
188
+ }
189
+
190
+ // If a native image exists, we're done
191
+ if ( false !== $image ) {
192
  return $image;
193
  }
194
 
207
  }
208
  } // enable_featured_image
209
 
210
+ // For any of the three "icon" variations, try to substitute an icon image
211
+ if ( 0 === strpos( self::$size_parameter, 'icon' ) ) {
212
+ if ( $src = wp_mime_type_icon( $attachment_id ) ) {
213
+ /** This filter is documented in wp-includes/post.php */
214
+ $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/media' );
215
+
216
+ $src_file = $icon_dir . '/' . wp_basename( $src );
217
+ @list( $width, $height ) = getimagesize( $src_file );
218
+ }
219
+
220
+ if ( $src && $width && $height ) {
221
+ $image = array( $src, $width, $height );
222
+ }
223
  }
224
 
225
  return $image;
493
  'mla_margin' => MLACore::mla_get_option('mla_gallery_margin'),
494
  'mla_target' => '',
495
  'mla_debug' => false,
496
+ 'mla_allow_rml' => false,
497
 
498
  'mla_named_transfer' => false,
499
  'mla_viewer' => false,
867
  }
868
  } // mla_alt_shortcode
869
 
870
+ if ( 'icon' == strtolower( $size ) ) {
871
  if ( 'checked' == MLACore::mla_get_option( MLACoreOptions::MLA_ENABLE_MLA_ICONS ) ) {
872
  $size = array( 64, 64 );
873
  } else {
874
  $size = array( 60, 60 );
875
  }
 
 
 
 
876
  }
877
 
878
  // Feeds such as RSS, Atom or RDF do not require styled and formatted output
1334
  } else {
1335
  add_filter( 'wp_get_attachment_image_src', 'MLAShortcode_Support::_get_attachment_image_src', 10, 4 );
1336
 
1337
+ // The fourth argument, "show icon" is always false because we handle it in _get_attachment_image_src()
1338
+ $item_values['pagelink'] = wp_get_attachment_link($attachment->ID, $size, true, false, $link_text);
1339
+ $item_values['filelink'] = wp_get_attachment_link($attachment->ID, $size, false, false, $link_text);
1340
 
1341
  remove_filter( 'wp_get_attachment_image_src', 'MLAShortcode_Support::_get_attachment_image_src' );
1342
  }
4380
  */
4381
  private static $query_parameters = array();
4382
 
4383
+ /**
4384
+ * Error details from _validate_array_specification()
4385
+ *
4386
+ * @since 2.94
4387
+ *
4388
+ * @var string
4389
+ */
4390
+ private static $array_specification_error = '';
4391
+
4392
  /**
4393
  * Checks for valid, perhaps nested PHP array specification
4394
  *
4400
  */
4401
  private static function _validate_array_specification( $specification ) {
4402
  //error_log( __LINE__ . " _validate_array_specification() specification = " . var_export( $specification, true ), 0 );
4403
+ self::$array_specification_error = '';
4404
 
4405
  // Check for outer array specification(s) and reject anything else.
4406
  if ( 1 !== preg_match( '/^array\s*\((.*)\)[\s\,]*$/', $specification, $matches ) ) {
4407
+ //error_log( __LINE__ . " _validate_array_specification() specification = " . var_export( $specification, true ), 0 );
4408
+ self::$array_specification_error = " FAILED outer array = " . var_export( $specification, true );
4409
  return false;
4410
  }
4411
 
4418
  foreach ( $matches[0] as $search ) {
4419
  // Replace valid arrays with a harmless literal value
4420
  if ( false === self::_validate_array_specification( $search ) ) {
4421
+ self::$array_specification_error = " FAILED nested array = " . var_export( $search, true );
4422
+ //error_log( __LINE__ . " _validate_array_specification() search = " . var_export( $search, true ), 0 );
4423
  return false;
4424
  }
4425
 
4456
  //error_log( __LINE__ . " _validate_array_specification() boolean and array() matches = " . var_export( $matches[10], true ), 0 );
4457
  if ( false === in_array( strtolower( $matches[11] ), array( 'false', 'true', 'array' ) ) ) {
4458
  //error_log( __LINE__ . " _validate_array_specification() FAILED boolean matches = " . var_export( $matches[7], true ), 0 );
4459
+ self::$array_specification_error = " FAILED boolean matches = " . var_export( $matches[7], true );
4460
  return false;
4461
  }
4462
  }
4469
  if ( 1 === preg_match( '/^(([\'\"](.+?)[\'\"])|(\d+)|([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))(.*)$/', $interior, $matches ) ) {
4470
  //error_log( __LINE__ . " _validate_array_specification() simple matches = " . var_export( $matches, true ), 0 );
4471
 
4472
+ $interior = trim( $matches[6], ' ,' );
4473
  continue;
4474
  }
4475
 
4476
  //error_log( __LINE__ . " _validate_array_specification() FAILED interior = " . var_export( $interior, true ), 0 );
4477
+ self::$array_specification_error = " FAILED interior = " . var_export( $interior, true );
4478
  return false;
4479
  }
4480
 
4504
  //error_log( __LINE__ . " _sanitize_query_specification() candidate = " . var_export( $candidate, true ), 0 );
4505
 
4506
  // Check for nested array specification(s) and reject anything else.
4507
+ $result = self::_validate_array_specification( $candidate );
4508
+ if ( true === $result ) {
4509
  return $candidate;
4510
  }
4511
 
4512
+ //error_log( __LINE__ . " _sanitize_query_specification() FAILED array_specification_error = " . var_export( self::$array_specification_error, true ), 0 );
4513
  return 'false';
4514
  }
4515
 
4719
  'cache_results' => NULL,
4720
  'update_post_meta_cache' => NULL,
4721
  'update_post_term_cache' => NULL,
4722
+ 'mla_allow_rml' => false, // RML Support, for Phil Boyd
4723
  );
4724
 
4725
  /**
4820
  $mla_page_parameter = $arguments['mla_page_parameter'];
4821
  unset( $arguments['mla_page_parameter'] );
4822
 
4823
+ // Convert to boolean
4824
+ $arguments['mla_allow_rml'] = 'true' === ( ( ! empty( $arguments['mla_allow_rml'] ) ) ? trim( strtolower( $arguments['mla_allow_rml'] ) ) : 'false' );
4825
+
4826
  /*
4827
  * $mla_page_parameter, if set, doesn't make it through the shortcode_atts filter,
4828
  * so we handle it separately
4872
  // Replace invalid queries from "where-used" callers with a harmless equivalent
4873
  if ( $where_used_query && ( ( 'false' === $value ) || ( false !== strpos( $value, '{+' ) ) ) ) {
4874
  $value = "array( array( 'taxonomy' => 'none', 'field' => 'slug', 'terms' => 'none' ) )";
4875
+ } else {
4876
+ // If sanitization fails, return an error message
4877
+ if ( 'false' === $value ) {
4878
+ return '<p>' . __( 'ERROR', 'media-library-assistant' ) . ': ' . __( 'Invalid mla_gallery', 'media-library-assistant' ) . ' tax_query = ' . self::$array_specification_error . '</p>';
4879
+ }
4880
  }
4881
 
4882
  try {
5311
  // Replace invalid queries from "where-used" callers with a harmless equivalent
5312
  if ( $where_used_query && ( ( 'false' === $value ) || ( false !== strpos( $value, '{+' ) ) ) ) {
5313
  $value = "array( array( 'key' => 'unlikely', 'value' => 'none or otherwise unlikely' ) )";
5314
+ } else {
5315
+ // If sanitization fails, return an error message
5316
+ if ( 'false' === $value ) {
5317
+ return '<p>' . __( 'ERROR', 'media-library-assistant' ) . ': ' . __( 'Invalid mla_gallery', 'media-library-assistant' ) . ' date_query = ' . self::$array_specification_error . '</p>';
5318
+ }
5319
  }
5320
 
5321
  try {
5347
  // Replace invalid queries from "where-used" callers with a harmless equivalent
5348
  if ( $where_used_query && ( ( 'false' === $value ) || ( false !== strpos( $value, '{+' ) ) ) ) {
5349
  $value = "array( array( 'key' => 'unlikely', 'value' => 'none or otherwise unlikely' ) )";
5350
+ } else {
5351
+ // If sanitization fails, return an error message
5352
+ if ( 'false' === $value ) {
5353
+ return '<p>' . __( 'ERROR', 'media-library-assistant' ) . ': ' . __( 'Invalid mla_gallery', 'media-library-assistant' ) . ' meta_query = ' . self::$array_specification_error . '</p>';
5354
+ }
5355
  }
5356
 
5357
  try {
5610
  add_filter( 'pmpro_search_filter_post_types', 'MLAShortcode_Support::mla_pmp_hide_attachments_filter' );
5611
  }
5612
 
5613
+ if ( isset( $query_arguments['post_type'] ) && is_string( $query_arguments['post_type'] ) && ( 'attachment' === $query_arguments['post_type'] ) ) {
5614
+ if ( self::$query_parameters['disable_tax_join'] ) {
5615
+ // Suppress WordPress WP_Query LEFT JOIN on post_parent, etc.
5616
+ $query_arguments['post_type'] = 'mladisabletaxjoin';
5617
+ } elseif ( defined('RML_FILE') && ( false === $arguments['mla_allow_rml'] ) ) {
5618
+ // Suppress RML additions to MLA queries
5619
+ $query_arguments['post_type'] = 'mladisablerml';
5620
+ }
5621
+ } // post_type is attachment
5622
+
5623
  MLAShortcodes::$mla_gallery_wp_query_object = new WP_Query;
5624
  $attachments = MLAShortcodes::$mla_gallery_wp_query_object->query( $query_arguments );
5625
 
5700
  * Set for taxonomy queries unless post_parent=current. If true, we must disable
5701
  * the LEFT JOIN clause that get_posts() adds to taxonomy queries.
5702
  * We leave the clause in because the WHERE clauses refer to "p2.".
5703
+ * Replaced in MLA v2.94 by "post_type = 'mladisabletaxjoin'"
5704
+ * /
5705
  if ( self::$query_parameters['disable_tax_join'] ) {
5706
  $join_clause = str_replace( " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) ", " LEFT JOIN {$wpdb->posts} AS p2 ON (p2.ID = p2.ID) ", $join_clause );
5707
+ } // */
5708
 
5709
  // These joins support the 'terms' search_field
5710
  if ( isset( MLAQuery::$search_parameters['tax_terms_count'] ) ) {
5762
  MLACore::mla_debug_add( '<strong>' . __( 'mla_debug WHERE filter', 'media-library-assistant' ) . '</strong> = ' . var_export( $where_clause, true ) );
5763
  }
5764
 
5765
+ // Reverse post_type modification used to avoid redundant LEFT JOIN insertion or RML folder insertion
5766
+ if ( strpos( $where_clause, "post_type = 'mladisabletaxjoin'" ) ) {
5767
+ $where_clause = str_replace( "post_type = 'mladisabletaxjoin'", "post_type = 'attachment'", $where_clause );
5768
+ } elseif ( strpos( $where_clause, "post_type = 'mladisablerml'" ) ) {
5769
+ $where_clause = str_replace( "post_type = 'mladisablerml'", "post_type = 'attachment'", $where_clause );
5770
+ }
5771
+
5772
+ // Add whitespace to prevent Role Scoper plugin clause modification
5773
  if ( strpos( $where_clause, "post_type = 'attachment'" ) ) {
5774
  $where_clause = str_replace( "post_type = 'attachment'", "post_type = 'attachment'", $where_clause );
5775
  }
6010
 
6011
  // Support Simple Taxonomy Ordering plugin
6012
  if ( 'tax_position' === strtolower( $arguments['orderby'] ) ) {
6013
+ if ( class_exists( 'Yikes_Custom_Taxonomy_Order', false ) ) {
6014
  $field_array[] = ' term_meta.meta_value AS tax_position';
6015
  } else {
6016
  $arguments['orderby'] = 'name';
6019
 
6020
  // Support Simple Custom Post Order plugin
6021
  if ( 'term_order' === strtolower( $arguments['orderby'] ) ) {
6022
+ if ( class_exists( 'SCPO_Engine', false ) ) {
6023
  $field_array[] = ' t.term_order';
6024
  } else {
6025
  $arguments['orderby'] = 'name';
6175
 
6176
  // Add sort order
6177
  if ( 'none' !== strtolower( $arguments['orderby'] ) ) {
6178
+ if ( ( 'tax_position' === strtolower( $arguments['orderby'] ) ) && class_exists( 'Yikes_Custom_Taxonomy_Order', false ) ) {
6179
  // Support Simple Taxonomy Ordering plugin
6180
  $yikes_custom_taxonomy_order = Yikes_Custom_Taxonomy_Order::get_instance();
6181
  $clauses = $yikes_custom_taxonomy_order->set_tax_order( $clauses, $taxonomies, array() );
6182
  // Adjust the orderby clause to account for the subquery and the alias in the fields[] clause
6183
  $clauses['orderby'] = 'ORDER BY CAST( tax_position AS UNSIGNED )';
6184
+ } elseif ( ( 'term_order' === strtolower( $arguments['orderby'] ) ) && class_exists( 'SCPO_Engine', false ) ) {
6185
  // Support Simple Custom Post Order plugin
6186
  $clauses['orderby'] = 'ORDER BY term_order';
6187
  } else {
6432
 
6433
  if ( $exclude_tree ) {
6434
  self::_remove_exclude_tree( $term_tree[ $taxonomy ], $exclude_tree );
6435
+ }
6436
 
6437
  $term_count = 0;
6438
  $root_limit = count( $term_tree[ $taxonomy ] );
includes/mla-stream-image.php CHANGED
@@ -9,7 +9,7 @@
9
  /*
10
  * Process mla_viewer image stream requests
11
  */
12
- //@ini_set('error_log','C:\Program Files (x86)\Apache Software Foundation\Apache24\logs\php-errors.log');
13
 
14
  require_once( pathinfo( __FILE__, PATHINFO_DIRNAME ) . '/class-mla-image-processor.php' );
15
 
9
  /*
10
  * Process mla_viewer image stream requests
11
  */
12
+ //@ini_set('error_log','C:\Program Files\Apache Software Foundation\Apache24\logs\php-errors.log');
13
 
14
  require_once( pathinfo( __FILE__, PATHINFO_DIRNAME ) . '/class-mla-image-processor.php' );
15
 
index.php CHANGED
@@ -6,7 +6,7 @@
6
  * will the rest of the plugin be loaded and run.
7
  *
8
  * @package Media Library Assistant
9
- * @version 2.93
10
  */
11
 
12
  /*
@@ -16,7 +16,7 @@ Description: Enhances the Media Library; powerful [mla_gallery] [mla_tag_cloud]
16
  Author: David Lingren
17
  Text Domain: media-library-assistant
18
  Domain Path: /languages
19
- Version: 2.93
20
  Author URI: http://davidlingren.com/
21
 
22
  Copyright 2011-2020 David Lingren
6
  * will the rest of the plugin be loaded and run.
7
  *
8
  * @package Media Library Assistant
9
+ * @version 2.94
10
  */
11
 
12
  /*
16
  Author: David Lingren
17
  Text Domain: media-library-assistant
18
  Domain Path: /languages
19
+ Version: 2.94
20
  Author URI: http://davidlingren.com/
21
 
22
  Copyright 2011-2020 David Lingren
readme.txt CHANGED
@@ -5,7 +5,7 @@ Tags: media, media library, gallery, images, categories, tags, attachments, IPTC
5
  Requires at least: 3.5.0
6
  Tested up to: 5.6
7
  Requires PHP: 5.3
8
- Stable tag: 2.93
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
@@ -181,6 +181,20 @@ All of the MLA source code has been annotated with "DocBlocks", a special type o
181
 
182
  == Changelog ==
183
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  = 2.93 =
185
  * New: The "MLA Simple Mapping Hooks Example" plugin has been updated so it will run when attachments are uploaded or updated by the WP/LR Sync plugin.
186
  * New: For the "Smart Media Categories" example plugin, support has been added for the "Postie" plugin chron job that creates posts and attachments from an email.
@@ -321,8 +335,8 @@ All of the MLA source code has been annotated with "DocBlocks", a special type o
321
 
322
  == Upgrade Notice ==
323
 
324
- = 2.93 =
325
- IMPORTANT: Correct defects in handling array values, e.g., tax_input, in request: parameters and pagination controls. Example plugin enhancements. Two enhancements in all, three fixes.
326
 
327
  == Other Notes ==
328
 
5
  Requires at least: 3.5.0
6
  Tested up to: 5.6
7
  Requires PHP: 5.3
8
+ Stable tag: 2.94
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
181
 
182
  == Changelog ==
183
 
184
+ = 2.94 =
185
+ * New: <strong>The "MLA Custom Field Search Example" plugin has been substantially upgraded.</strong> The new version has many more parameters and a new plugin settings page. A Documentation tab on the settings page contains all the information you need to understand and use the new version.
186
+ * New: <strong>A new example plugin, "MLA Postie Post After Example"</strong>, adds support for running MLA mapping rules after the "Postie" plugin chron job creates posts and attachments from an email.
187
+ * New: The Library Views/Post MIME Type "Table View" custom field queries have been enhanced to allow searching multiple field names or "all fields" for one or more values.
188
+ * Fix: The "Smart Media Categories" example plugin has been enhanced to handle additional WordPress alternatives for term assignments and a new option is provided to exclude the Default Post Category from sync processing.
189
+ * Fix: Wildcard values for Library Views/Post MIME Type "Table View" custom field queries have been restored.
190
+ * Fix: PDF thumbnail streaming for <code>mla_viewer</code> processing has been restored.
191
+ * Fix: For the `[mla_gallery]` shortcode, error reporting for the tax_query, date_query and meta_query parameters has been improved.
192
+ * Fix: For the `[mla_gallery]` shortcode, proper handling of the <code>size=</code> "icon", "icon_only" and "icon_feature" options has been restored.
193
+ * Fix: For the `[mla_gallery]` shortcode, performance is improved by avoiding a redundant LEFT JOIN database query clause (added by WP_Query).
194
+ * Fix: For the `[mla_gallery]` shortcode, performance is improved by avoiding LEFT JOIN and WHERE database query clauses added by Real Media Library.
195
+ * Fix: Unnecessary "term meta cache" queries have been removed from the Media/Assistant submenu table generation.
196
+ * Fix: Handling of disimissible admin messages has been restored.
197
+
198
  = 2.93 =
199
  * New: The "MLA Simple Mapping Hooks Example" plugin has been updated so it will run when attachments are uploaded or updated by the WP/LR Sync plugin.
200
  * New: For the "Smart Media Categories" example plugin, support has been added for the "Postie" plugin chron job that creates posts and attachments from an email.
335
 
336
  == Upgrade Notice ==
337
 
338
+ = 2.94 =
339
+ For [mla_gallery], icon handling, mla_viewer, and performance fixes. New and enhanced example plugins. Three enhancements in all, nine fixes.
340
 
341
  == Other Notes ==
342
 
tpls/documentation-settings-tab.tpl CHANGED
@@ -628,7 +628,7 @@ The Size parameter specifies the image size to use for the thumbnail display; "t
628
  </tr>
629
  <tr>
630
  <td class="mla-doc-table-label">icon</td>
631
- <td>Display an appropriate 60x60 (or 64x64) pixel thumbnail for image items and an appropriate icon for non-image items such as PDF or text files.</td>
632
  </tr>
633
  <tr>
634
  <td class="mla-doc-table-label">icon_feature</td>
@@ -6116,12 +6116,23 @@ Within WordPress, the Post MIME Types list is returned from <code>/wp-includes/p
6116
  The Table View list adds several enhancements to the Post MIME Type list. In the Specification field you can select several MIME types with a comma-separated list, e.g., "audio,video". Wildcard specifications are also supported. For example, "*/mpeg" to select audio and video mpeg formats or "application/*ms*" to select all Microsoft application formats (Word, Excel, etc.). In the Menu Order field you can enter numeric values to re-arrange the order in which the list entries are displayed in, for example, the Media/Assistant screen.
6117
  </p>
6118
  <p>
6119
- The Table View list also supports custom field queries. You can choose from three forms of the custom field specification:
 
 
 
 
 
 
 
 
 
 
 
 
6120
  </p>
6121
  <ul class="mla_settings">
6122
- <li>To return all items that have a non-NULL value in the field, simply enter the prefix "custom:" followed by the custom field name. For example, <code>custom:My Featured Items</code>. You can also enter the custom field name and then "=*", e.g., <code>custom:My Featured Items=*</code>.</li>
6123
- <li>To return all items that have a NULL value in the field, enter the prefix "custom:" followed by the custom field name and then ",null". For example, <code>custom:My Featured Items,null</code>. You can also enter the custom field name and then "=", e.g., <code>custom:My Featured Items=</code>.</li>
6124
- <li>To return all items that match one or more values, enter the prefix "custom:" followed by the custom field name and then "=" followed by a list of values. For example, <code>custom:Color=red</code> or <code>custom:Color=red,green,blue</code>. Wildcard specifications are also supported; for example, "*post" to match anything ending in "post" or "th*da*" to match values like "the date" and "this day".</li>
6125
  </ul>
6126
  <p>
6127
  If you have enabled the <em><strong>Media Manager Enhanced MIME Type filter</strong></em>, the Table View list will also be available in the Media Manager/Add Media "media items" drop down list.
@@ -6715,7 +6726,7 @@ The Format element has a "commas" value that can improve the results of sorting
6715
  If you code the "template:" prefix at the beginning of the EXIF/Template value you have all the power of Content Templates at your disposal. Do <strong>not</strong> add the "[+" and "+]" delimiters; the prefix is all you need.
6716
  </p>
6717
  <p>
6718
- A template can be used to access any XMP metadata your items contain. For example:<br />
6719
  &nbsp;<br />
6720
  <code>template:([+xmp:Title+])</code><br />
6721
  <code>template:([+xmp:Regions.RegionList.*.*.Name,array+])</code><br />
@@ -6770,6 +6781,23 @@ The first example above sets the date to a fixed value. The second example uses
6770
  </p>
6771
  <h4>IPTC/EXIF Mapping for PDF Documents</h4>
6772
  <p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6773
  You can use Content Templates in the EXIF/Template Value text box to extract metadata from your PDF documents and add it to the Standard Fields and Taxonomy Terms of your Media Library items. The templates can be coded to select the appropriate source whether the item is a PDF document or an image. Here are three rules for metadata contained in PDF documents:
6774
  </p>
6775
  <table>
628
  </tr>
629
  <tr>
630
  <td class="mla-doc-table-label">icon</td>
631
+ <td>Display an appropriate 60x60 (or 64x64) pixel thumbnail for image items and an appropriate icon for non-image items such as PDF or text files. If, however, a non-image item has a "Featured Image" it will replace the icon.</td>
632
  </tr>
633
  <tr>
634
  <td class="mla-doc-table-label">icon_feature</td>
6116
  The Table View list adds several enhancements to the Post MIME Type list. In the Specification field you can select several MIME types with a comma-separated list, e.g., "audio,video". Wildcard specifications are also supported. For example, "*/mpeg" to select audio and video mpeg formats or "application/*ms*" to select all Microsoft application formats (Word, Excel, etc.). In the Menu Order field you can enter numeric values to re-arrange the order in which the list entries are displayed in, for example, the Media/Assistant screen.
6117
  </p>
6118
  <p>
6119
+ The Table View list also supports custom field queries. A custom field query has four parts:
6120
+ </p>
6121
+ <ol>
6122
+ <li>A prefix, "custom:"</li>
6123
+ <li>A comma-separated list of one or more custom field names</li>
6124
+ <li>An equals sign ("="), to divide the field names from the values</li>
6125
+ <li>A comma-separated list of one or more values</li>
6126
+ </ol>
6127
+ <p>
6128
+ To return all items that match one or more values, enter the prefix "custom:" followed by the custom field name(s) and then "=" followed by a list of values. For example, <code>custom:Color=red</code> or <code>custom:Color=red,green,blue</code>. To search multiple fields, enter something like <code>custom:Artist,Patron=smith,jones</code>. To search <strong>all</strong> custom fields, enter an asterisk ("*"), e.g., <code>custom:*=smith,jones</code>. Wildcard specifications are also supported; for example, "*post" to match anything ending in "post" or "th*da*" to match values like "the date" and "this day". As explained below, a value of "*" will match any non-NULL value for a custom field.
6129
+ </p>
6130
+ <p>
6131
+ There are two special forms of the custom field specification used to test for the presence (non-NULL) or absence (NULL) of <strong>any</strong> values:
6132
  </p>
6133
  <ul class="mla_settings">
6134
+ <li>To return all items that have a non-NULL value in the field, enter the custom field name and then "=*", e.g., <code>custom:My Featured Items=*</code>. You can also enter the prefix "custom:" followed by just the custom field name(s). For example, <code>custom:My Featured Items</code>.</li>
6135
+ <li>To return all items that have a NULL value in the field, enter the prefix "custom:" followed by the custom field name(s) and then "=", e.g., <code>custom:My Featured Items,My Inserted Items=</code>. You can also enter a single custom field name and then ",null". For example, <code>custom:My Featured Items,null</code>.</li>
 
6136
  </ul>
6137
  <p>
6138
  If you have enabled the <em><strong>Media Manager Enhanced MIME Type filter</strong></em>, the Table View list will also be available in the Media Manager/Add Media "media items" drop down list.
6726
  If you code the "template:" prefix at the beginning of the EXIF/Template value you have all the power of Content Templates at your disposal. Do <strong>not</strong> add the "[+" and "+]" delimiters; the prefix is all you need.
6727
  </p>
6728
  <p>
6729
+ A template can be used to access any IPTC, EXIF or XMP metadata your items contain, as well as any of the <a href="#field_level_data_sources">field-level data sources</a>. For example:<br />
6730
  &nbsp;<br />
6731
  <code>template:([+xmp:Title+])</code><br />
6732
  <code>template:([+xmp:Regions.RegionList.*.*.Name,array+])</code><br />
6781
  </p>
6782
  <h4>IPTC/EXIF Mapping for PDF Documents</h4>
6783
  <p>
6784
+ PDF documents contain a Document Information Dictionary (D.I.D.) and many also contain XMP metadata. For the <code>pdf:</code> prefix, you can code any of the nine D.I.D. entries:
6785
+ </p>
6786
+ <ul class="mla_settings">
6787
+ <li><strong>Title</strong> - The document's title</li>
6788
+ <li><strong>Author</strong> - The name of the person who created the document</li>
6789
+ <li><strong>Subject</strong> - The subject of the document</li>
6790
+ <li><strong>Keywords</strong> - Keywords associated with the document</li>
6791
+ <li><strong>Creator</strong> - the name of the conforming product that created the original document</li>
6792
+ <li><strong>Producer</strong> - the name of the conforming product that converted it to PDF</li>
6793
+ <li><strong>CreationDate</strong> - The date and time the document was created</li>
6794
+ <li><strong>ModDate</strong> - The date and time the document was most recently modified</li>
6795
+ <li><strong>Trapped</strong> - indicates whether the document has been modified to include trapping information</li>
6796
+ </ul>
6797
+ <p>
6798
+ MLA contains logic that attempts to fill in the entries from the dictionary or from any XMP metadata the document contains. This gives you a simple way to access the information regardless of where in the metadata it appears.
6799
+ </p>
6800
+ <p>
6801
  You can use Content Templates in the EXIF/Template Value text box to extract metadata from your PDF documents and add it to the Standard Fields and Taxonomy Terms of your Media Library items. The templates can be coded to select the appropriate source whether the item is a PDF document or an image. Here are three rules for metadata contained in PDF documents:
6802
  </p>
6803
  <table>