from flask import Flask, render_template, request, session, redirect, url_for, make_response, jsonify, flash from flask_assets import Environment, Bundle from flask_mail import Mail, Message from translation_manager import init_app, translate, get_current_language, create_language_selector, set_language import os import re from datetime import datetime from xml.etree import ElementTree as ET app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY', 'your-secret-key-change-in-production') # Email configuration app.config['MAIL_SERVER'] = os.environ.get('MAIL_SERVER', 'smtp.gmail.com') app.config['MAIL_PORT'] = int(os.environ.get('MAIL_PORT', 587)) app.config['MAIL_USE_TLS'] = os.environ.get('MAIL_USE_TLS', 'True').lower() == 'true' app.config['MAIL_USE_SSL'] = os.environ.get('MAIL_USE_SSL', 'False').lower() == 'true' app.config['MAIL_USERNAME'] = os.environ.get('MAIL_USERNAME', 'contact@kobelly.be') app.config['MAIL_PASSWORD'] = os.environ.get('MAIL_PASSWORD', '') app.config['MAIL_DEFAULT_SENDER'] = os.environ.get('MAIL_DEFAULT_SENDER', 'contact@kobelly.be') # Initialize Flask-Mail mail = Mail(app) # Debug: Print email configuration print("DEBUG: Email Configuration:") print(f" MAIL_SERVER: {app.config.get('MAIL_SERVER')}") print(f" MAIL_PORT: {app.config.get('MAIL_PORT')}") print(f" MAIL_USE_TLS: {app.config.get('MAIL_USE_TLS')}") print(f" MAIL_USERNAME: {app.config.get('MAIL_USERNAME')}") print(f" MAIL_DEFAULT_SENDER: {app.config.get('MAIL_DEFAULT_SENDER')}") def validate_email(email): """Validate email format""" pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' return re.match(pattern, email) is not None def validate_phone(phone): """Validate phone number format (basic validation)""" if not phone: return True # Phone is optional # Remove all non-digit characters digits_only = re.sub(r'\D', '', phone) return len(digits_only) >= 8 def send_contact_email(form_data): """Send contact form email""" try: # Create email content subject = f"New Contact Form Submission from {form_data['firstName']} {form_data['lastName']}" # HTML email body html_body = f"""

New Contact Form Submission

Name: {form_data['firstName']} {form_data['lastName']}

Email: {form_data['email']}

Phone: {form_data.get('phone', 'Not provided')}

Company: {form_data.get('company', 'Not provided')}

Service Interested In: {form_data['service']}

Budget: {form_data.get('budget', 'Not specified')}

Message:

