Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Export Link widget #1997

Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
df96317
Add CSV download link widget
doekenorg Feb 26, 2024
e4eea1f
no capital Link
doekenorg Feb 26, 2024
535a0ad
Move CSV download to REST endpoint with nonce check.
doekenorg Feb 28, 2024
52f43c1
Fix unit test warnings
doekenorg Mar 4, 2024
8cd307f
Add classes, allow field name headers, strip notice.
doekenorg Mar 4, 2024
93228e7
update image
doekenorg Mar 4, 2024
0871618
Fix styling with new name. Hide export until allowed.
doekenorg Mar 4, 2024
7519a97
Transfer search query params to export link.
doekenorg Mar 4, 2024
6757731
Add notification all entries will be downloaded by widget
doekenorg Mar 7, 2024
8419d72
Add translations
doekenorg Mar 7, 2024
4e67815
Use Utils::get() instead of rgar()
zackkatz Mar 13, 2024
31add4b
Fix punctuation
zackkatz Mar 13, 2024
be28c72
Tweak the language a bit
zackkatz Mar 13, 2024
c38d961
Use the same language as other field settings (for now)
zackkatz Mar 13, 2024
f7f07f7
Make some changes for accessibility
zackkatz Mar 13, 2024
8923f32
Fix fatal error when Search Bar has multiselect
zackkatz Mar 13, 2024
ad4cd7e
Update language for the widget
zackkatz Mar 13, 2024
0378ec2
Add container div to improve styling/scripting
zackkatz Mar 13, 2024
4dbbdc6
Fix mime type
zackkatz Mar 13, 2024
2803710
Provide the base URL for JS updating
zackkatz Mar 13, 2024
d638a48
Fix incorrect variable name
zackkatz Mar 13, 2024
10ca055
Merge branch 'develop' into feature/1891-implement-a-csv-widget-to-be…
zackkatz Mar 13, 2024
a4f04bd
Update readme.txt [ci skip]
zackkatz Mar 16, 2024
b93d9e3
Merge branch 'develop' into feature/1891-implement-a-csv-widget-to-be…
mrcasual Mar 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,3 @@ workflows:
- run_php_72_unit_tests
- run_php_80_unit_tests
# - run_acceptance_tests

2 changes: 1 addition & 1 deletion assets/css/admin-global.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions assets/css/scss/admin-global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,20 @@
margin: -1px;
}
}


.gv-fields.csv-disabled {
cursor: not-allowed;

.csv-disabled-notice {
display: block;
}
.gv-field-label,
.gv-field-controls button {
color: #d7dade !important;
}

.ui-tooltip-content .gv-items-picker-container.gv-widget-picker-container & {
display: none;
}
}
17 changes: 17 additions & 0 deletions assets/js/admin-views.js
Original file line number Diff line number Diff line change
Expand Up @@ -2802,4 +2802,21 @@
$( document.body ).trigger( 'gravityview/loaded' );
} );

