Initial commit - QBPOS Help

This commit is contained in:
2026-01-27 18:07:54 -06:00
commit e3d556b732
2307 changed files with 219842 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env python3
"""
Remove inline font-size styles from all HTML files in the POS_Help directory.
This allows the CSS rules to properly control font sizes.
"""
import os
import re
from pathlib import Path
def remove_inline_font_styles(file_path):
"""Remove style="font-size: Xpt;" from HTML files."""
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
original_content = content
# Remove style="font-size: 9pt;" and similar patterns
# Pattern matches: style="font-size: XXpt;" or style="font-size:XXpt;"
content = re.sub(r'\s+style="font-size:\s*\d+pt;"', '', content)
# Also handle cases where there might be other styles in the attribute
# Just remove the font-size portion
content = re.sub(r'(style="[^"]*?)font-size:\s*\d+pt;\s*', r'\1', content)
# Clean up empty style attributes
content = re.sub(r'\s+style=""', '', content)
if content != original_content:
with open(file_path, 'w', encoding='utf-8', errors='ignore') as f:
f.write(content)
return True
return False
except Exception as e:
print(f"Error processing {file_path}: {e}")
return False
def main():
base_dir = Path('/home/pts/Documents/QBPOS_Help_Web/QB_Help_Web/POS_Help')
if not base_dir.exists():
print(f"Error: Directory {base_dir} not found")
return
# Find all .htm files
htm_files = list(base_dir.rglob('*.htm'))
print(f"Found {len(htm_files)} HTML files")
print("Removing inline font-size styles...")
modified_count = 0
for htm_file in htm_files:
if remove_inline_font_styles(htm_file):
modified_count += 1
if modified_count % 100 == 0:
print(f" Processed {modified_count} files...")
print(f"\nComplete! Modified {modified_count} files")
print("All inline font-size styles have been removed.")
print("The CSS rules will now control all font sizes uniformly.")
if __name__ == "__main__":
main()

43
python/fix_navbar.py Normal file
View File

@@ -0,0 +1,43 @@
#!/usr/bin/env python3
import os
import re
import glob
# The correct navbar HTML to insert
navbar_html = '''<div class="mobile-nav">
<div class="mobile-nav-content">
<a href="../___left.htm" class="home-btn">🏠 Home</a>
<div class="site-title">QuickBooks Point of Sale Help</div>
</div>
</div>
<div class="offline-notice">Note: All Online Services are disabled and won't work. This is Completely Offline and No subscription needed</div>'''
os.chdir('/home/pts/Documents/QBPOS_Help_Web/QB_Help_Web/POS_Help')
# Process all htm files except special ones
files = []
for pattern in ['*/*.htm', '*/*/*.htm']:
files.extend(glob.glob(pattern))
files = [f for f in files if not f.startswith('___')]
print(f"Processing {len(files)} files...")
for filepath in files:
try:
with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Remove all mobile-nav and offline-notice divs
content = re.sub(r'<div class="mobile-nav">.*?</div>\s*</div>', '', content, flags=re.DOTALL)
content = re.sub(r'<div class="offline-notice">.*?</div>', '', content, flags=re.DOTALL)
# Insert navbar after <body> tag
content = re.sub(r'(<body[^>]*>)', r'\1\n' + navbar_html, content, count=1)
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
except Exception as e:
print(f"Error processing {filepath}: {e}")
print("Done!")

