Tidying the repo

This commit is contained in:
2025-10-23 13:50:03 -04:00
parent d55798508d
commit be68a76b8b
13 changed files with 0 additions and 0 deletions

11
Firefox/Code/INSTALL.md Normal file
View File

@@ -0,0 +1,11 @@
# Installing SimplyReports SQL Extractor (SiReS Ex)
1. Download ```sires-ex.xpi```
2. Drag that file into Firefox
3. Click "Add" when prompted
## Troubleshooting
- If the icon doesn't appear, check the Extensions page (`about:addons`)
- Make sure the target page has an input with id or name "SQLStatementHide"
- Check browser console for any error messages

18
Firefox/Code/README.md Normal file
View File

@@ -0,0 +1,18 @@
# SimplyReports SQL Extractor (SiReS Ex)
The SimplyReports SQL Extractor is a Firefox add-on that will pull the SQL query from a SimplyReports results page. After extracting the query, it allows you to copy that query to the clipboard or save it as a file.
## Features
- Visual indicator when a SimplyReports SQL query is found on a page
- Copy extracted SQL to clipboard
- Save SQL as .sql file with custom filename
- Basic SQL formatting for better readability
- Auto-extraction when opening the extension popup
## Usage
1. Build a report in SimplyReports.
2. Run the report.
3. On the results page, you should see a green indicator on the top right of the page that indicates that SiReS Ex found a SimplyReports query in your results page.
4. Click the add-on's icon and you'll be prompted to copy the query to the clipboard or save the query to a file.

View File

@@ -0,0 +1,29 @@
// background.js - Background script for handling downloads
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "downloadSQL") {
const { sqlQuery, filename } = request;
// Create blob with SQL content
const blob = new Blob([sqlQuery], { type: 'text/sql' });
const url = URL.createObjectURL(blob);
// Generate filename if not provided
const finalFilename = filename || `sql_query_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.sql`;
// Download the file
browser.downloads.download({
url: url,
filename: finalFilename,
saveAs: true
}).then(() => {
URL.revokeObjectURL(url);
sendResponse({ success: true });
}).catch((error) => {
console.error('Download failed:', error);
sendResponse({ success: false, error: error.message });
});
return true; // Keep the message channel open for async response
}
});

89
Firefox/Code/content.js Normal file
View File