/**
* Handles CSV widget classes.
* @since $ver$
*/
$( function () {
const $csv_enable = $( '#gravityview_se_csv_enable' );
const update_csv_widget_classes = function () {
$( '[data-fieldid="export_link"]' )
.toggleClass( 'csv-disabled', !$csv_enable.is( ':checked' ) )
.attr( 'aria-disabled', $csv_enable.is( ':checked' ) ? 'false' : 'true' )
;
};

$csv_enable.on( 'change', update_csv_widget_classes );
update_csv_widget_classes();
} );

}(jQuery));
2 changes: 1 addition & 1 deletion assets/js/admin-views.min.js

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion future/includes/class-gv-field.php
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,11 @@ public function get_label( View $view = null, Source $source = null, Entry $entr

/** A custom label is available. */
if ( ! empty( $this->custom_label ) ) {
return \GravityView_API::replace_variables( $this->custom_label, $source ? $source->form ? : null : null, $entry ? $entry->as_entry() : null );
return \GravityView_API::replace_variables(
$this->custom_label,
$source ? $source->form ?? null : null,
$entry ? $entry->as_entry() : null
);
}

return '';
Expand Down
90 changes: 79 additions & 11 deletions future/includes/rest/class-gv-rest-views-route.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
*/
namespace GV\REST;

use GravityView_Widget_Export_Link;
use WP_REST_Request;

/** If this file is called directly, abort. */
if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
die();
Expand All @@ -36,6 +39,23 @@ class Views_Route extends Route {
protected $sub_type = 'entries';


/**
* Whether the headers are rendered.
*
* @since $ver$
* @var bool
*/
private $headers_done;

/**
* The headers for the output.
*
* @since $ver$
* @var array
*/
private $headers = [];


/**
* Get a collection of views
*
Expand Down Expand Up @@ -150,8 +170,10 @@ function ( $field ) use ( $allowed_field_ids ) {
$used_ids = array();

foreach ( $allowed as $field ) {
$source = is_numeric( $field->ID ) ? $view->form : new \GV\Internal_Source();
// remove all links from output.
$field->update_configuration( [ 'show_as_link' => '0' ] );

$source = is_numeric( $field->ID ) ? $view->form : new \GV\Internal_Source();
$field_id = $field->ID;
$index = null;

Expand All @@ -171,14 +193,26 @@ function ( $field ) use ( $allowed_field_ids ) {
/**
* Filter the key name in the results for JSON output.
*
* @param string $field_id The ID. Should be unique or keys will be gobbled up.
* @param \GV\View $view The view.
* @param \GV\Entry $entry The entry.
* @param \WP_REST_Request $request Request object.
* @param string $context The context (directory, single)
* @param string $field_id The ID. Should be unique or keys will be gobbled up.
* @param \GV\View $view The view.
* @param \GV\Entry $entry The entry.
* @param \WP_REST_Request $request Request object.
* @param string $context The context (directory, single)
*/
$field_id = apply_filters( 'gravityview/api/field/key', $field_id, $view, $entry, $request, $context );

if ( ! $this->headers_done ) {
$label = $field->get_label( $view, $source, $entry );
if ( ! $label ) {
$label = $field_id;
}

$this->headers[] = [
'field_id' => $field_id,
'label' => $label,
];
}

if ( ! $class && in_array( $field->ID, array( 'custom' ) ) ) {
/**
* Custom fields (and perhaps some others) will require rendering as they don't
Expand Down Expand Up @@ -294,21 +328,25 @@ function ( $context ) use ( &$count, &$total ) {

$csv_or_tsv = fopen( 'php://output', 'w' );

$filename = apply_filters( 'gravityview/output/' . $format . '/filename', get_the_title( $view->post ), $view );

/** Da' BOM :) */
if ( apply_filters( 'gform_include_bom_export_entries', true, $view->form ? $view->form->form : null ) ) {
fputs( $csv_or_tsv, "\xef\xbb\xbf" );
}

$headers_done = false;
$this->headers_done = false;
$this->headers = [];

// If not "tsv" then use comma
// If not "tsv" then use comma.
$delimiter = ( 'tsv' === $format ) ? "\t" : ',';

foreach ( $entries->all() as $entry ) {
$entry = $this->prepare_entry_for_response( $view, $entry, $request, 'directory', '\GV\Field_CSV_Template' );
$label = $request->get_param( 'use_labels' ) ? 'label' : 'field_id';

if ( ! $headers_done ) {
$headers_done = fputcsv( $csv_or_tsv, array_map( array( '\GV\Utils', 'strip_excel_formulas' ), array_keys( $entry ) ), $delimiter );
if ( ! $this->headers_done ) {
$this->headers_done = false !== fputcsv( $csv_or_tsv, array_map( array( '\GV\Utils', 'strip_excel_formulas' ), array_column( $this->headers, $label ) ), $delimiter );
}

fputcsv( $csv_or_tsv, array_map( array( '\GV\Utils', 'strip_excel_formulas' ), $entry ), $delimiter );
Expand All @@ -318,6 +356,8 @@ function ( $context ) use ( &$count, &$total ) {
$response->header( 'X-Item-Count', $entries->count() );
$response->header( 'X-Item-Total', $entries->total() );
$response->header( 'Content-Type', 'text/' . $format );
$response->header( 'Content-Transfer-Encoding', 'binary' );
$response->header( 'Content-Disposition', sprintf( 'attachment;filename="%s.%s"', sanitize_file_name( $filename ), $format ) );

fflush( $csv_or_tsv );

Expand Down Expand Up @@ -526,8 +566,36 @@ public function get_items_permissions_check( $request ) {
return true;
}

/**
* Permission check for the REST endpoint.
*
* @param WP_REST_Request $request The request object.
*
* @return bool|\WP_Error The permission result.
*/
public function get_sub_items_permissions_check( $request ) {
// Accessing all entries of a View needs the same permissions as accessing the View.
// Make sure to get the format from the URL.
$params = $request->get_url_params();
$format = strtolower( rgar( $params, 'format', '' ) );
$nonce = $request->get_param( '_nonce' );
$view_id = rgar( $params, 'id', 0 );

if ( ! $view = \GV\View::by_id( $view_id ) ) {
return new \WP_Error( 'rest_forbidden', __( 'You are not allowed to access this content.', 'gk-gravityview' ) );
}

if (
'1' === $view->settings->get( 'csv_enable' )
&& in_array( $format, [ 'csv', 'tsv' ], true )
&& wp_verify_nonce( $nonce, sprintf( '%s.%d', GravityView_Widget_Export_Link::WIDGET_ID, $view->ID ) )
) {
// All results.
$request->set_param( 'limit', 0 );

// The current request is a nonce verified CSV download request.
return true;
}

return $this->get_item_permissions_check( $request );
}
}
Loading