Skip to content

Commit eed8d4a

Browse files
committed
Implement file upload filtering: Added support for filtering posts based on file uploads in the archive template, updated documentation to reflect new filter options, and enhanced JavaScript handling for file upload filters. This improves the user experience by allowing users to search for posts with or without uploaded files.
1 parent c38410c commit eed8d4a

4 files changed

Lines changed: 129 additions & 2 deletions

File tree

archive-template.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ class="loading-spinner"></span>
948948
<div class="grid-x">
949949
<div class="cell small-4 filter-modal-left">
950950
<?php $fields = [];
951-
$allowed_types = [ 'user_select', 'multi_select', 'key_select', 'boolean', 'date', 'datetime', 'location', 'location_meta', 'connection', 'tags', 'text', 'communication_channel' ];
951+
$allowed_types = [ 'user_select', 'multi_select', 'key_select', 'boolean', 'date', 'datetime', 'location', 'location_meta', 'connection', 'tags', 'text', 'communication_channel', 'file_upload' ];
952952
//order fields alphabetically by Name
953953
uasort( $field_options, function ( $a, $b ){
954954
return strnatcmp( $a['name'] ?? 'z', $b['name'] ?? 'z' );
@@ -1106,6 +1106,29 @@ class="text-comms-filter-input"
11061106
<?php echo esc_html( sprintf( _x( 'All %1$s with no value in %2$s', 'All Contacts with no value in field', 'disciple_tools' ), $post_settings['label_plural'], $field_options[$field]['name'] ) ) ?>
11071107
</label>
11081108
</p>
1109+
<?php elseif ( isset( $field_options[$field] ) && $field_options[$field]['type'] === 'file_upload' ) : ?>
1110+
<p>
1111+
<label>
1112+
<input
1113+
name="filter_by_file_upload_option_<?php echo esc_attr( $field ) ?>"
1114+
class="filter-by-file-upload-option"
1115+
type="radio"
1116+
value="all-with-files"
1117+
data-field="<?php echo esc_html( $field ) ?>"
1118+
/>
1119+
<?php echo esc_html( sprintf( _x( 'All %1$s with files in %2$s', 'All Contacts with files in Documents', 'disciple_tools' ), $post_settings['label_plural'], $field_options[$field]['name'] ) ) ?>
1120+
</label>
1121+
<label>
1122+
<input
1123+
name="filter_by_file_upload_option_<?php echo esc_attr( $field ) ?>"
1124+
class="filter-by-file-upload-option"
1125+
type="radio"
1126+
value="all-without-files"
1127+
data-field="<?php echo esc_html( $field ) ?>"
1128+
/>
1129+
<?php echo esc_html( sprintf( _x( 'All %1$s without files in %2$s', 'All Contacts without files in Documents', 'disciple_tools' ), $post_settings['label_plural'], $field_options[$field]['name'] ) ) ?>
1130+
</label>
1131+
</p>
11091132
<?php endif; ?>
11101133
</div>
11111134
</div>

docs/dt-posts-list-query.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ Different field types have different filter formats:
189189
'assigned_to' => []
190190
```
191191

192+
### File Upload Fields
193+
194+
```php
195+
// Find posts with at least one uploaded file
196+
'documents' => ['*']
197+
198+
// Find posts with no uploaded files
199+
'documents' => []
200+
```
201+
192202
## Combining Multiple Filters
193203

194204
Multiple filters can be combined to create complex queries:

dt-assets/js/modular-list.js

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1700,6 +1700,15 @@
17001700
break;
17011701
}
17021702
}
1703+
} else if (type === 'file_upload') {
1704+
const selectedOption = $(
1705+
`#${field}-options .filter-by-file-upload-option:checked`,
1706+
).val();
1707+
if (selectedOption === 'all-with-files') {
1708+
search_query.push({ [field]: ['*'] });
1709+
} else if (selectedOption === 'all-without-files') {
1710+
search_query.push({ [field]: [] });
1711+
}
17031712
} else {
17041713
let options = [];
17051714
$(`#${field}-options input:checked`).each(function () {
@@ -2077,6 +2086,13 @@
20772086
});
20782087
});
20792088

