diff --git a/__pycache__/forms.cpython-313.pyc b/__pycache__/forms.cpython-313.pyc index 4baab87..4f89d08 100644 Binary files a/__pycache__/forms.cpython-313.pyc and b/__pycache__/forms.cpython-313.pyc differ diff --git a/forms.py b/forms.py index 715229c..5e05847 100644 --- a/forms.py +++ b/forms.py @@ -56,4 +56,18 @@ class ConversationForm(FlaskForm): def __init__(self, *args, **kwargs): super(ConversationForm, self).__init__(*args, **kwargs) - self.members.choices = [(u.id, f"{u.username} {u.last_name}") for u in User.query.filter_by(is_active=True).all()] \ No newline at end of file + self.members.choices = [(u.id, f"{u.username} {u.last_name}") for u in User.query.filter_by(is_active=True).all()] + +class CompanySettingsForm(FlaskForm): + company_name = StringField('Company Name', validators=[Optional(), Length(max=100)]) + company_website = StringField('Website', validators=[Optional(), Length(max=200)]) + company_email = StringField('Email', validators=[Optional(), Email(), Length(max=100)]) + company_phone = StringField('Phone', validators=[Optional(), Length(max=20)]) + company_address = StringField('Address', validators=[Optional(), Length(max=200)]) + company_city = StringField('City', validators=[Optional(), Length(max=100)]) + company_state = StringField('State', validators=[Optional(), Length(max=100)]) + company_zip = StringField('ZIP Code', validators=[Optional(), Length(max=20)]) + company_country = StringField('Country', validators=[Optional(), Length(max=100)]) + company_description = TextAreaField('Description', validators=[Optional()]) + company_industry = StringField('Industry', validators=[Optional(), Length(max=100)]) + company_logo = FileField('Company Logo', validators=[FileAllowed(['jpg', 'jpeg', 'png', 'gif'], 'Images only!')]) \ No newline at end of file diff --git a/routes/__pycache__/main.cpython-313.pyc b/routes/__pycache__/main.cpython-313.pyc index 55ec96e..88f6758 100644 Binary files a/routes/__pycache__/main.cpython-313.pyc and b/routes/__pycache__/main.cpython-313.pyc differ diff --git a/routes/__pycache__/trash.cpython-313.pyc b/routes/__pycache__/trash.cpython-313.pyc index 1522d8f..af2d832 100644 Binary files a/routes/__pycache__/trash.cpython-313.pyc and b/routes/__pycache__/trash.cpython-313.pyc differ diff --git a/routes/main.py b/routes/main.py index f82d73a..c874956 100644 --- a/routes/main.py +++ b/routes/main.py @@ -9,6 +9,7 @@ from datetime import datetime, timedelta import logging import sys import time +from forms import CompanySettingsForm # Set up logging to show in console logging.basicConfig( @@ -407,54 +408,61 @@ def init_routes(main_bp): flash('Only administrators can access settings.', 'error') return redirect(url_for('main.dashboard')) - site_settings = SiteSettings.get_settings() + # Get active tab from URL or default to colors active_tab = request.args.get('tab', 'colors') + # Get site settings + site_settings = SiteSettings.get_settings() + # Get events data if events tab is active events = None - total_pages = 1 + total_pages = 0 current_page = 1 users = [] if active_tab == 'events': # Get filter parameters - event_type = request.args.get('event_type') + event_type = request.args.get('event_type', '') date_range = request.args.get('date_range', '7d') - user_id = request.args.get('user_id') + user_id = request.args.get('user_id', '') page = request.args.get('page', 1, type=int) - per_page = 50 - - # Calculate date range - end_date = datetime.utcnow() - if date_range == '24h': - start_date = end_date - timedelta(days=1) - elif date_range == '7d': - start_date = end_date - timedelta(days=7) - elif date_range == '30d': - start_date = end_date - timedelta(days=30) - else: - start_date = None + per_page = 10 # Build query query = Event.query + # Apply filters if event_type: - query = query.filter_by(event_type=event_type) - if start_date: - query = query.filter(Event.timestamp >= start_date) + query = query.filter(Event.event_type == event_type) if user_id: - query = query.filter_by(user_id=user_id) - - # Get total count for pagination - total_events = query.count() - total_pages = (total_events + per_page - 1) // per_page + query = query.filter(Event.user_id == user_id) + if date_range: + cutoff_date = datetime.utcnow() - timedelta(days=int(date_range[:-1])) + query = query.filter(Event.timestamp >= cutoff_date) # Get paginated events events = query.order_by(Event.timestamp.desc()).paginate(page=page, per_page=per_page) + total_pages = events.pages + current_page = events.page # Get all users for filter dropdown users = User.query.order_by(User.username).all() + # Create form for company settings + company_form = CompanySettingsForm() + if request.method == 'GET': + company_form.company_name.data = site_settings.company_name + company_form.company_website.data = site_settings.company_website + company_form.company_email.data = site_settings.company_email + company_form.company_phone.data = site_settings.company_phone + company_form.company_address.data = site_settings.company_address + company_form.company_city.data = site_settings.company_city + company_form.company_state.data = site_settings.company_state + company_form.company_zip.data = site_settings.company_zip + company_form.company_country.data = site_settings.company_country + company_form.company_description.data = site_settings.company_description + company_form.company_industry.data = site_settings.company_industry + return render_template('settings/settings.html', primary_color=site_settings.primary_color, secondary_color=site_settings.secondary_color, @@ -464,7 +472,7 @@ def init_routes(main_bp): total_pages=total_pages, current_page=current_page, users=users, - csrf_token=session.get('csrf_token')) + form=company_form) @main_bp.route('/settings/colors', methods=['POST']) @login_required @@ -544,12 +552,17 @@ def init_routes(main_bp): flash('Only administrators can update settings.', 'error') return redirect(url_for('main.dashboard')) + form = CompanySettingsForm() + if not form.validate(): + flash('Please check the form for errors.', 'error') + return redirect(url_for('main.settings')) + site_settings = SiteSettings.get_settings() # Handle logo upload - if 'company_logo' in request.files: - logo_file = request.files['company_logo'] - if logo_file and logo_file.filename: + if form.company_logo.data: + logo_file = form.company_logo.data + if logo_file.filename: # Delete old logo if it exists if site_settings.company_logo: old_logo_path = os.path.join('static', 'uploads', 'company_logos', site_settings.company_logo) @@ -565,17 +578,17 @@ def init_routes(main_bp): site_settings.company_logo = filename # Update all company fields - site_settings.company_name = request.form.get('company_name') - site_settings.company_website = request.form.get('company_website') - site_settings.company_email = request.form.get('company_email') - site_settings.company_phone = request.form.get('company_phone') - site_settings.company_address = request.form.get('company_address') - site_settings.company_city = request.form.get('company_city') - site_settings.company_state = request.form.get('company_state') - site_settings.company_zip = request.form.get('company_zip') - site_settings.company_country = request.form.get('company_country') - site_settings.company_description = request.form.get('company_description') - site_settings.company_industry = request.form.get('company_industry') + site_settings.company_name = form.company_name.data + site_settings.company_website = form.company_website.data + site_settings.company_email = form.company_email.data + site_settings.company_phone = form.company_phone.data + site_settings.company_address = form.company_address.data + site_settings.company_city = form.company_city.data + site_settings.company_state = form.company_state.data + site_settings.company_zip = form.company_zip.data + site_settings.company_country = form.company_country.data + site_settings.company_description = form.company_description.data + site_settings.company_industry = form.company_industry.data try: db.session.commit() @@ -599,7 +612,7 @@ def init_routes(main_bp): 'company_country': site_settings.company_country, 'company_description': site_settings.company_description, 'company_industry': site_settings.company_industry, - 'logo_updated': bool(request.files.get('company_logo')) + 'logo_updated': bool(form.company_logo.data) } } ) @@ -607,10 +620,11 @@ def init_routes(main_bp): flash('Company settings updated successfully!', 'success') except Exception as e: + logger.error(f"Error updating company settings: {str(e)}") db.session.rollback() flash('An error occurred while updating company settings.', 'error') - return redirect(url_for('main.settings', tab='general')) + return redirect(url_for('main.settings')) @main_bp.route('/dynamic-colors.css') def dynamic_colors(): diff --git a/routes/trash.py b/routes/trash.py index ee8726e..3148252 100644 --- a/routes/trash.py +++ b/routes/trash.py @@ -1,6 +1,6 @@ from flask import Blueprint, jsonify, request, abort from flask_login import login_required, current_user -from models import db, Room, RoomFile, TrashedFile +from models import db, Room, RoomFile, TrashedFile, UserStarredFile from utils import user_has_permission, clean_path import os from datetime import datetime @@ -99,29 +99,34 @@ def permanently_delete_file(room_id, trash_id): @trash_bp.route('//trash/empty', methods=['POST']) @login_required def empty_trash(room_id): - room = Room.query.get_or_404(room_id) - if not user_has_permission(room, 'can_delete'): - abort(403) - - # Get all trashed files for this room - trashed_files = TrashedFile.query.filter_by(room_id=room_id).all() - room_files = RoomFile.query.filter_by(room_id=room_id, deleted=True).all() - - # Delete physical files - room_dir = os.path.join('/data/rooms', str(room_id)) - for file in trashed_files + room_files: - try: - if file.type == 'file': - file_path = os.path.join(room_dir, file.original_path if hasattr(file, 'original_path') else file.path, file.name) + """Empty the trash for a specific room.""" + try: + # Get all trashed files + trashed_files = TrashedFile.query.filter_by(room_id=room_id).all() + + # Delete physical files first + for file in trashed_files: + try: + file_path = os.path.join(current_app.config['UPLOAD_FOLDER'], str(room_id), file.name) if os.path.exists(file_path): os.remove(file_path) - except Exception as e: - print(f"Error deleting physical file {file.name}: {str(e)}") - continue - - # Delete all trashed files from both tables - TrashedFile.query.filter_by(room_id=room_id).delete() - RoomFile.query.filter_by(room_id=room_id, deleted=True).delete() - db.session.commit() - - return jsonify({'success': True}) \ No newline at end of file + except Exception as e: + print(f"Error deleting physical file {file.name}: {str(e)}") + continue + + # Delete all starred file references for the trashed files + room_files = RoomFile.query.filter_by(room_id=room_id, deleted=True).all() + for file in room_files: + UserStarredFile.query.filter_by(file_id=file.id).delete() + + # Delete all trashed files from both tables + TrashedFile.query.filter_by(room_id=room_id).delete() + RoomFile.query.filter_by(room_id=room_id, deleted=True).delete() + + db.session.commit() + + return jsonify({'success': True}) + except Exception as e: + db.session.rollback() + print(f"Error emptying trash: {str(e)}") + return jsonify({'success': False, 'error': str(e)}), 500 \ No newline at end of file diff --git a/static/js/rooms/fileManager.js b/static/js/rooms/fileManager.js index 039c3ac..823a39d 100644 --- a/static/js/rooms/fileManager.js +++ b/static/js/rooms/fileManager.js @@ -392,7 +392,7 @@ export class FileManager { */ async downloadFile(filename, path = '') { console.log('[FileManager] Downloading file:', { filename, path }); - const url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(filename)}`; + let url = `/api/rooms/${this.roomManager.roomId}/files/${encodeURIComponent(filename)}`; if (path) { url += `?path=${encodeURIComponent(path)}`; } diff --git a/static/js/settings.js b/static/js/settings.js index 9a6b970..07ac64e 100644 --- a/static/js/settings.js +++ b/static/js/settings.js @@ -12,6 +12,7 @@ document.addEventListener('DOMContentLoaded', function() { const primaryColorInput = document.getElementById('primaryColor'); const secondaryColorInput = document.getElementById('secondaryColor'); const colorSettingsForm = document.getElementById('colorSettingsForm'); + const companyInfoForm = document.getElementById('companyInfoForm'); // Get all tab buttons const settingsTabs = document.querySelectorAll('[data-bs-toggle="tab"]'); @@ -73,10 +74,14 @@ document.addEventListener('DOMContentLoaded', function() { eventsTableBody.innerHTML = 'Loading events...'; } + // Get CSRF token + const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); + // Fetch events data fetch(`/settings/events?event_type=${eventType}&date_range=${dateRange}&user_id=${userId}&page=${page}`, { headers: { - 'X-Requested-With': 'XMLHttpRequest' + 'X-Requested-With': 'XMLHttpRequest', + 'X-CSRF-Token': csrfToken } }) .then(response => response.text()) @@ -303,11 +308,55 @@ document.addEventListener('DOMContentLoaded', function() { // Handle form submission if (colorSettingsForm) { colorSettingsForm.addEventListener('submit', function(e) { - console.log('[Settings] Form submitted with values:', { - primary: primaryColorInput.value, - secondary: secondaryColorInput.value, - csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'), - timestamp: new Date().toISOString() + e.preventDefault(); // Prevent default form submission + + const formData = new FormData(colorSettingsForm); + const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); + + fetch(colorSettingsForm.action, { + method: 'POST', + headers: { + 'X-CSRF-Token': csrfToken + }, + body: formData + }) + .then(response => { + if (response.ok) { + window.location.reload(); // Reload to show updated colors + } else { + throw new Error('Failed to update colors'); + } + }) + .catch(error => { + console.error('Error updating colors:', error); + alert('Failed to update colors. Please try again.'); + }); + }); + } + + // Handle company info form submission + if (companyInfoForm) { + companyInfoForm.addEventListener('submit', function(e) { + e.preventDefault(); + + const formData = new FormData(companyInfoForm); + const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content'); + formData.append('csrf_token', csrfToken); + + fetch(companyInfoForm.action, { + method: 'POST', + body: formData + }) + .then(response => { + if (response.ok) { + window.location.reload(); // Reload to show updated info + } else { + throw new Error('Failed to update company info'); + } + }) + .catch(error => { + console.error('Error updating company info:', error); + alert('Failed to update company info. Please try again.'); }); }); } diff --git a/templates/settings/settings.html b/templates/settings/settings.html index 55034ef..9662c52 100644 --- a/templates/settings/settings.html +++ b/templates/settings/settings.html @@ -63,7 +63,7 @@
- {{ company_info_tab(site_settings, csrf_token) }} + {{ company_info_tab(site_settings, form) }}
diff --git a/templates/settings/tabs/company_info.html b/templates/settings/tabs/company_info.html index 448211f..c468131 100644 --- a/templates/settings/tabs/company_info.html +++ b/templates/settings/tabs/company_info.html @@ -1,11 +1,11 @@ -{% macro company_info_tab(site_settings, csrf_token) %} +{% macro company_info_tab(site_settings, form) %}
-
- + + {{ form.csrf_token }}
@@ -20,50 +20,26 @@ class="img-thumbnail" style="max-height: 100px; max-width: 200px;"> {% endif %} - + {{ form.company_logo(class="form-control", accept="image/*") }}
Upload your company logo. Recommended size: 200x100 pixels.
- + {{ form.company_name(class="form-control", placeholder="Enter your company name") }}
This name will be displayed in the website title and navigation bar.
- + {{ form.company_website(class="form-control", placeholder="https://example.com") }}
- + {{ form.company_email(class="form-control", placeholder="contact@company.com") }}
- + {{ form.company_phone(class="form-control", placeholder="+1 (555) 123-4567") }}
@@ -72,50 +48,25 @@
Address Information
- + {{ form.company_address(class="form-control", placeholder="123 Business Street") }}
- + {{ form.company_city(class="form-control", placeholder="City") }}
- + {{ form.company_state(class="form-control", placeholder="State") }}
- + {{ form.company_zip(class="form-control", placeholder="ZIP") }}
- + {{ form.company_country(class="form-control", placeholder="Country") }}
@@ -124,20 +75,11 @@
Additional Information
- + {{ form.company_description(class="form-control", rows="3", placeholder="Brief description of your company") }}
- + {{ form.company_industry(class="form-control", placeholder="e.g., Technology, Healthcare, Finance") }}