Skip to content
Merged

Release #2927

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
24a1af3
feat: support XML and CSV exports for form submissions
girishpanchal30 Jul 21, 2026
b359a3b
fix: sanitize CSV output
girishpanchal30 Jul 21, 2026
ad6e823
fix: phpcs
girishpanchal30 Jul 21, 2026
605d8b7
Sync branch [skip ci]
pirate-bot Jul 22, 2026
e40d581
Sync branch [skip ci]
pirate-bot Jul 22, 2026
b97d8f7
Merge pull request #2924 from Codeinwp/feat/2884
selul Jul 28, 2026
1d3ae24
feat: enhance CSV export
girishpanchal30 Jul 29, 2026
e632e8e
fix: enhance CSV export with max submission ID handling
girishpanchal30 Jul 29, 2026
0bf1276
fix: optimize query performance by disabling cache results
girishpanchal30 Jul 29, 2026
5ebc94f
fix: refactor CSV and XML export
girishpanchal30 Jul 29, 2026
d972bbb
Merge pull request #2928 from Codeinwp/feat/2884
selul Jul 29, 2026
cb1d14f
build(deps): bump codeinwp/themeisle-sdk from 3.3.54 to 3.3.58
dependabot[bot] Jul 30, 2026
5e42f8e
build(deps): bump codeinwp/themeisle-sdk in /plugins/blocks-css
dependabot[bot] Aug 1, 2026
d07ea2c
build(deps): bump codeinwp/themeisle-sdk in /plugins/blocks-animation
dependabot[bot] Aug 1, 2026
bb0a866
Merge pull request #2930 from Codeinwp/dependabot/composer/developmen…
selul Aug 4, 2026
48b9ee7
Merge pull request #2951 from Codeinwp/dependabot/composer/plugins/bl…
selul Aug 4, 2026
cd31288
Merge pull request #2950 from Codeinwp/dependabot/composer/plugins/bl…
selul Aug 4, 2026
476104b
fix: ensure dynamic block renderer class exists before instantiation
girishpanchal30 Aug 6, 2026
ca29165
fix: assertions in block registration tests
girishpanchal30 Aug 7, 2026
5e6da55
fix: accept expected include failure parameter
girishpanchal30 Aug 7, 2026
c674d7e
Merge pull request #2966 from Codeinwp/bugfix/2943
selul Aug 12, 2026
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
16 changes: 8 additions & 8 deletions composer.lock

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

2 changes: 1 addition & 1 deletion inc/class-registration.php
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ public function register_blocks() {
);
}