@@ -0,0 +1,89 @@
// content.js - Content script that runs on web pages
function extractSQLQuery() {
// Look for the specific input element
const sqlInput = document.getElementById('SQLStatementHide');
if (sqlInput) {
const sqlQuery = sqlInput.value;
if (sqlQuery && sqlQuery.trim()) {
return cleanupSQLQuery(sqlQuery.trim());
} else {
return null;
}
}
// Fallback: look for any input with name="SQLStatementHide"
const sqlInputByName = document.querySelector('input[name="SQLStatementHide"]');
if (sqlInputByName) {
const sqlQuery = sqlInputByName.value;
if (sqlQuery && sqlQuery.trim()) {
return cleanupSQLQuery(sqlQuery.trim());
}
}
return null;
}
function cleanupSQLQuery(sqlQuery) {
// Fix double single quotes around dates/values (''2017-10-23'' becomes '2017-10-23')
// This handles the common issue where systems incorrectly double-escape quotes
return sqlQuery.replace(/''/g, "'");
}
// Listen for messages from popup
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === "extractSQL") {
const sqlQuery = extractSQLQuery();
sendResponse({
success: sqlQuery !== null,
sqlQuery: sqlQuery,
url: window.location.href,
timestamp: new Date().toISOString()
});
}
});
// Optional: Add a visual indicator when SQL is found
function addVisualIndicator() {
const sqlInput = document.getElementById('SQLStatementHide') ||
document.querySelector('input[name="SQLStatementHide"]');
if (sqlInput && sqlInput.value && sqlInput.value.trim()) {
// Add a small visual indicator that SQL was found
if (!document.getElementById('sql-extractor-indicator')) {
const indicator = document.createElement('div');
indicator.id = 'sql-extractor-indicator';
indicator.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background: #4CAF50;
color: white;
padding: 5px 10px;
border-radius: 3px;
font-size: 12px;
z-index: 10000;
font-family: Arial, sans-serif;
opacity: 0.8;
pointer-events: none;
`;
indicator.textContent = 'SQL Query Found';
document.body.appendChild(indicator);
// Remove indicator after 3 seconds
setTimeout(() => {
if (document.getElementById('sql-extractor-indicator')) {
document.body.removeChild(indicator);
}
}, 3000);
}
}
}
// Run indicator check when page loads
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addVisualIndicator);
} else {
addVisualIndicator();
}

BIN
Firefox/Code/icon-128.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

BIN
Firefox/Code/icon-16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

BIN
Firefox/Code/icon-32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
Firefox/Code/icon-48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 990 B

11
Firefox/Code/icon.svg Normal file
View File

@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48" height="48">
<!-- Background -->
<rect width="48" height="48" rx="8" fill="#2196F3"/>
<!-- SQL text -->
<text x="24" y="20" font-family="Arial, sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="white">SQL</text>
<!-- Extract arrow -->
<path d="M18 28 L24 34 L30 28" stroke="white" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M24 32 L24 38" stroke="white" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 561 B

View File

@@ -0,0 +1,42 @@
{
"manifest_version": 2,
"name": "SimplyReports SQL Extractor",
"version": "1.0",
"description": "Grab the SQL query used in a SimplyReports results page and copy it to the clipboard or save it to a file.",
"permissions": [
"activeTab",
"clipboardWrite",
"downloads"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html",
"default_title": "Extract SQL Query",
"default_icon": {
"16": "icon-16.png",
"32": "icon-32.png",
"48": "icon-48.png",
"128": "icon-128.png"
}
},
"icons": {
"16": "icon-16.png",
"32": "icon-32.png",
"48": "icon-48.png",
"128": "icon-128.png"
}
}

149
Firefox/Code/popup.html Normal file
View File

@@ -0,0 +1,149 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
width: 350px;
padding: 15px;
font-family: Arial, sans-serif;
margin: 0;
}
.header {
text-align: center;
margin-bottom: 15px;
}
.header h3 {
margin: 0 0 5px 0;
color: #333;
}
.status {
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
text-align: center;
font-weight: bold;
}
.status.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.sql-preview {
background-color: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
margin-bottom: 15px;
max-height: 200px;
overflow-y: auto;
font-family: 'Courier New', Consolas, monospace;
font-size: 13px;
white-space: pre;
word-break: normal;
line-height: 1.4;
color: #333;
}
.actions {
display: flex;
flex-direction: column;
gap: 10px;
}
button {
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
font-weight: bold;
transition: background-color 0.2s;
}
.btn-primary {
background-color: #007bff;
color: white;
}
.btn-primary:hover {
background-color: #0056b3;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
}
.btn-success {
background-color: #28a745;
color: white;
}
.btn-success:hover {
background-color: #1e7e34;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.filename-input {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
margin-bottom: 10px;
width: 100%;
box-sizing: border-box;
}
.info {
font-size: 11px;
color: #666;
text-align: center;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="header">
<h3>SQL Query Extractor</h3>
</div>
<div id="status" class="status" style="display: none;"></div>
<div id="sqlPreview" class="sql-preview" style="display: none;"></div>
<div class="actions">
<button id="extractBtn" class="btn-primary">Extract SQL Query</button>
<div id="actionButtons" style="display: none;">
<button id="copyBtn" class="btn-success">Copy to Clipboard</button>
<input type="text" id="filenameInput" class="filename-input" placeholder="Enter filename (optional)">
<button id="downloadBtn" class="btn-secondary">Save as File</button>
</div>
</div>
<div class="info">
Click "Extract SQL Query" to search for SQL in the current page
</div>
<script src="popup.js"></script>
</body>
</html>

167
Firefox/Code/popup.js Normal file
View File

@@ -0,0 +1,167 @@
// popup.js - Popup script for user interactions
let currentSQLQuery = null;
document.addEventListener('DOMContentLoaded', function() {
const extractBtn = document.getElementById('extractBtn');
const copyBtn = document.getElementById('copyBtn');
const downloadBtn = document.getElementById('downloadBtn');
const filenameInput = document.getElementById('filenameInput');
const statusDiv = document.getElementById('status');
const sqlPreview = document.getElementById('sqlPreview');
const actionButtons = document.getElementById('actionButtons');
// Extract SQL Query
extractBtn.addEventListener('click', function() {
extractBtn.disabled = true;
extractBtn.textContent = 'Extracting...';
// Send message to content script
browser.tabs.query({ active: true, currentWindow: true }, function(tabs) {
browser.tabs.sendMessage(tabs[0].id, { action: "extractSQL" }, function(response) {
extractBtn.disabled = false;
extractBtn.textContent = 'Extract SQL Query';
if (response && response.success) {
// Debug: Log the original SQL
console.log('Original SQL:', response.sqlQuery);
// Store the formatted version as the main query
currentSQLQuery = formatSQL(response.sqlQuery);
// Debug: Log the formatted SQL
console.log('Formatted SQL:', currentSQLQuery);
console.log('Formatted SQL contains newlines:', currentSQLQuery.includes('\n'));
showStatus('success', 'SQL Query found and extracted!');
showSQLPreview(currentSQLQuery);
actionButtons.style.display = 'block';
// Set default filename based on current page
const url = new URL(response.url);
const defaultName = `sql_query_${url.hostname}_${new Date().toISOString().slice(0, 10)}.sql`;
filenameInput.value = defaultName;
} else {
showStatus('error', 'No SQL query found in the current page');
actionButtons.style.display = 'none';
sqlPreview.style.display = 'none';
currentSQLQuery = null;
}
});
});
});
// Copy to Clipboard
copyBtn.addEventListener('click', function() {
if (!currentSQLQuery) return;
// Debug: Log what we're trying to copy
console.log('Copying SQL:', currentSQLQuery);
console.log('SQL length:', currentSQLQuery.length);
console.log('Contains newlines:', currentSQLQuery.includes('\n'));
navigator.clipboard.writeText(currentSQLQuery).then(function() {
const originalText = copyBtn.textContent;
copyBtn.textContent = 'Copied!';
copyBtn.style.backgroundColor = '#28a745';
setTimeout(function() {
copyBtn.textContent = originalText;
copyBtn.style.backgroundColor = '';
}, 1500);
}).catch(function(err) {
showStatus('error', 'Failed to copy to clipboard: ' + err.message);
});
});
// Download as File
downloadBtn.addEventListener('click', function() {
if (!currentSQLQuery) return;
downloadBtn.disabled = true;
downloadBtn.textContent = 'Downloading...';
const filename = filenameInput.value.trim() || null;
// Send message to background script
browser.runtime.sendMessage({
action: "downloadSQL",
sqlQuery: currentSQLQuery,
filename: filename
}, function(response) {
downloadBtn.disabled = false;
downloadBtn.textContent = 'Save as File';
if (response && response.success) {
const originalText = downloadBtn.textContent;
downloadBtn.textContent = 'Downloaded!';
downloadBtn.style.backgroundColor = '#28a745';
setTimeout(function() {
downloadBtn.textContent = originalText;
downloadBtn.style.backgroundColor = '';
}, 1500);
} else {
showStatus('error', 'Failed to download file: ' + (response ? response.error : 'Unknown error'));
}
});
});
function showStatus(type, message) {
statusDiv.className = `status ${type}`;
statusDiv.textContent = message;
statusDiv.style.display = 'block';
// Auto-hide after 5 seconds
setTimeout(function() {
statusDiv.style.display = 'none';
}, 5000);
}
function showSQLPreview(sqlQuery) {
sqlPreview.textContent = sqlQuery;
sqlPreview.style.display = 'block';
}
function formatSQL(sql) {
// Simple but reliable SQL formatting
let formatted = sql.trim();
// Normalize whitespace first
formatted = formatted.replace(/\s+/g, ' ');
// Add line breaks before major keywords (simple approach)
formatted = formatted.replace(/\sFROM\s/gi, '\nFROM ');
formatted = formatted.replace(/\sWHERE\s/gi, '\nWHERE ');
formatted = formatted.replace(/\sAND\s/gi, '\n AND ');
formatted = formatted.replace(/\sOR\s/gi, '\n OR ');
formatted = formatted.replace(/\sORDER\sBY\s/gi, '\nORDER BY ');
formatted = formatted.replace(/\sGROUP\sBY\s/gi, '\nGROUP BY ');
formatted = formatted.replace(/\sHAVING\s/gi, '\nHAVING ');
formatted = formatted.replace(/\sJOIN\s/gi, '\nJOIN ');
formatted = formatted.replace(/\sINNER\sJOIN\s/gi, '\nINNER JOIN ');
formatted = formatted.replace(/\sLEFT\sJOIN\s/gi, '\nLEFT JOIN ');
formatted = formatted.replace(/\sRIGHT\sJOIN\s/gi, '\nRIGHT JOIN ');
// Add semicolon if missing
if (!formatted.trim().endsWith(';')) {
formatted += ';';
}
// Clean up any leading/trailing whitespace on lines
formatted = formatted.split('\n').map(line => line.trim()).join('\n');
return formatted;
}
// Auto-extract on popup open if we're on a page that might have SQL
browser.tabs.query({ active: true, currentWindow: true }, function(tabs) {
const currentTab = tabs[0];
if (currentTab.url && !currentTab.url.startsWith('chrome://') && !currentTab.url.startsWith('moz-extension://')) {
// Auto-extract after a short delay
setTimeout(function() {
extractBtn.click();
}, 500);
}
});
});