65
python/inject_style.py Normal file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env python3
"""
Inject inline style tag into all HTML files to force 12pt font
"""
import os
import re
from pathlib import Path
def inject_style_tag(file_path):
"""Add style tag after <head> to force 12pt."""
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
original_content = content
# Check if our style tag already exists
if '/* FORCE ALL FONTS TO 12PT */' in content:
return False
# Inject style tag after <head> or <HEAD>
style_injection = '''<style type="text/css">
/* FORCE ALL FONTS TO 12PT */
* { font-size: 12pt !important; }
body, p, li, ol, ul, div, span, td, th, h1, h2, h3, h4, h5, h6 { font-size: 12pt !important; }
</style>
'''
# Try to inject after <head>
content = re.sub(r'(<head[^>]*>)', r'\1\n' + style_injection, content, flags=re.IGNORECASE)
if content != original_content:
with open(file_path, 'w', encoding='utf-8', errors='ignore') as f:
f.write(content)
return True
return False
except Exception as e:
print(f"Error processing {file_path}: {e}")
return False
def main():
base_dir = Path('/home/pts/Documents/QBPOS_Help_Web/QB_Help_Web/POS_Help')
if not base_dir.exists():
print(f"Error: Directory {base_dir} not found")
return
# Find all .htm files
htm_files = list(base_dir.rglob('*.htm'))
print(f"Found {len(htm_files)} HTML files")
print("Injecting inline style tags...")
modified_count = 0
for htm_file in htm_files:
if inject_style_tag(htm_file):
modified_count += 1
if modified_count % 100 == 0:
print(f" Processed {modified_count} files...")
print(f"\nComplete! Modified {modified_count} files")
print("All files now have inline style tags forcing 12pt font.")
if __name__ == "__main__":
main()

83
python/move_styles.py Normal file
View File