if ( isset( $dynamic_blocks[ $block ] ) ) {
if ( isset( $dynamic_blocks[ $block ] ) && class_exists( $dynamic_blocks[ $block ] ) ) {
$classname = $dynamic_blocks[ $block ];
$renderer = new $classname();

Expand Down
116 changes: 110 additions & 6 deletions inc/plugins/class-dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,54 @@ private function the_otter_banner() {
max-height: 35px;
}

.o-export-split {
position: relative;
display: inline-flex;
}

.o-export-split #export-submissions {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}

.o-export-split__toggle {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
border-left: none !important;
padding: 0 6px !important;
}

.o-export-split__menu {
position: absolute;
top: 100%;
right: 0;
z-index: 10;
margin: 4px 0 0;
padding: 4px 0;
list-style: none;
background: #fff;
border: 1px solid #c3c4c7;
border-radius: 4px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15);
min-width: 220px;
}

.o-export-split__item {
display: block;
width: 100%;
padding: 8px 12px;
background: none;
border: none;
text-align: left;
cursor: pointer;
font-size: 13px;
}

.o-export-split__item:hover,
.o-export-split__item:focus {
background: #f0f0f1;
}

.wp-core-ui .button.o-locked-action,
.wp-core-ui .button.o-locked-action:focus {
display: inline-flex;
Expand Down Expand Up @@ -683,9 +731,25 @@ private function the_otter_banner() {
<h1 class="otter-banner__title" style="line-height: normal;"><?php esc_html_e( 'Form Submissions', 'otter-blocks' ); ?></h1>

<?php if ( Pro::is_pro_active() ) : ?>
<button id="export-submissions" class="button">
<?php esc_html_e( 'Export', 'otter-blocks' ); ?>
</button>
<div class="o-export-split">
<button id="export-submissions" class="button" data-format="xml">
<?php esc_html_e( 'Export', 'otter-blocks' ); ?>
</button>
<button
id="export-submissions-toggle"
type="button"
class="button o-export-split__toggle"
aria-controls="export-submissions-menu"
aria-expanded="false"
aria-label="<?php esc_attr_e( 'Choose file format', 'otter-blocks' ); ?>"
>
<span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
</button>
<ul id="export-submissions-menu" class="o-export-split__menu" hidden>
<li><button type="button" class="o-export-split__item" data-format="xml"><?php esc_html_e( 'Export as WordPress XML (WXR)', 'otter-blocks' ); ?></button></li>
<li><button type="button" class="o-export-split__item" data-format="csv"><?php esc_html_e( 'Export as CSV', 'otter-blocks' ); ?></button></li>
</ul>
</div>
<?php else : ?>
<span class="otter-banner__actions">
<span class="o-pro-notice"><?php esc_html_e( 'Filter and export form submissions with Otter Pro.', 'otter-blocks' ); ?></span>
Expand All @@ -706,14 +770,28 @@ class="button o-locked-action"
</div>
<script>
window.document.addEventListener('DOMContentLoaded', () => {
document.querySelector('#export-submissions')?.addEventListener('click', () => {
const exportBtn = document.querySelector('#export-submissions');
const toggleBtn = document.querySelector('#export-submissions-toggle');
const menu = document.querySelector('#export-submissions-menu');

const closeMenu = () => {
if (!menu || !toggleBtn) {
return;
}

menu.setAttribute('hidden', '');
toggleBtn.setAttribute('aria-expanded', 'false');
};

const runExport = (format) => {
fetch('<?php echo esc_url( admin_url( 'admin-ajax.php' ) ); ?>', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
action: 'otter_form_submissions',
format: format,
_nonce: '<?php echo esc_attr( wp_create_nonce( 'otter_form_export_submissions' ) ); ?>'
})
})
Expand All @@ -724,17 +802,43 @@ class="button o-locked-action"
const month = String(currentDate.getMonth() + 1).padStart(2, '0');
const day = String(currentDate.getDate()).padStart(2, '0');

const blob = new Blob([response], {type: 'text/xml'});
const isCsv = 'csv' === format;
const blob = new Blob([response], {type: isCsv ? 'text/csv;charset=utf-8' : 'text/xml'});
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `otter_form_submissions__${year}-${month}-${day}.xml`;
a.download = `otter_form_submissions__${year}-${month}-${day}.${isCsv ? 'csv' : 'xml'}`;
document.body.appendChild(a);
a.click();
})
.catch(error => console.error('Error:', error));
};

exportBtn?.addEventListener('click', () => runExport(exportBtn.dataset.format || 'xml'));

toggleBtn?.addEventListener('click', (event) => {
event.stopPropagation();

if (menu.hasAttribute('hidden')) {
menu.removeAttribute('hidden');
toggleBtn.setAttribute('aria-expanded', 'true');
} else {
closeMenu();
}
});

menu?.querySelectorAll('.o-export-split__item').forEach((item) => {
item.addEventListener('click', () => {
runExport(item.dataset.format);
closeMenu();
});
Comment on lines +831 to +834
});

document.addEventListener('click', (event) => {
if (menu && !menu.hasAttribute('hidden') && !menu.contains(event.target) && event.target !== toggleBtn) {
closeMenu();
}
});
})
</script>
<?php
Expand Down
Loading
Loading