2089+
$('.filter-by-file-upload-option').on('click', function (e) {
2090+
handle_filter_by_file_upload({
2091+
id: $(this).val(),
2092+
field: $(this).data('field'),
2093+
});
2094+
});
2095+
20802096
function handle_filter_by_text_comms(options) {
20812097
const { id, field } = options || { id: null, field: null };
20822098
if (id && field) {
@@ -2154,6 +2170,32 @@
21542170
}
21552171
}
21562172

2173+
function handle_filter_by_file_upload(options) {
2174+
const { id, field } = options || { id: null, field: null };
2175+
if (!id || !field) {
2176+
return;
2177+
}
2178+
2179+
const without = id === 'all-without-files';
2180+
const { newLabel, filterName } = create_label_all(
2181+
field,
2182+
without,
2183+
id,
2184+
list_settings,
2185+
);
2186+
2187+
// Replace existing label for this file_upload field.
2188+
new_filter_labels = new_filter_labels.filter(
2189+
(label) => label.field !== field,
2190+
);
2191+
$(selected_filters).find(`.current-filter.${field}`).remove();
2192+
2193+
selected_filters.append(
2194+
`<span class="current-filter ${esc(field)}" data-id="${id}">${filterName}</span>`,
2195+
);
2196+
new_filter_labels.push(newLabel);
2197+
}
2198+
21572199
let load_multi_select_typeaheads =
21582200
async function load_multi_select_typeaheads() {
21592201
for (let input of $(
@@ -2628,7 +2670,11 @@
26282670
list_settings,
26292671
`post_type_settings.fields.${label.field}.type`,
26302672
);
2631-
if (type === 'key_select' || type === 'boolean') {
2673+
if (
2674+
type === 'key_select' ||
2675+
type === 'boolean' ||
2676+
type === 'file_upload'
2677+
) {
26322678
$(
26332679
`#filter-modal #${label.field}-options input[value="${label.id}"]`,
26342680
).prop('checked', true);

dt-posts/posts.php

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,54 @@ public static function fields_to_sql( $post_type, $query_array, $operator = 'AND
913913
if ( empty( $query_value ) ){
914914
$where_sql .= " $table_key.meta_value IS NULL ";
915915
}
916+
} else if ( in_array( $field_type, [ 'file_upload' ] ) ) {
917+
/**
918+
* file_upload
919+
* Supports:
920+
* ['*'] => has at least one file
921+
* [] => no files
922+
*/
923+
if ( !is_array( $query_value ) ) {
924+
return new WP_Error( __FUNCTION__, "$query_key must be an array", [ 'status' => 400 ] );
925+
}
926+
927+
$meta_table = $wpdb->postmeta;
928+
$user_condition = '';
929+
if ( isset( $field_settings[$query_key]['private'] ) && $field_settings[$query_key]['private'] ) {
930+
$meta_table = $wpdb->dt_post_user_meta;
931+
$user_condition = ' AND pm.user_id = ' . get_current_user_id();
932+
}
933+
934+
// Detect "has files" query (supports '*' and prefixed legacy option id)
935+
$has_files = false;
936+
foreach ( $query_value as $value ) {
937+
if ( strpos( (string) $value, '-' ) !== 0 ) {
938+
$normalized = ltrim( (string) $value, '^' );
939+
if ( $normalized === '*' || $normalized === 'all-with-files' ) {
940+
$has_files = true;
941+
}
942+
}
943+
}
944+
945+
$has_value_sql = "
946+
EXISTS (
947+
SELECT 1
948+
FROM $meta_table pm
949+
WHERE pm.post_id = p.ID
950+
AND pm.meta_key = '" . esc_sql( $query_key ) . "'
951+
$user_condition
952+
AND pm.meta_value <> ''
953+
AND pm.meta_value <> 'a:0:{}'
954+
)
955+
";
956+
957+
if ( empty( $query_value ) ) {
958+
$where_sql .= " NOT $has_value_sql ";
959+
} elseif ( $has_files ) {
960+
$where_sql .= " $has_value_sql ";
961+
} else {
962+
return new WP_Error( __FUNCTION__, "Invalid file_upload filter values for $query_key", [ 'status' => 400 ] );
963+
}
916964
} else if ( in_array( $field_type, [ 'connection' ] ) ){
917965
/**
918966
* connection

0 commit comments

Comments
 (0)