Skip to content

Commit 9ab43fe

Browse files
authored
Merge pull request #2110 from themeum/fix/import-export
fix: Enhance export/import to support JSON file handling
2 parents f1a1b49 + ef17cc4 commit 9ab43fe

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)