@@ -0,0 +1,83 @@
#!/usr/bin/env python3
"""
Move inline style injection to AFTER the qbpos.css link
This ensures it has final say on font sizes
"""
import os
import re
from pathlib import Path
def move_style_after_css_link(file_path):
"""Move the FORCE ALL FONTS style tag to after qbpos.css link."""
try:
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
original_content = content
# Find the FORCE ALL FONTS style block
style_pattern = r'<style type="text/css">\s*/\* FORCE ALL FONTS TO 12PT \*/.*?</style>\s*'
style_match = re.search(style_pattern, content, re.DOTALL)
if not style_match:
return False
style_block = style_match.group(0)
# Remove the style block from its current position
content = content.replace(style_block, '', 1)
# Find the qbpos.css link and insert AFTER it
css_link_pattern = r'(<link[^>]*qbpos\.css[^>]*>)'
if re.search(css_link_pattern, content):
# Insert the style block right after the CSS link
content = re.sub(
css_link_pattern,
r'\1\n' + style_block,
content,
count=1
)
else:
# If no qbpos.css link found, put it after </head>
content = re.sub(
r'(</head>)',
style_block + r'\1',
content,
flags=re.IGNORECASE
)
if content != original_content:
with open(file_path, 'w', encoding='utf-8', errors='ignore') as f:
f.write(content)
return True
return False
except Exception as e:
print(f"Error processing {file_path}: {e}")
return False
def main():
base_dir = Path('/home/pts/Documents/QBPOS_Help_Web/QB_Help_Web/POS_Help')
if not base_dir.exists():
print(f"Error: Directory {base_dir} not found")
return
# Find all .htm files
htm_files = list(base_dir.rglob('*.htm'))
print(f"Found {len(htm_files)} HTML files")
print("Moving inline styles to after CSS links...")
modified_count = 0
for htm_file in htm_files:
if move_style_after_css_link(htm_file):
modified_count += 1
if modified_count % 100 == 0:
print(f" Processed {modified_count} files...")
print(f"\nComplete! Modified {modified_count} files")
print("Inline styles now come AFTER external CSS links for proper priority.")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""
Production HTTP Server for QuickBooks POS Help Documentation
Optimized for high traffic (100,000+ users)
"""
import http.server
import socketserver
import os
import logging
from datetime import datetime, timedelta
PORT = 8888
DIRECTORY = "QB_Help_Web"
# Configure minimal logging
logging.basicConfig(level=logging.ERROR)
class OptimizedHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
# Disable verbose request logging
def log_message(self, format, *args):
# Only log errors, not every request
if args[1][0] in ('4', '5'): # 4xx and 5xx errors only
super().log_message(format, *args)
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
def end_headers(self):
# Disable caching to ensure changes take effect immediately
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
super().end_headers()
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Use ThreadingTCPServer for handling multiple simultaneous connections
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True
daemon_threads = True
request_queue_size = 50 # Increase queue size for high traffic
print(f"="*60)
print(f"QuickBooks POS Help Documentation Server")
print(f"="*60)
print(f"Server running at:")
print(f" Local: http://localhost:{PORT}/POS_Help.html")
print(f" Network: http://192.168.10.130:{PORT}/POS_Help.html")
print(f"\nOptimizations:")
print(f" ✓ Static asset caching (1 year)")
print(f" ✓ Minimal logging (errors only)")
print(f" ✓ Multi-threaded connections")
print(f" ✓ High traffic queue (50 requests)")
print(f"\nFor 100,000+ users, consider:")
print(f" - Installing nginx (sudo apt install nginx)")
print(f" - Using a CDN for static assets")
print(f" - Load balancing across multiple servers")
print(f"\nPress Ctrl+C to stop the server")
print(f"="*60)
try:
with ThreadedTCPServer(("0.0.0.0", PORT), OptimizedHTTPRequestHandler) as httpd:
httpd.serve_forever()
except KeyboardInterrupt:
print("\n\nServer stopped.")

135
python/rebuild_left_nav.py Normal file
View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""
Rebuild ___left.htm from the .hhc table of contents file
"""
import re
import sys
def parse_hhc(hhc_file):
"""Parse the HHC file and extract navigation structure"""
with open(hhc_file, 'r', encoding='utf-8', errors='ignore') as f:
content = f.read()
# Extract all <object> blocks
items = []
level = 0
in_ul = False
lines = content.split('\n')
for i, line in enumerate(lines):
if '<ul>' in line.lower():
level += 1
in_ul = True
elif '</ul>' in line.lower():
level -= 1
elif '<object type="text/sitemap">' in line.lower():
# Get the next few lines to extract params
obj_lines = []
for j in range(i, min(i+10, len(lines))):
obj_lines.append(lines[j])
if '</object>' in lines[j].lower():
break
# Parse params
name = ''
local = ''
for ol in obj_lines:
name_match = re.search(r'<param name="Name" value="([^"]+)"', ol, re.IGNORECASE)
local_match = re.search(r'<param name="Local" value="([^"]+)"', ol, re.IGNORECASE)
if name_match:
name = name_match.group(1)
if local_match:
local = local_match.group(1)
if name:
items.append({
'level': level - 1, # Adjust for 0-based
'name': name,
'url': local if local else ''
})
return items
def generate_left_htm(items):
"""Generate the ___left.htm HTML content"""
html = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Contents</title>
<link rel="StyleSheet" href="___dtree.css" type="text/css" />
<link rel="StyleSheet" href="prompttech-header.css" type="text/css" />
<script language="JavaScript" src="___dtree.js" type="text/javascript"></script>
</head>
<body class="dtreebody">
<div class="dtreecontainer">
<script type="text/javascript">
<!--
d = new dTree('d');
d.config.useCookies = false;
"""
# Add root node
html += "d.add(0,-1,'QuickBooks Point of Sale Help','qbpos_basic_procedures/basic_welcome.htm','QuickBooks Point of Sale Help','body');\n"
# Add all items
node_id = 1
parent_stack = [0] # Stack to track parent IDs at each level
last_level = 0
for item in items:
level = item['level']
name = item['name'].replace("'", "\\'")
url = item['url'].replace("'", "\\'")
# Adjust parent stack based on level changes
if level > last_level:
# Going deeper
parent_stack.append(node_id - 1)
elif level < last_level:
# Going up - pop stack
for _ in range(last_level - level):
if len(parent_stack) > 1:
parent_stack.pop()
parent_id = parent_stack[-1]
# Add node
if url:
html += f"d.add({node_id},{parent_id},'{name}','{url}');\n"
else:
html += f"d.add({node_id},{parent_id},'{name}');\n"
node_id += 1
last_level = level
html += """
document.write(d);
//-->
</script>
</div>
</body>
</html>"""
return html
def main():
hhc_file = '/home/pts/Documents/QBPOS_Help_Web/QB_Help_Web/POS_Help/POS_Help_v19R1_Feb_2020.hhc'
output_file = '/home/pts/Documents/QBPOS_Help_Web/QB_Help_Web/POS_Help/___left.htm'
print(f"Parsing {hhc_file}...")
items = parse_hhc(hhc_file)
print(f"Found {len(items)} navigation items")
print(f"Generating {output_file}...")
html = generate_left_htm(items)
with open(output_file, 'w', encoding='utf-8') as f:
f.write(html)
print(f"Successfully rebuilt ___left.htm with {len(items)} items")
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,204 @@
#!/usr/bin/env python3
"""
Secure Production HTTP Server for QuickBooks POS Help Documentation
Features:
- IP whitelist security
- Rate limiting
- Auto-restart capability
- Production-ready error handling
- Logging
- HTTPS ready (configurable)
"""
import http.server
import socketserver
import os
import logging
import signal
import sys
from datetime import datetime
from collections import defaultdict
from threading import Lock
import time
# ============ CONFIGURATION ============
PORT = 8888
DIRECTORY = "QB_Help_Web"
# Security: Allowed IP addresses (empty list = allow all)
# Add your allowed IPs here, e.g., ['192.168.10.0/24', '10.0.0.1']
ALLOWED_IPS = [] # Empty = allow all. Populate for security.
# Rate limiting: max requests per IP per minute
RATE_LIMIT_REQUESTS = 1000
RATE_LIMIT_WINDOW = 60 # seconds
# Logging configuration
LOG_FILE = "/tmp/qbpos_help_server.log"
LOG_LEVEL = logging.INFO
# HTTPS Configuration (set to True when ready)
USE_HTTPS = False
SSL_CERT_FILE = "/etc/ssl/certs/server.crt" # Update path when ready
SSL_KEY_FILE = "/etc/ssl/private/server.key" # Update path when ready
# ============ END CONFIGURATION ============
# Configure logging
logging.basicConfig(
level=LOG_LEVEL,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(LOG_FILE),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
# Rate limiting storage
rate_limit_data = defaultdict(list)
rate_limit_lock = Lock()
def is_ip_allowed(client_ip):
"""Check if IP is in whitelist. If whitelist is empty, allow all."""
if not ALLOWED_IPS:
return True
# Simple IP matching (for production, use ipaddress module for CIDR)
for allowed in ALLOWED_IPS:
if client_ip.startswith(allowed.split('/')[0]):
return True
return False
def check_rate_limit(client_ip):
"""Check if client has exceeded rate limit."""
with rate_limit_lock:
current_time = time.time()
# Clean old entries
rate_limit_data[client_ip] = [
t for t in rate_limit_data[client_ip]
if current_time - t < RATE_LIMIT_WINDOW
]
# Check limit
if len(rate_limit_data[client_ip]) >= RATE_LIMIT_REQUESTS:
return False
# Add current request
rate_limit_data[client_ip].append(current_time)
return True
class SecureHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
"""Secure HTTP request handler with IP whitelist and rate limiting."""
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
def do_GET(self):
"""Handle GET requests with security checks."""
client_ip = self.client_address[0]
# Check IP whitelist
if not is_ip_allowed(client_ip):
logger.warning(f"Blocked request from unauthorized IP: {client_ip}")
self.send_error(403, "Forbidden: Access denied")
return
# Check rate limit
if not check_rate_limit(client_ip):
logger.warning(f"Rate limit exceeded for IP: {client_ip}")
self.send_error(429, "Too Many Requests: Rate limit exceeded")
return
# Process request
try:
super().do_GET()
except Exception as e:
logger.error(f"Error processing request from {client_ip}: {e}")
self.send_error(500, "Internal Server Error")
def log_message(self, format, *args):
"""Log only errors and important events."""
if args[1][0] in ('4', '5'): # 4xx and 5xx errors
logger.warning(f"{self.client_address[0]} - {format % args}")
def end_headers(self):
"""Add security and cache headers."""
# Security headers
self.send_header('X-Content-Type-Options', 'nosniff')
self.send_header('X-Frame-Options', 'SAMEORIGIN')
self.send_header('X-XSS-Protection', '1; mode=block')
# Cache control
self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
super().end_headers()
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
"""Threaded TCP server for concurrent connections."""
allow_reuse_address = True
daemon_threads = True
request_queue_size = 100
def signal_handler(sig, frame):
"""Handle graceful shutdown."""
logger.info("\nShutting down server gracefully...")
sys.exit(0)
def main():
"""Main server function."""
# Change to script directory
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Register signal handlers
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# Print startup information
protocol = "HTTPS" if USE_HTTPS else "HTTP"
print("=" * 70)
print("QuickBooks POS Help Documentation Server - SECURE MODE")
print("=" * 70)
print(f"Server running at:")
print(f" Local: {protocol.lower()}://localhost:{PORT}/POS_Help.html")
print(f" Network: {protocol.lower()}://192.168.10.130:{PORT}/POS_Help.html")
print(f"\nSecurity Features:")
print(f" ✓ IP Whitelist: {'Enabled' if ALLOWED_IPS else 'Disabled (Allow All)'}")
print(f" ✓ Rate Limiting: {RATE_LIMIT_REQUESTS} req/min per IP")
print(f" ✓ Security Headers: Enabled")
print(f" ✓ HTTPS: {'Enabled' if USE_HTTPS else 'Ready (Set USE_HTTPS=True)'}")
print(f"\nLogging:")
print(f" ✓ Log File: {LOG_FILE}")
print(f" ✓ Log Level: {logging.getLevelName(LOG_LEVEL)}")
print(f"\nPress Ctrl+C to stop")
print("=" * 70)
logger.info(f"Server starting on port {PORT}")
try:
if USE_HTTPS:
import ssl
# HTTPS server (when ready)
httpd = ThreadedTCPServer(("0.0.0.0", PORT), SecureHTTPRequestHandler)
httpd.socket = ssl.wrap_socket(
httpd.socket,
certfile=SSL_CERT_FILE,
keyfile=SSL_KEY_FILE,
server_side=True
)
logger.info("HTTPS enabled")
else:
# HTTP server
httpd = ThreadedTCPServer(("0.0.0.0", PORT), SecureHTTPRequestHandler)
logger.info("Server ready to accept connections")
httpd.serve_forever()
except Exception as e:
logger.error(f"Server error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()

39
python/server.py Normal file
View File

@@ -0,0 +1,39 @@
#!/usr/bin/env python3
"""
Simple HTTP Server for QuickBooks POS Help Documentation
Access via: http://localhost:8888
"""
import http.server
import socketserver
import os
from datetime import datetime
PORT = 8888
DIRECTORY = "QB_Help_Web"
class MyHTTPRequestHandler(http.server.SimpleHTTPRequestHandler):
def __init__(self, *args, **kwargs):
super().__init__(*args, directory=DIRECTORY, **kwargs)
def end_headers(self):
# Disable caching for development
self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
self.send_header('Pragma', 'no-cache')
self.send_header('Expires', '0')
super().end_headers()
os.chdir(os.path.dirname(os.path.abspath(__file__)))
# Use ThreadingTCPServer for better responsiveness
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
allow_reuse_address = True
with ThreadedTCPServer(("0.0.0.0", PORT), MyHTTPRequestHandler) as httpd:
print(f"Server running at:")
print(f" Local access: http://localhost:{PORT}/POS_Help.html")
print(f" Network access: http://192.168.10.130:{PORT}/POS_Help.html")
print(f"\nFeatures:")
print(f" - No caching (instant updates)")
print(f" - Multi-threaded (faster response)")
print(f"\nPress Ctrl+C to stop the server")
httpd.serve_forever()