Skip to content

Commit ef17cc4

Browse files
committed
fix: Enhance export/import to support JSON file handling
Updated export flow to generate and download JSON files for non-Pro users, including dynamic file naming and size formatting. Adjusted UI to reflect file type and improved download button logic. Modified backend import to accept uploaded JSON files instead of POST data.
1 parent f1a1b49 commit ef17cc4

File tree

4 files changed

+51
-18
lines changed

4 files changed

+51
-18
lines changed

assets/react/v3/entries/import-export/components/Export.tsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { typography } from '@TutorShared/config/typography';
1818
import { type ExportableContent } from '@TutorShared/services/import-export';
1919
import { styleUtils } from '@TutorShared/utils/style-utils';
2020
import { decodeParams } from '@TutorShared/utils/url';
21-
import { convertToErrorMessage } from '@TutorShared/utils/util';
21+
import { convertToErrorMessage, formatBytes } from '@TutorShared/utils/util';
2222

2323
const CONTENT_BANK_PAGE = 'tutor-content-bank';
2424
const isTutorPro = !!tutorConfig.tutor_pro_url;
@@ -110,6 +110,7 @@ const Export = () => {
110110

111111
useEffect(() => {
112112
const progress = Number(exportContentResponse?.job_progress);
113+
113114
if (isError) {
114115
updateModal<typeof ExportModal>('export-modal', {
115116
currentStep: 'error',
@@ -137,24 +138,43 @@ const Export = () => {
137138
updateModal<typeof ExportModal>('export-modal', {
138139
currentStep: 'success',
139140
progress: 100,
140-
fileName: exportContentResponse?.exported_data,
141-
fileSize: exportContentResponse?.export_file?.file_size || 0,
142-
message: exportContentResponse?.message || '',
141+
fileName: isTutorPro ? exportContentResponse?.exported_data : '',
142+
fileSize: isTutorPro
143+
? exportContentResponse?.export_file?.file_size
144+
: formatBytes(JSON.stringify(exportContentResponse?.exported_data).length),
145+
message: isTutorPro ? exportContentResponse?.message || '' : __('Settings', 'tutor'),
143146
completedContents: exportContentResponse?.completed_contents,
144147
onClose: () => {
145148
closeModal();
146149
const newUrl = new URL(url);
147150
newUrl.searchParams.set('download', 'false'); // this will delete the generated download link and file
148151
fetch(newUrl);
149152
},
150-
onDownload: () => {
153+
onDownload: (fileName: string) => {
151154
closeModal();
152-
const url = exportContentResponse?.export_file?.url;
155+
156+
if (isTutorPro) {
157+
const url = exportContentResponse?.export_file?.url;
158+
const a = document.createElement('a');
159+
a.href = url;
160+
document.body.appendChild(a);
161+
a.click();
162+
document.body.removeChild(a);
163+
164+
return;
165+
}
166+
167+
const jsonFile = new Blob([JSON.stringify(exportContentResponse?.exported_data)], {
168+
type: 'application/json',
169+
});
170+
const url = URL.createObjectURL(jsonFile);
153171
const a = document.createElement('a');
154172
a.href = url;
173+
a.download = fileName;
155174
document.body.appendChild(a);
156175
a.click();
157176
document.body.removeChild(a);
177+
URL.revokeObjectURL(url);
158178
},
159179
});
160180
}

assets/react/v3/entries/import-export/components/modals/ExportModal.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ interface ExportModalProps extends ModalProps {
3535
onClose: () => void;
3636
onExport: ({ data, exportableContent }: { data: ExportFormData; exportableContent: ExportableContent[] }) => void;
3737
currentStep: ImportExportModalState;
38-
onDownload?: () => void;
38+
onDownload?: (fileName: string) => void;
3939
progress: number;
4040
fileName?: string;
41-
fileSize?: number;
41+
fileSize?: number | string;
4242
message?: string;
4343
failedMessage?: string;
4444
completedContents?: ImportExportContentResponseBase['completed_contents'];
@@ -61,8 +61,8 @@ const ExportModal = ({
6161
progress,
6262
fileName,
6363
fileSize,
64-
message,
65-
failedMessage,
64+
message = '',
65+
failedMessage = '',
6666
completedContents,
6767
collection,
6868
}: ExportModalProps) => {

assets/react/v3/entries/import-export/components/modals/import-export-states/ImportExportCompletedState.tsx

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { css } from '@emotion/react';
2-
import { __ } from '@wordpress/i18n';
2+
import { __, sprintf } from '@wordpress/i18n';
3+
import { format } from 'date-fns';
34
import { useState } from 'react';
45

56
import Button from '@TutorShared/atoms/Button';
@@ -23,16 +24,20 @@ import exportErrorImage from '@SharedImages/import-export/export-error.webp';
2324
import exportSuccessImage from '@SharedImages/import-export/export-success.webp';
2425
import importErrorImage from '@SharedImages/import-export/import-error.webp';
2526
import importSuccessImage from '@SharedImages/import-export/import-success.webp';
27+
import { tutorConfig } from '@TutorShared/config/config';
28+
29+
const isTutorPro = !!tutorConfig.tutor_pro_url;
30+
const fileName = `tutor-lms-data-${format(new Date(), 'yyyy-MM-dd-HH-mm-ss')}.json`;
2631

2732
interface ImportExportCompletedStateProps {
2833
state: ImportExportModalState;
2934
isImportingToContentBank?: boolean;
30-
fileSize?: number;
35+
fileSize?: number | string;
3136
message?: string;
3237
failedMessage?: string;
3338
completedContents?: ImportExportContentResponseBase['completed_contents'];
3439
importErrors?: ImportContentResponse['errors'];
35-
onDownload?: () => void;
40+
onDownload?: (fileName: string) => void;
3641
onClose: () => void;
3742
exportFileName?: string;
3843
type: 'import' | 'export';
@@ -121,7 +126,7 @@ const ImportExportCompletedState = ({
121126
? // prettier-ignore
122127
__('The export process has finished. However, certain items could not be exported. Check the summary below:', 'tutor')
123128
: // prettier-ignore
124-
__('Download the JSON file and use it to import your data into another Tutor LMS website.', 'tutor'),
129+
sprintf(__('Download the %s file and use it to import your data into another Tutor LMS website.', 'tutor'), isTutorPro ? 'ZIP' : 'JSON'),
125130
error: message || __('Something went wrong during export. Please try again!', 'tutor'),
126131
},
127132
reportList: {
@@ -248,8 +253,8 @@ const ImportExportCompletedState = ({
248253
</div>
249254
<div css={styles.fileRight}>
250255
<div css={styles.fileDetails}>
251-
<div css={styles.fileName} title={exportFileName}>
252-
{exportFileName}
256+
<div css={styles.fileName} title={exportFileName || fileName}>
257+
{exportFileName || fileName}
253258
</div>
254259
<div css={styles.fileSize}>{fileSize || formatBytes(0)}</div>
255260
</div>
@@ -259,7 +264,7 @@ const ImportExportCompletedState = ({
259264
variant="primary"
260265
size="small"
261266
icon={<SVGIcon name="download" width={24} height={24} />}
262-
onClick={() => onDownload?.()}
267+
onClick={() => onDownload?.(fileName)}
263268
>
264269
{__('Download', 'tutor')}
265270
</Button>

classes/Options_V2.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,15 @@ public function tutor_import_settings() {
431431
wp_send_json_error( tutor_utils()->error_message() );
432432
}
433433

434-
$request = json_decode( stripslashes( $_POST['data'] ), true );
434+
$data = $_FILES['data'];
435+
436+
if ( ! isset( $data['tmp_name'] ) ) {
437+
$this->response_bad_request( __( 'Invalid file', 'tutor' ) );
438+
}
439+
440+
$request = json_decode( file_get_contents( $data['tmp_name'] ), true );
441+
442+
unlink( $data['tmp_name'] );
435443

436444
$settings_found = false;
437445

0 commit comments

Comments
 (0)