{form_data['message'].replace(chr(10), '
')}

""" # Plain text email body text_body = f""" New Contact Form Submission Name: {form_data['firstName']} {form_data['lastName']} Email: {form_data['email']} Phone: {form_data.get('phone', 'Not provided')} Company: {form_data.get('company', 'Not provided')} Service Interested In: {form_data['service']} Budget: {form_data.get('budget', 'Not specified')} Message: {form_data['message']} """ # Create message msg = Message( subject=subject, recipients=[app.config['MAIL_DEFAULT_SENDER']], html=html_body, body=text_body ) # Send email mail.send(msg) return True, "Email sent successfully" except Exception as e: return False, f"Failed to send email: {str(e)}" # Initialize Flask-Assets assets = Environment(app) assets.url = app.static_url_path # Configure assets for production if not app.debug: assets.auto_build = False assets.cache = True assets.manifest = 'file' # Register asset bundles css_bundle = assets.register('css_all', Bundle( 'css/main.css', filters='cssmin', output='css/main.min.css' )) js_bundle = assets.register('js_all', Bundle( 'js/main.js', filters='jsmin', output='js/main.min.js' )) # Initialize the translation system init_app(app) # WWW to non-WWW redirect @app.before_request def redirect_www(): """Redirect www subdomain to non-www version""" if request.host.startswith('www.'): # Get the non-www version of the host non_www_host = request.host.replace('www.', '', 1) # Preserve the full URL including path and query parameters url = request.url.replace(request.host, non_www_host) return redirect(url, code=301) # Context processor to make variables available in templates @app.context_processor def inject_conf_var(): return dict( translate=translate, get_current_language=get_current_language, create_language_selector=create_language_selector, t=translate, # Short alias current_year=datetime.now().year ) def generate_sitemap(): """Generate XML sitemap for the website""" root = ET.Element("urlset") root.set("xmlns", "http://www.sitemaps.org/schemas/sitemap/0.9") # Define the main pages with their priorities and change frequencies pages = [ {'url': '/', 'priority': '1.0', 'changefreq': 'weekly'}, {'url': '/services', 'priority': '0.9', 'changefreq': 'monthly'}, {'url': '/about', 'priority': '0.8', 'changefreq': 'monthly'}, {'url': '/contact', 'priority': '0.7', 'changefreq': 'monthly'}, {'url': '/portfolio', 'priority': '0.8', 'changefreq': 'weekly'}, ] base_url = "https://kobelly.com" # Change this to your actual domain for page in pages: url_elem = ET.SubElement(root, "url") loc = ET.SubElement(url_elem, "loc") loc.text = base_url + page['url'] lastmod = ET.SubElement(url_elem, "lastmod") lastmod.text = datetime.now().strftime("%Y-%m-%d") changefreq = ET.SubElement(url_elem, "changefreq") changefreq.text = page['changefreq'] priority = ET.SubElement(url_elem, "priority") priority.text = page['priority'] return ET.tostring(root, encoding='unicode', method='xml') @app.route('/sitemap.xml') def sitemap(): """Serve the XML sitemap""" sitemap_xml = generate_sitemap() response = make_response(sitemap_xml) response.headers["Content-Type"] = "application/xml" return response @app.route('/') def index(): print("Current language in index:", get_current_language()) return render_template('index.html') @app.route('/services') def services(): return render_template('services.html') @app.route('/contact') def contact(): return render_template('contact.html') @app.route('/contact', methods=['POST']) def submit_contact(): """Handle contact form submission""" try: # Get form data data = request.get_json() print('DEBUG: Received form data:', data) # Validate required fields required_fields = ['firstName', 'lastName', 'email', 'service', 'message'] for field in required_fields: if not data.get(field) or not data[field].strip(): print(f'DEBUG: Missing required field: {field}') return jsonify({ 'success': False, 'message': f'{field.replace("firstName", "First name").replace("lastName", "Last name").title()} is required' }), 400 # Validate email format if not validate_email(data['email']): print('DEBUG: Invalid email format:', data['email']) return jsonify({ 'success': False, 'message': 'Please enter a valid email address' }), 400 # Validate phone if provided if data.get('phone') and not validate_phone(data['phone']): print('DEBUG: Invalid phone format:', data['phone']) return jsonify({ 'success': False, 'message': 'Please enter a valid phone number' }), 400 # Prepare form data form_data = { 'firstName': data['firstName'].strip(), 'lastName': data['lastName'].strip(), 'email': data['email'].strip(), 'phone': data.get('phone', '').strip(), 'company': data.get('company', '').strip(), 'service': data['service'].strip(), 'budget': data.get('budget', '').strip(), 'message': data['message'].strip() } print('DEBUG: Prepared form data for email:', form_data) # Send email success, message = send_contact_email(form_data) print('DEBUG: Email send result:', success, message) if success: return jsonify({ 'success': True, 'message': 'Thank you for your message! I will get back to you within 48 hours.' }) else: print('DEBUG: Email sending failed:', message) return jsonify({ 'success': False, 'message': 'Sorry, there was an error sending your message. Please try again or contact me directly.' }), 500 except Exception as e: import traceback print('DEBUG: Exception in /contact POST route:', e) traceback.print_exc() return jsonify({ 'success': False, 'message': 'An unexpected error occurred. Please try again.' }), 500 @app.route('/about') def about(): return render_template('about.html') @app.route('/portfolio') def portfolio(): return render_template('portfolio.html') @app.route('/set_language/') def set_language_route(language): """Set the language and redirect back to the previous page""" if set_language(language): # Redirect back to the referring page or home return redirect(request.referrer or url_for('index')) return redirect(url_for('index')) @app.route('/build-assets') def build_assets(): """Build minified assets with cache busting""" if app.debug: try: # Build assets css_bundle.build() js_bundle.build() return {'status': 'success', 'message': 'Assets built successfully'} except Exception as e: return {'status': 'error', 'message': str(e)}, 500 else: return {'status': 'error', 'message': 'Asset building only available in debug mode'}, 403 if __name__ == '__main__': # Check environment variables for debug mode debug_mode = os.environ.get('FLASK_DEBUG', '1') == '1' and os.environ.get('FLASK_ENV') != 'production' app.run(debug=debug_mode, host='0.0.0.0', port=5000)