From e157eb4e0f8f96f573c409f4bf6203f3672cd06c Mon Sep 17 00:00:00 2001 From: admin Date: Thu, 1 May 2025 00:24:26 +0800 Subject: [PATCH] Initial commit --- .vscode/launch.json | 15 + Dockerfile | 35 + Procfile | 1 + app.py | 816 +++++++++++++++++++ dms.db | Bin 0 -> 36864 bytes edit_document_route.py | 47 ++ files_route.py | 61 ++ requirements.txt | 7 + static/scripts.js | 64 ++ static/styles.css | 70 ++ templates/admin.html | 123 +++ templates/dashboard.html | 194 +++++ templates/edit_document.html | 77 ++ templates/error.html | 49 ++ templates/files.html | 145 ++++ templates/index.html | 17 + templates/layout.html | 92 +++ templates/login.html | 33 + templates/share_document.html | 163 ++++ templates/shared_view.html | 140 ++++ templates/upload.html | 98 +++ templates/view_document.html | 160 ++++ uploads/8402731ae1994135823f9a7076255c78.pdf | 74 ++ uploads/930d548939884089a2ef119b8e181a3f.pdf | Bin 0 -> 51396 bytes uploads/990c4a4b1a004a7090e221e401f5b4eb.pdf | Bin 0 -> 51396 bytes 25 files changed, 2481 insertions(+) create mode 100644 .vscode/launch.json create mode 100644 Dockerfile create mode 100644 Procfile create mode 100644 app.py create mode 100644 dms.db create mode 100644 edit_document_route.py create mode 100644 files_route.py create mode 100644 requirements.txt create mode 100644 static/scripts.js create mode 100644 static/styles.css create mode 100644 templates/admin.html create mode 100644 templates/dashboard.html create mode 100644 templates/edit_document.html create mode 100644 templates/error.html create mode 100644 templates/files.html create mode 100644 templates/index.html create mode 100644 templates/layout.html create mode 100644 templates/login.html create mode 100644 templates/share_document.html create mode 100644 templates/shared_view.html create mode 100644 templates/upload.html create mode 100644 templates/view_document.html create mode 100644 uploads/8402731ae1994135823f9a7076255c78.pdf create mode 100644 uploads/930d548939884089a2ef119b8e181a3f.pdf create mode 100644 uploads/990c4a4b1a004a7090e221e401f5b4eb.pdf diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8b27833 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..20f56ab --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Use the official Python image from the Docker Hub +FROM python:3.10-slim + +# Install dependencies (Including SQLite libraries and timezone data) +RUN apt-get update && \ + apt-get install -y \ + iputils-ping \ + curl \ + apt-transport-https \ + sqlite3 \ + libsqlite3-dev \ + tzdata && \ + rm -rf /var/lib/apt/lists/* + +# Set the timezone environment variable +ENV TZ="Asia/Manila" + +# Set the working directory in the container +WORKDIR /app + +# Copy the requirements.txt file into the container +COPY requirements.txt . + +# Install the dependencies +RUN pip install --upgrade pip && \ + pip install --no-cache-dir -r requirements.txt + +# Copy the rest of your application code into the container +COPY . . + +# Expose port 1000 to the outside world +EXPOSE 1000 + +# Define the command to run your application (app.py as primary file) +CMD ["python", "app.py"] diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..ca6e941 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: gunicorn app:app diff --git a/app.py b/app.py new file mode 100644 index 0000000..967e865 --- /dev/null +++ b/app.py @@ -0,0 +1,816 @@ +import os +import sqlite3 +import uuid +import hashlib +import datetime +from flask import Flask, render_template, request, redirect, url_for, flash, session, send_from_directory +from werkzeug.utils import secure_filename +from functools import wraps + +app = Flask(__name__) +app.secret_key = os.environ.get('SECRET_KEY', os.urandom(24)) + +# Configuration +UPLOAD_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'uploads') +ALLOWED_EXTENSIONS = {'pdf', 'docx', 'xlsx', 'jpg', 'png', 'mp4', 'txt'} +DB_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dms.db') + +# Create upload folder if it doesn't exist +if not os.path.exists(UPLOAD_FOLDER): + os.makedirs(UPLOAD_FOLDER) + +app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER +app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # 16MB max upload size + +# Production configuration +app.config['PREFERRED_URL_SCHEME'] = 'https' +app.config['SESSION_COOKIE_SECURE'] = False +app.config['SESSION_COOKIE_HTTPONLY'] = True +app.config['PERMANENT_SESSION_LIFETIME'] = datetime.timedelta(days=1) + +# Database initialization +def init_db(): + conn = sqlite3.connect(DB_PATH) + cursor = conn.cursor() + + # Create users table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT UNIQUE NOT NULL, + password TEXT NOT NULL, + email TEXT UNIQUE NOT NULL, + role TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + + # Create documents table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS documents ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + filename TEXT NOT NULL, + original_filename TEXT NOT NULL, + custom_filename TEXT NOT NULL, + file_type TEXT NOT NULL, + file_size INTEGER NOT NULL, + category TEXT NOT NULL, + user_id INTEGER NOT NULL, + visibility TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users (id) + ) + ''') + + # Create document_shares table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS document_shares ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + document_id INTEGER NOT NULL, + shared_by INTEGER NOT NULL, + shared_with INTEGER, + share_link TEXT, + qr_code TEXT, + expires_at TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (document_id) REFERENCES documents (id), + FOREIGN KEY (shared_by) REFERENCES users (id), + FOREIGN KEY (shared_with) REFERENCES users (id) + ) + ''') + + # Create document_versions table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS document_versions ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + document_id INTEGER NOT NULL, + filename TEXT NOT NULL, + version_number INTEGER NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (document_id) REFERENCES documents (id) + ) + ''') + + # Create analytics table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS analytics ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + document_id INTEGER NOT NULL, + action TEXT NOT NULL, + user_id INTEGER, + ip_address TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (document_id) REFERENCES documents (id), + FOREIGN KEY (user_id) REFERENCES users (id) + ) + ''') + + # Insert default admin user if not exists + cursor.execute("SELECT * FROM users WHERE username = 'admin'") + if not cursor.fetchone(): + # Password: admin123 + hashed_password = hashlib.sha256("admin123".encode()).hexdigest() + cursor.execute("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, ?)", + ("admin", hashed_password, "admin@example.com", "admin")) + + # Insert default regular user if not exists + cursor.execute("SELECT * FROM users WHERE username = 'user'") + if not cursor.fetchone(): + # Password: user123 + hashed_password = hashlib.sha256("user123".encode()).hexdigest() + cursor.execute("INSERT INTO users (username, password, email, role) VALUES (?, ?, ?, ?)", + ("user", hashed_password, "user@example.com", "user")) + + conn.commit() + conn.close() + +# Helper functions +def allowed_file(filename): + return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS + +def get_file_type(filename): + ext = filename.rsplit('.', 1)[1].lower() + if ext in ['pdf']: + return 'pdf' + elif ext in ['docx', 'doc']: + return 'document' + elif ext in ['xlsx', 'xls']: + return 'spreadsheet' + elif ext in ['jpg', 'jpeg', 'png', 'gif']: + return 'image' + elif ext in ['mp4', 'avi', 'mov']: + return 'video' + else: + return 'other' + +def get_db_connection(): + conn = sqlite3.connect(DB_PATH) + conn.row_factory = sqlite3.Row + return conn + +# Authentication decorator +def login_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if 'user_id' not in session: + flash('Please log in to access this page', 'error') + return redirect(url_for('login')) + return f(*args, **kwargs) + return decorated_function + +def admin_required(f): + @wraps(f) + def decorated_function(*args, **kwargs): + if 'user_id' not in session: + flash('Please log in to access this page', 'error') + return redirect(url_for('login')) + + conn = get_db_connection() + user = conn.execute('SELECT * FROM users WHERE id = ?', (session['user_id'],)).fetchone() + conn.close() + + if user['role'] != 'admin': + flash('Admin access required', 'error') + return redirect(url_for('dashboard')) + + return f(*args, **kwargs) + return decorated_function + +# Routes +@app.route('/') +def index(): + if 'user_id' in session: + return redirect(url_for('dashboard')) + return render_template('index.html') + +@app.route('/login', methods=['GET', 'POST']) +def login(): + if request.method == 'POST': + username = request.form['username'] + password = request.form['password'] + hashed_password = hashlib.sha256(password.encode()).hexdigest() + + conn = get_db_connection() + user = conn.execute('SELECT * FROM users WHERE username = ? AND password = ?', + (username, hashed_password)).fetchone() + conn.close() + + if user: + session['user_id'] = user['id'] + session['username'] = user['username'] + session['role'] = user['role'] + flash(f'Welcome back, {username}!', 'success') + return redirect(url_for('dashboard')) + else: + flash('Invalid username or password', 'error') + return render_template('login.html') + else: + return render_template('login.html') + +@app.route('/logout') +def logout(): + session.clear() + flash('You have been logged out', 'info') + return redirect(url_for('index')) + +@app.route('/dashboard') +@login_required +def dashboard(): + conn = get_db_connection() + + # Get document counts by category + category_counts = conn.execute(''' + SELECT category, COUNT(*) as count + FROM documents + WHERE user_id = ? OR visibility = "public" + OR id IN (SELECT document_id FROM document_shares WHERE shared_with = ?) + GROUP BY category + ''', (session['user_id'], session['user_id'])).fetchall() + + # Get recent documents (limited to 5) + recent_documents = conn.execute(''' + SELECT * FROM documents + WHERE user_id = ? OR visibility = "public" + OR id IN (SELECT document_id FROM document_shares WHERE shared_with = ?) + ORDER BY created_at DESC + LIMIT 5 + ''', (session['user_id'], session['user_id'])).fetchall() + + # Get analytics data for admin + if session['role'] == 'admin': + analytics = conn.execute(''' + SELECT documents.custom_filename, COUNT(analytics.id) as view_count + FROM documents + LEFT JOIN analytics ON documents.id = analytics.document_id + WHERE analytics.action = 'view' + GROUP BY documents.id + ORDER BY view_count DESC + LIMIT 5 + ''').fetchall() + + # Get user activity + user_activity = conn.execute(''' + SELECT users.username, COUNT(analytics.id) as action_count + FROM analytics + JOIN users ON analytics.user_id = users.id + GROUP BY analytics.user_id + ORDER BY action_count DESC + LIMIT 5 + ''').fetchall() + else: + analytics = None + user_activity = None + + conn.close() + + return render_template('dashboard.html', + recent_documents=recent_documents, + category_counts=category_counts, + analytics=analytics, + user_activity=user_activity) + +@app.route('/upload', methods=['GET', 'POST']) +@login_required +def upload(): + if request.method == 'POST': + # Check if the post request has the file part + if 'file' not in request.files: + flash('No file part', 'error') + return redirect(request.url) + + file = request.files['file'] + visibility = request.form.get('visibility', 'private') + custom_filename = request.form.get('custom_filename', '').strip() + category = request.form.get('category', 'general') + + # If user does not select file, browser also + # submit an empty part without filename + if file.filename == '': + flash('No selected file', 'error') + return redirect(request.url) + + if file and allowed_file(file.filename): + # Generate unique filename + original_filename = secure_filename(file.filename) + file_extension = original_filename.rsplit('.', 1)[1].lower() + unique_filename = f"{uuid.uuid4().hex}.{file_extension}" + + # Use custom filename if provided, otherwise use original + if not custom_filename: + custom_filename = os.path.splitext(original_filename)[0] + + # Save the file + file_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename) + file.save(file_path) + + # Get file size + file_size = os.path.getsize(file_path) + + # Save to database + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute(''' + INSERT INTO documents (filename, original_filename, custom_filename, file_type, file_size, category, user_id, visibility) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + ''', (unique_filename, original_filename, custom_filename, get_file_type(original_filename), file_size, category, session['user_id'], visibility)) + + document_id = cursor.lastrowid + + # Add initial version + cursor.execute(''' + INSERT INTO document_versions (document_id, filename, version_number) + VALUES (?, ?, ?) + ''', (document_id, unique_filename, 1)) + + conn.commit() + conn.close() + + flash('File successfully uploaded', 'success') + return redirect(url_for('files')) + else: + flash(f'Allowed file types are: {", ".join(ALLOWED_EXTENSIONS)}', 'error') + + # Get available categories + categories = ['admin', 'accounting', 'hr', 'marketing', 'legal', 'general', 'other'] + + return render_template('upload.html', categories=categories) + +@app.route('/files') +@login_required +def files(): + # Get query parameters + search_query = request.args.get('search', '') + selected_category = request.args.get('category', 'all') + page = request.args.get('page', 1, type=int) + per_page = 10 # Number of documents per page + + conn = get_db_connection() + + # Base query + query = ''' + SELECT * FROM documents + WHERE (user_id = ? OR visibility = "public" + OR id IN (SELECT document_id FROM document_shares WHERE shared_with = ?)) + ''' + params = [session['user_id'], session['user_id']] + + # Add search condition if search query provided + if search_query: + query += ' AND (custom_filename LIKE ? OR original_filename LIKE ?)' + params.extend(['%' + search_query + '%', '%' + search_query + '%']) + + # Add category filter if specific category selected + if selected_category != 'all': + query += ' AND category = ?' + params.append(selected_category) + + # Count total matching documents + count_query = query.replace('SELECT *', 'SELECT COUNT(*)') + total_docs = conn.execute(count_query, params).fetchone()[0] + + # Add pagination + query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?' + offset = (page - 1) * per_page + params.extend([per_page, offset]) + + # Execute final query + documents = conn.execute(query, params).fetchall() + + # Get available categories for filter dropdown + categories = ['admin', 'accounting', 'hr', 'marketing', 'legal', 'general', 'other'] + + # Calculate pagination info + total_pages = (total_docs + per_page - 1) // per_page # Ceiling division + pagination = { + 'page': page, + 'per_page': per_page, + 'total': total_docs, + 'pages': total_pages + } + + conn.close() + + return render_template('files.html', + documents=documents, + categories=categories, + selected_category=selected_category, + search_query=search_query, + pagination=pagination) + +@app.route('/document//edit', methods=['GET', 'POST']) +@login_required +def edit_document(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('files')) + + # Check ownership + if document['user_id'] != session['user_id'] and session['role'] != 'admin': + conn.close() + flash('You do not have permission to edit this document', 'error') + return redirect(url_for('files')) + + # Get available categories + categories = ['admin', 'accounting', 'hr', 'marketing', 'legal', 'general', 'other'] + + if request.method == 'POST': + custom_filename = request.form.get('custom_filename', '').strip() + category = request.form.get('category', 'general') + visibility = request.form.get('visibility', 'private') + + # Use custom filename if provided, otherwise keep existing + if not custom_filename: + custom_filename = document['custom_filename'] + + # Update document + conn.execute(''' + UPDATE documents + SET custom_filename = ?, category = ?, visibility = ?, updated_at = CURRENT_TIMESTAMP + WHERE id = ? + ''', (custom_filename, category, visibility, document_id)) + + conn.commit() + conn.close() + + flash('Document updated successfully', 'success') + return redirect(url_for('view_document', document_id=document_id)) + + conn.close() + + return render_template('edit_document.html', document=document, categories=categories) + +@app.route('/document/') +@login_required +def view_document(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('dashboard')) + + # Check permissions + if document['visibility'] != 'public' and document['user_id'] != session['user_id']: + # Check if shared with user + shared = conn.execute(''' + SELECT * FROM document_shares + WHERE document_id = ? AND shared_with = ? + ''', (document_id, session['user_id'])).fetchone() + + if not shared and session['role'] != 'admin': + conn.close() + flash('You do not have permission to view this document', 'error') + return redirect(url_for('dashboard')) + + # Record view in analytics + conn.execute(''' + INSERT INTO analytics (document_id, action, user_id, ip_address) + VALUES (?, ?, ?, ?) + ''', (document_id, 'view', session['user_id'], request.remote_addr)) + + # Get document versions + versions = conn.execute(''' + SELECT * FROM document_versions + WHERE document_id = ? + ORDER BY version_number DESC + ''', (document_id,)).fetchall() + + # Get share information + shares = conn.execute(''' + SELECT document_shares.*, users.username + FROM document_shares + LEFT JOIN users ON document_shares.shared_with = users.id + WHERE document_id = ? + ''', (document_id,)).fetchall() + + conn.commit() + conn.close() + + return render_template('view_document.html', document=document, versions=versions, shares=shares) + + +@app.route('/document//file') +@login_required +def serve_document_file(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('dashboard')) + + # Check permissions + if document['visibility'] != 'public' and document['user_id'] != session['user_id']: + shared = conn.execute(''' + SELECT * FROM document_shares + WHERE document_id = ? AND shared_with = ? + ''', (document_id, session['user_id'])).fetchone() + + if not shared and session['role'] != 'admin': + conn.close() + flash('You do not have permission to view this document', 'error') + return redirect(url_for('dashboard')) + + try: + # Serve the document without download (viewable in browser) + return send_from_directory(app.config['UPLOAD_FOLDER'], document['filename'], + as_attachment=False, download_name=document['original_filename']) + except FileNotFoundError: + flash('Document file not found', 'error') + conn.close() + return redirect(url_for('dashboard')) + finally: + conn.close() + + +@app.route('/document//download') +@login_required +def download_document(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('dashboard')) + + # Check permissions + if document['visibility'] != 'public' and document['user_id'] != session['user_id']: + # Check if shared with user + shared = conn.execute(''' + SELECT * FROM document_shares + WHERE document_id = ? AND shared_with = ? + ''', (document_id, session['user_id'])).fetchone() + + if not shared and session['role'] != 'admin': + conn.close() + flash('You do not have permission to download this document', 'error') + return redirect(url_for('dashboard')) + + # Record download in analytics + conn.execute(''' + INSERT INTO analytics (document_id, action, user_id, ip_address) + VALUES (?, ?, ?, ?) + ''', (document_id, 'download', session['user_id'], request.remote_addr)) + + conn.commit() + conn.close() + + return send_from_directory(app.config['UPLOAD_FOLDER'], document['filename'], + as_attachment=True, download_name=document['original_filename']) + +@app.route('/document//share', methods=['GET', 'POST']) +@login_required +def share_document(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('dashboard')) + + # Check ownership + if document['user_id'] != session['user_id'] and session['role'] != 'admin': + conn.close() + flash('You do not have permission to share this document', 'error') + return redirect(url_for('dashboard')) + + if request.method == 'POST': + share_type = request.form.get('share_type') + + if share_type == 'user': + # Share with specific user + username = request.form.get('username') + user = conn.execute('SELECT * FROM users WHERE username = ?', (username,)).fetchone() + + if not user: + flash(f'User {username} not found', 'error') + else: + # Check if already shared + existing = conn.execute(''' + SELECT * FROM document_shares + WHERE document_id = ? AND shared_with = ? + ''', (document_id, user['id'])).fetchone() + + if existing: + flash(f'Document already shared with {username}', 'info') + else: + conn.execute(''' + INSERT INTO document_shares (document_id, shared_by, shared_with) + VALUES (?, ?, ?) + ''', (document_id, session['user_id'], user['id'])) + flash(f'Document shared with {username}', 'success') + + elif share_type == 'link': + # Generate shareable link + expiry_days = int(request.form.get('expiry', 7)) + expires_at = datetime.datetime.now() + datetime.timedelta(days=expiry_days) + share_token = uuid.uuid4().hex + + conn.execute(''' + INSERT INTO document_shares (document_id, shared_by, share_link, expires_at) + VALUES (?, ?, ?, ?) + ''', (document_id, session['user_id'], share_token, expires_at)) + + flash(f'Shareable link created (expires in {expiry_days} days)', 'success') + + conn.commit() + + # Get users for sharing + users = conn.execute('SELECT * FROM users WHERE id != ?', (session['user_id'],)).fetchall() + + # Get existing shares + shares = conn.execute(''' + SELECT document_shares.*, users.username + FROM document_shares + LEFT JOIN users ON document_shares.shared_with = users.id + WHERE document_id = ? + ''', (document_id,)).fetchall() + + conn.close() + + return render_template('share_document.html', document=document, users=users, shares=shares) + +@app.route('/document//delete') +@login_required +def delete_document(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('dashboard')) + + # Check ownership + if document['user_id'] != session['user_id'] and session['role'] != 'admin': + conn.close() + flash('You do not have permission to delete this document', 'error') + return redirect(url_for('dashboard')) + + # Get all versions + versions = conn.execute('SELECT * FROM document_versions WHERE document_id = ?', (document_id,)).fetchall() + + # Delete files + for version in versions: + file_path = os.path.join(app.config['UPLOAD_FOLDER'], version['filename']) + if os.path.exists(file_path): + os.remove(file_path) + + # Delete from database (cascade will handle related records) + conn.execute('DELETE FROM analytics WHERE document_id = ?', (document_id,)) + conn.execute('DELETE FROM document_shares WHERE document_id = ?', (document_id,)) + conn.execute('DELETE FROM document_versions WHERE document_id = ?', (document_id,)) + conn.execute('DELETE FROM documents WHERE id = ?', (document_id,)) + + conn.commit() + conn.close() + + flash('Document deleted successfully', 'success') + return redirect(url_for('dashboard')) + +@app.route('/admin') +@admin_required +def admin_panel(): + conn = get_db_connection() + + # Get all users + users = conn.execute('SELECT * FROM users').fetchall() + + # Get document statistics + doc_stats = conn.execute(''' + SELECT + COUNT(*) as total_documents, + SUM(file_size) as total_size, + COUNT(DISTINCT user_id) as total_contributors + FROM documents + ''').fetchone() + + # Get activity statistics + activity_stats = conn.execute(''' + SELECT + COUNT(*) as total_actions, + COUNT(DISTINCT user_id) as active_users, + COUNT(DISTINCT document_id) as accessed_documents + FROM analytics + ''').fetchone() + + conn.close() + + return render_template('admin.html', users=users, doc_stats=doc_stats, activity_stats=activity_stats) + +@app.route('/shared/') +def access_shared(share_token): + conn = get_db_connection() + + # Get share information + share = conn.execute(''' + SELECT document_shares.*, documents.* + FROM document_shares + JOIN documents ON document_shares.document_id = documents.id + WHERE document_shares.share_link = ? + ''', (share_token,)).fetchone() + + if not share: + conn.close() + flash('Invalid or expired share link', 'error') + return redirect(url_for('index')) + + # Check if expired + if share['expires_at'] and datetime.datetime.strptime(share['expires_at'], '%Y-%m-%d %H:%M:%S.%f') < datetime.datetime.now(): + conn.close() + flash('This share link has expired', 'error') + return redirect(url_for('index')) + + # Record view in analytics + user_id = session.get('user_id') + conn.execute(''' + INSERT INTO analytics (document_id, action, user_id, ip_address) + VALUES (?, ?, ?, ?) + ''', (share['document_id'], 'shared_view', user_id, request.remote_addr)) + + conn.commit() + + # Get document versions + versions = conn.execute(''' + SELECT * FROM document_versions + WHERE document_id = ? + ORDER BY version_number DESC + ''', (share['document_id'],)).fetchall() + + conn.close() + + return render_template('shared_view.html', document=share, versions=versions, share_token=share_token) + +@app.route('/shared//download') +def download_shared(share_token): + conn = get_db_connection() + + # Get share information + share = conn.execute(''' + SELECT document_shares.*, documents.* + FROM document_shares + JOIN documents ON document_shares.document_id = documents.id + WHERE document_shares.share_link = ? + ''', (share_token,)).fetchone() + + if not share: + conn.close() + flash('Invalid or expired share link', 'error') + return redirect(url_for('index')) + + # Check if expired + if share['expires_at'] and datetime.datetime.strptime(share['expires_at'], '%Y-%m-%d %H:%M:%S.%f') < datetime.datetime.now(): + conn.close() + flash('This share link has expired', 'error') + return redirect(url_for('index')) + + # Record download in analytics + user_id = session.get('user_id') + conn.execute(''' + INSERT INTO analytics (document_id, action, user_id, ip_address) + VALUES (?, ?, ?, ?) + ''', (share['document_id'], 'shared_download', user_id, request.remote_addr)) + + conn.commit() + conn.close() + + return send_from_directory(app.config['UPLOAD_FOLDER'], share['filename'], + as_attachment=True, download_name=share['original_filename']) + +# Error handlers +@app.errorhandler(404) +def page_not_found(e): + return render_template('error.html', error_code=404, error_message="Page not found"), 404 + +@app.errorhandler(500) +def internal_server_error(e): + return render_template('error.html', error_code=500, error_message="Internal server error"), 500 + +# Initialize database and start app +if __name__ == '__main__': + init_db() + app.run(host='0.0.0.0', port=1000, debug=False) +else: + # For production WSGI servers + init_db() diff --git a/dms.db b/dms.db new file mode 100644 index 0000000000000000000000000000000000000000..7a1d629ed8b75cc32158881b085010f5ae97f5f3 GIT binary patch literal 36864 zcmeI5&2QVt6~HOdwj@^)w{^5?nmQ)XLuEv^XZRtK0&3WsDuGeQ@j7yMgB}Qe3>{)6 z(Gg`kNiR+6qKov}Q!fRI9(wAf|3I+V!vc#v^paCi^w8S^i(VF6^wc3GQIaFubkN=I zCT{>r(KE&fA+_mk6%kX_;L?V%}gxxQ0hZ`6gx`dgcIA&wTxvqC?3jP$E?pyV(MkSVZy(Vd!Eh04P?>2<3+jQC8+#II+9dFNTn@!_9LHi)+`mJ-t zLN!MB;r^)wMgoENh{SvGqEWWlC3}A7;b}%61f*k(aClL}p%-|T*Yvul>aaV6mf{%n z%+%PpRln0%zqKv=xPD`Oce5c}-`&}v7>zh(2<%|rd8vFiZtv7LZf-?&RqpS=qOeoH zQKuzcuip{ExIid-&SF$~i@%)BM6U8HJ8~71R?@?+X9w|0cFa}c=$CetfwP@W*0j5x z-%eOFLu}r@VLDES1VNN_nDDaoFvfGl^f9J`_#W3`p<|hoRK^oGoi44cq>g_+)^3eM zT4xkT+zHqhAUY*u?`X*XJN3`6*Qp$= z`Ck**o|*}p{@tkM2lwgOn-~GPe?ZzcIk7oC*78pzmct943eCCrWImNHl}f4O%aP8= z8>1g~EPvli`Jy-y<)~j|Lf^>Kh2`fM z{(brb0SEvAAOHk_01yBIKmZ5;0U!VbfWYfRAYe00@o|2sD1}{RQbkoFX%=!c8|#)R z+b$wB0%=&2a8)FxYCBcQbyXdUj%A6Wr4S8CqE@wJS+0gU*2n|1wcjMmw%-c3MIKKSej=nT6)5 zbofqxFBCa$B^He*wwcbRxKea~AYk+SA;T6{nZn-+tMm^75C8%|00;m9AOHk_01yBI zKmZ5;fe8d2U*U4h?$3TONBfgkBuST$qsW>r>6#`ZO*cj2V*1j&MzDrW$z9%e+#3g^ zX>4~qI$fZ#O{WOn^yy5(bb3}}$=LRfXvA9^12Lf|Ml&%Veb^*>W|Q6Tc!zXq-}nW3 zT|i<@L^XsbL_IFCp>@SMZXp|5rz2Z7Web}K$)<{QL_`r28DUqkWMU19O<-Mv1l^ul zEwl3;>3Z$GiI!pdYQHvaSrkQ4K4sofF3+>2&@$U}h>2CrmEEdL-|E*i>?o#cSt@Z& z8(m&Y}xu!@bED57}^Zz|#_f00e*l5C8&SErE9@6?c`((l7RfpXrl0cD#R2Z;N2% zDofL|;ivi}c9>4*9TaP_7WV)7UoafcF#I!qjc4cz1RwwefB+Bx0zd!=00AHX1b_e# z00OTdfz|05CbgTDxFlm2Ug%#VygJp(&n6jNFrI%f5@0R+PBz&PfD`${=l82@zkE92 zLR2Me60GYomK05tT%C?`P^+S%*s2zeZiw>_`u|wX^M7WFxfzB(=6}jR=gWov6h12a zu5hnVEoAb~^MA@e%5Ubc=KhuYAotsxo0F+11RwwefB+Bx0zd!=00AHX1isM(D)VV3 zRm}cSVh=rXgmrNlSG8rdjFA%e8FT^5HA$%<5q+OZ&xg{Ef7EXJrgK(~u$NM~l2pG; zA4^ugmt0!HmCJo;?i}02YE4!u-%Tnl)fBx_N-8bZB(d`5g`|tgr7@~px`;HIqtZ!h zh(0G4^~!hp`p@%#NE&_n0{XFDqo2!r<03h<8Wt;jLW^)HjS3ghuht~Bl258XlFmi? z7e;(uRA~#LZDgc{(5LEQ3&EAygwpgL0LJBPWC_(BdGKVtkt99Hl(WU`f^?RkQWMMH JN}w^a^uN4fvuOYT literal 0 HcmV?d00001 diff --git a/edit_document_route.py b/edit_document_route.py new file mode 100644 index 0000000..4d9ffe5 --- /dev/null +++ b/edit_document_route.py @@ -0,0 +1,47 @@ +@app.route('/document//edit', methods=['GET', 'POST']) +@login_required +def edit_document(document_id): + conn = get_db_connection() + + # Get document + document = conn.execute('SELECT * FROM documents WHERE id = ?', (document_id,)).fetchone() + + if not document: + conn.close() + flash('Document not found', 'error') + return redirect(url_for('files')) + + # Check ownership + if document['user_id'] != session['user_id'] and session['role'] != 'admin': + conn.close() + flash('You do not have permission to edit this document', 'error') + return redirect(url_for('files')) + + # Get available categories + categories = ['admin', 'accounting', 'hr', 'marketing', 'legal', 'general', 'other'] + + if request.method == 'POST': + custom_filename = request.form.get('custom_filename', '').strip() + category = request.form.get('category', 'general') + visibility = request.form.get('visibility', 'private') + + # Use custom filename if provided, otherwise keep existing + if not custom_filename: + custom_filename = document['custom_filename'] + + # Update document + conn.execute(''' + UPDATE documents + SET custom_filename = ?, category = ?, visibility = ?, updated_at = CURRENT_TIMESTAMP + WHERE id = ? + ''', (custom_filename, category, visibility, document_id)) + + conn.commit() + conn.close() + + flash('Document updated successfully', 'success') + return redirect(url_for('view_document', document_id=document_id)) + + conn.close() + + return render_template('edit_document.html', document=document, categories=categories) diff --git a/files_route.py b/files_route.py new file mode 100644 index 0000000..f5d243f --- /dev/null +++ b/files_route.py @@ -0,0 +1,61 @@ +@app.route('/files') +@login_required +def files(): + # Get query parameters + search_query = request.args.get('search', '') + selected_category = request.args.get('category', 'all') + page = request.args.get('page', 1, type=int) + per_page = 10 # Number of documents per page + + conn = get_db_connection() + + # Base query + query = ''' + SELECT * FROM documents + WHERE (user_id = ? OR visibility = "public" + OR id IN (SELECT document_id FROM document_shares WHERE shared_with = ?)) + ''' + params = [session['user_id'], session['user_id']] + + # Add search condition if search query provided + if search_query: + query += ' AND (custom_filename LIKE ? OR original_filename LIKE ?)' + params.extend(['%' + search_query + '%', '%' + search_query + '%']) + + # Add category filter if specific category selected + if selected_category != 'all': + query += ' AND category = ?' + params.append(selected_category) + + # Count total matching documents + count_query = query.replace('SELECT *', 'SELECT COUNT(*)') + total_docs = conn.execute(count_query, params).fetchone()[0] + + # Add pagination + query += ' ORDER BY created_at DESC LIMIT ? OFFSET ?' + offset = (page - 1) * per_page + params.extend([per_page, offset]) + + # Execute final query + documents = conn.execute(query, params).fetchall() + + # Get available categories for filter dropdown + categories = ['admin', 'accounting', 'hr', 'marketing', 'legal', 'general', 'other'] + + # Calculate pagination info + total_pages = (total_docs + per_page - 1) // per_page # Ceiling division + pagination = { + 'page': page, + 'per_page': per_page, + 'total': total_docs, + 'pages': total_pages + } + + conn.close() + + return render_template('files.html', + documents=documents, + categories=categories, + selected_category=selected_category, + search_query=search_query, + pagination=pagination) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c6410d7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +Flask==2.3.3 +Werkzeug==2.3.7 +Jinja2==3.1.2 +MarkupSafe==2.1.3 +itsdangerous==2.1.2 +click==8.1.7 +gunicorn==21.2.0 diff --git a/static/scripts.js b/static/scripts.js new file mode 100644 index 0000000..69c5fa4 --- /dev/null +++ b/static/scripts.js @@ -0,0 +1,64 @@ +// Document Management System JavaScript +document.addEventListener('DOMContentLoaded', function() { + // Auto-dismiss flash messages after 5 seconds + window.setTimeout(function() { + document.querySelectorAll('.alert').forEach(function(alert) { + if (bootstrap && bootstrap.Alert) { + var bsAlert = new bootstrap.Alert(alert); + bsAlert.close(); + } else { + alert.style.display = 'none'; + } + }); + }, 5000); + + // Copy link functionality + window.copyLink = function(elementId) { + var copyText = document.getElementById(elementId); + if (copyText) { + copyText.select(); + copyText.setSelectionRange(0, 99999); + navigator.clipboard.writeText(copyText.value); + + // Show a temporary tooltip + var tooltip = document.createElement("div"); + tooltip.innerHTML = "Copied!"; + tooltip.style.position = "fixed"; + tooltip.style.backgroundColor = "#4CAF50"; + tooltip.style.color = "white"; + tooltip.style.padding = "5px 10px"; + tooltip.style.borderRadius = "5px"; + tooltip.style.zIndex = "1000"; + tooltip.style.top = (event.clientY - 30) + "px"; + tooltip.style.left = event.clientX + "px"; + document.body.appendChild(tooltip); + + setTimeout(function() { + document.body.removeChild(tooltip); + }, 2000); + } + }; + + // Email sharing functionality + window.shareViaEmail = function(filename, url) { + var subject = "Shared Document: " + filename; + var body = "I'm sharing a document with you: " + filename + "\n\nYou can access it here: " + url; + window.location.href = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body); + }; + + // WhatsApp sharing functionality + window.shareViaWhatsApp = function(filename, url) { + var text = "I'm sharing a document with you: " + filename + "\n\nYou can access it here: " + url; + window.open("https://wa.me/?text=" + encodeURIComponent(text), "_blank"); + }; + + // Confirm delete + var deleteButtons = document.querySelectorAll('[data-confirm]'); + deleteButtons.forEach(function(button) { + button.addEventListener('click', function(e) { + if (!confirm(this.getAttribute('data-confirm'))) { + e.preventDefault(); + } + }); + }); +}); diff --git a/static/styles.css b/static/styles.css new file mode 100644 index 0000000..bd67a1c --- /dev/null +++ b/static/styles.css @@ -0,0 +1,70 @@ +/* Main Styles */ +body { + padding-top: 60px; + background-color: #f8f9fa; +} + +.card { + margin-bottom: 20px; + box-shadow: 0 4px 6px rgba(0,0,0,.1); +} + +.document-card { + transition: transform 0.2s; +} + +.document-card:hover { + transform: translateY(-5px); +} + +.file-icon { + font-size: 2.5rem; + margin-bottom: 10px; +} + +.pdf-icon { color: #e74c3c; } +.document-icon { color: #3498db; } +.spreadsheet-icon { color: #2ecc71; } +.image-icon { color: #9b59b6; } +.video-icon { color: #e67e22; } +.other-icon { color: #95a5a6; } + +.flash-messages { + position: fixed; + top: 70px; + right: 20px; + z-index: 1000; + max-width: 300px; +} + +/* Dashboard Styles */ +.analytics-card { + background-color: #f8f9fa; + border-left: 4px solid #007bff; +} + +/* Document Viewer Styles */ +.document-viewer { + min-height: 400px; + background-color: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 0.25rem; + padding: 1rem; +} + +/* Responsive Adjustments */ +@media (max-width: 768px) { + .card-body { + padding: 1rem; + } + + .btn-group { + display: flex; + flex-direction: column; + } + + .btn-group .btn { + margin-bottom: 0.25rem; + border-radius: 0.25rem !important; + } +} diff --git a/templates/admin.html b/templates/admin.html new file mode 100644 index 0000000..ec093d3 --- /dev/null +++ b/templates/admin.html @@ -0,0 +1,123 @@ +{% extends "layout.html" %} + +{% block content %} +

Admin Panel

+ +
+
+
+
+
Document Statistics
+
+
+
    +
  • + Total Documents + {{ doc_stats.total_documents }} +
  • +
  • + Total Storage Used + {{ (doc_stats.total_size / (1024*1024))|round(2) }} MB +
  • +
  • + Active Contributors + {{ doc_stats.total_contributors }} +
  • +
+
+
+
+ +
+
+
+
Activity Statistics
+
+
+
    +
  • + Total Actions + {{ activity_stats.total_actions }} +
  • +
  • + Active Users + {{ activity_stats.active_users }} +
  • +
  • + Accessed Documents + {{ activity_stats.accessed_documents }} +
  • +
+
+
+
+ +
+
+
+
System Actions
+
+
+
+ + + +
+
+
+
+
+ +
+
+
User Management
+
+
+
+ + + + + + + + + + + + + {% for user in users %} + + + + + + + + + {% endfor %} + +
IDUsernameEmailRoleCreatedActions
{{ user.id }}{{ user.username }}{{ user.email }} + + {{ user.role }} + + {{ user.created_at.split(' ')[0] }} +
+ + +
+
+
+
+
+{% endblock %} diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..cd07db6 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,194 @@ +{% extends "layout.html" %} + +{% block content %} +

Dashboard

+ +
+ +
+
+
+
Document Categories
+
+
+ {% if category_counts %} +
+ {% for category in category_counts %} +
+
+
+
+
+
{{ category.category|capitalize }}
+ {{ category.count }} document{% if category.count != 1 %}s{% endif %} +
+ + + +
+
+
+
+ {% endfor %} +
+ {% else %} +
+ No documents found. Upload your first document to get started! +
+ {% endif %} + +
+
+
+ + + +
+ +
+ + + + + {% if session.role == 'admin' and analytics %} +
+
+
+
Analytics Overview
+
+
+
Most Viewed Documents
+
+ + + + + + + + + {% for doc in analytics %} + + + + + {% endfor %} + +
DocumentViews
{{ doc.custom_filename }}{{ doc.view_count }}
+
+ + {% if user_activity %} +
Most Active Users
+
+ + + + + + + + + {% for user in user_activity %} + + + + + {% endfor %} + +
UserActions
{{ user.username }}{{ user.action_count }}
+
+ {% endif %} +
+
+
+ {% endif %} +
+{% endblock %} diff --git a/templates/edit_document.html b/templates/edit_document.html new file mode 100644 index 0000000..5c6d0a0 --- /dev/null +++ b/templates/edit_document.html @@ -0,0 +1,77 @@ +{% extends "layout.html" %} + +{% block content %} +

Edit Document

+ +
+
+
+
+
Edit Document Details
+
+
+
+
+ + +
+ +
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ +
+ + Cancel + + +
+
+
+
+
+ +
+
+
+
Document Information
+
+
+

Original Filename: {{ document.original_filename }}

+

File Type: {{ document.file_type|capitalize }}

+

File Size: {{ (document.file_size / 1024)|round(1) }} KB

+

Uploaded: {{ document.created_at.split(' ')[0] }}

+

Last Modified: {{ document.updated_at.split(' ')[0] }}

+
+
+
+
+{% endblock %} diff --git a/templates/error.html b/templates/error.html new file mode 100644 index 0000000..25e3660 --- /dev/null +++ b/templates/error.html @@ -0,0 +1,49 @@ + + + + + + Document Management System - Error + + + + + + + + + +
+
+
+
+
+

{{ error_code }}

+

{{ error_message }}

+

Sorry, something went wrong.

+ + Return to Home + +
+
+
+
+
+ + +
+
+

Document Management System

+
+
+ + + + + diff --git a/templates/files.html b/templates/files.html new file mode 100644 index 0000000..7cc28c5 --- /dev/null +++ b/templates/files.html @@ -0,0 +1,145 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+

Files

+
+ +
+ +
+
+
Document Library
+
+ +
+ + +
+
+
+
+
+ + + + + + + + + + + + + + {% if documents %} + {% for doc in documents %} + + + + + + + + + + {% endfor %} + {% else %} + + + + {% endif %} + +
NameCategoryTypeSizeUploadedVisibilityActions
+
+ {% if doc.file_type == 'pdf' %} + + {% elif doc.file_type == 'document' %} + + {% elif doc.file_type == 'spreadsheet' %} + + {% elif doc.file_type == 'image' %} + + {% elif doc.file_type == 'video' %} + + {% else %} + + {% endif %} + {{ doc.custom_filename }} +
+
{{ doc.category|capitalize }}{{ doc.file_type|capitalize }}{{ (doc.file_size / 1024)|round(1) }} KB{{ doc.created_at.split(' ')[0] }} + {% if doc.visibility == 'public' %} + Public + {% else %} + Private + {% endif %} + + +
+
+ + {% if search_query %} + No documents found matching your search criteria. + {% elif selected_category != 'all' %} + No documents found in the selected category. + {% else %} + No documents found. Upload your first document to get started! + {% endif %} +
+
+
+
+
+ +{% if pagination and pagination.pages > 1 %} + +{% endif %} +{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..fc52ca8 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,17 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+
+
+

Document Management System

+

A secure platform for managing, sharing, and collaborating on documents.

+
+

Please login to access your documents and start managing your files.

+ Login +
+
+
+
+{% endblock %} diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 0000000..d105f97 --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,92 @@ + + + + + + Document Management System + + + + + + + + + +
+ {% for category, message in get_flashed_messages(with_categories=true) %} + {% set alert_class = 'alert-info' %} + {% if category == 'success' %} + {% set alert_class = 'alert-success' %} + {% elif category == 'error' %} + {% set alert_class = 'alert-danger' %} + {% endif %} +
+ {{ message }} + +
+ {% endfor %} +
+ + +
+ {% block content %}{% endblock %} +
+ + +
+
+

Document Management System

+
+
+ + + + {% block scripts %}{% endblock %} + + diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..c9a1d8c --- /dev/null +++ b/templates/login.html @@ -0,0 +1,33 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+
+
+

Login

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+
+
+
+
+{% endblock %} diff --git a/templates/share_document.html b/templates/share_document.html new file mode 100644 index 0000000..c2e3c30 --- /dev/null +++ b/templates/share_document.html @@ -0,0 +1,163 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+

Share Document: {{ document.original_filename }}

+
+ +
+ +
+
+
+
+
Share with User
+
+
+
+ +
+ + +
+
+ +
+
+
+
+
+ +
+
+
+
Create Shareable Link
+
+
+
+ +
+ + +
+
+ +
+
+
+
+
+
+ +
+
+
Current Shares
+
+
+ {% if shares %} +
+ + + + + + + + + + + + {% for share in shares %} + + + + + + + + {% endfor %} + +
TypeShared WithDate SharedExpiryActions
+ {% if share.shared_with %} + User + {% elif share.share_link %} + Link + {% endif %} + + {% if share.shared_with %} + {{ share.username }} + {% elif share.share_link %} +
+ + +
+ {% endif %} +
{{ share.created_at.split(' ')[0] }} + {% if share.expires_at %} + {{ share.expires_at.split(' ')[0] }} + {% else %} + Never + {% endif %} + + + Revoke + +
+
+ {% else %} +
+ This document hasn't been shared yet. +
+ {% endif %} +
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/shared_view.html b/templates/shared_view.html new file mode 100644 index 0000000..12e3455 --- /dev/null +++ b/templates/shared_view.html @@ -0,0 +1,140 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+
+ You are viewing a shared document. + {% if not session.user_id %} + Login to access more features. + {% endif %} +
+
+
+ +
+
+
+
+
{{ document.original_filename }}
+
+
+
+ {% if document.file_type == 'pdf' %} +
+ +
+ {% elif document.file_type == 'image' %} + {{ document.original_filename }} + {% elif document.file_type == 'video' %} +
+ +
+ {% else %} +
+ Preview not available for this file type. Please download the file to view its contents. +
+ {% endif %} +
+ + +
+
+
+ +
+
+
+
Document Information
+
+
+
    +
  • + File Type + {{ document.file_type }} +
  • +
  • + Size + {{ (document.file_size / 1024)|round(1) }} KB +
  • +
  • + Uploaded + {{ document.created_at.split(' ')[0] }} +
  • +
+
+
+ +
+
+
Sharing Options
+
+
+

This document has been shared with you via a secure link.

+ +
+ + +
+ +
+ + +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/templates/upload.html b/templates/upload.html new file mode 100644 index 0000000..279678a --- /dev/null +++ b/templates/upload.html @@ -0,0 +1,98 @@ +{% extends "layout.html" %} + +{% block content %} +

Upload Document

+ +
+
+
+
+
Upload New Document
+
+
+
+
+ + +
+ Allowed file types: PDF, DOCX, XLSX, JPG, PNG, MP4, TXT +
+
+ +
+ + +
+ If left blank, the original filename will be used +
+
+ +
+ + +
+ +
+ +
+ + +
+
+ + +
+
+ +
+ +
+
+
+
+
+ +
+
+
+
Information
+
+
+
File Size Limits
+

Maximum file size: 16MB

+ +
Supported File Types
+
    +
  • Documents: PDF, DOCX, TXT
  • +
  • Spreadsheets: XLSX
  • +
  • Images: JPG, PNG
  • +
  • Videos: MP4
  • +
+ +
Categories
+
    +
  • Admin: Administrative documents
  • +
  • Accounting: Financial documents
  • +
  • HR: Human resources documents
  • +
  • Marketing: Marketing materials
  • +
  • Legal: Legal documents
  • +
  • General: General purpose documents
  • +
  • Other: Miscellaneous documents
  • +
+
+
+
+
+{% endblock %} diff --git a/templates/view_document.html b/templates/view_document.html new file mode 100644 index 0000000..e509065 --- /dev/null +++ b/templates/view_document.html @@ -0,0 +1,160 @@ +{% extends "layout.html" %} + +{% block content %} +
+
+

{{ document.original_filename }}

+
+ +
+ +
+
+
+
+
Document Viewer
+
+
+
+ {% if document.file_type == 'pdf' %} +
+ +
+ {% elif document.file_type == 'image' %} + {{ document.original_filename }} + {% elif document.file_type == 'video' %} +
+ +
+ {% else %} +
+ Preview not available for this file type. Please download the file to view its contents. +
+ {% endif %} +
+ +
+ + Download + + + Share + + {% if document.user_id == session.user_id or session.role == 'admin' %} + + Delete + + {% endif %} +
+
+
+ +
+
+
Version History
+
+
+
+ + + + + + + + + + {% for version in versions %} + + + + + + {% endfor %} + +
VersionDateActions
v{{ version.version_number }}{{ version.created_at }} + + Download + +
+
+
+
+
+ +
+
+
+
Document Information
+
+
+
    +
  • + File Type + {{ document.file_type }} +
  • +
  • + Size + {{ (document.file_size / 1024)|round(1) }} KB +
  • +
  • + Uploaded + {{ document.created_at.split(' ')[0] }} +
  • +
  • + Visibility + + {{ document.visibility }} + +
  • +
  • + Versions + {{ versions|length }} +
  • +
+
+
+ +
+
+
Sharing Information
+
+
+ {% if shares %} +
Shared With
+
    + {% for share in shares %} +
  • + {% if share.shared_with %} + {{ share.username }} + {% elif share.share_link %} + Via Link + + {% if share.expires_at %} + Expires: {{ share.expires_at.split(' ')[0] }} + {% endif %} + + {% endif %} +
  • + {% endfor %} +
+ {% else %} +

This document hasn't been shared yet.

+ {% endif %} + + + Manage Sharing + +
+
+
+
+{% endblock %} diff --git a/uploads/8402731ae1994135823f9a7076255c78.pdf b/uploads/8402731ae1994135823f9a7076255c78.pdf new file mode 100644 index 0000000..79c46d2 --- /dev/null +++ b/uploads/8402731ae1994135823f9a7076255c78.pdf @@ -0,0 +1,74 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document http://www.reportlab.com +1 0 obj +<< +/F1 2 0 R /F2 3 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/Contents 8 0 R /MediaBox [ 0 0 612 792 ] /Parent 7 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +5 0 obj +<< +/PageMode /UseNone /Pages 7 0 R /Type /Catalog +>> +endobj +6 0 obj +<< +/Author (\(anonymous\)) /CreationDate (D:20250419002510-06'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20250419002510-06'00') /Producer (ReportLab PDF Library - www.reportlab.com) + /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False +>> +endobj +7 0 obj +<< +/Count 1 /Kids [ 4 0 R ] /Type /Pages +>> +endobj +8 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 529 +>> +stream +Gat='gMYb"%"6H'SB&'?;G!(UO2I8!__@a?J&$#f$*'2bAXE&8YLFaE%h_F"gc[^)I0%X)Pc5hu_FE>V7;%?hdLCrlk.^1WtmrJhE7H7@Uo];&Ff-6nsG%#k0re&L-;G\@#YN=BTFDp9ifP=K.j6rY!a+WiH,?8*`<7RKoKQCQb9A(BZdV?r=iBJ"o'dC9KP">m\>811tXQ$SYV=Uu^knO09pVM#ZD&?k_>AG8SQa&"d2VRuiZY\8C2G'57>>o'CP=IOs]eQpANnCr;]'2E^joo27AAD=.,*N>RiD-=WOId&C`12"KYe?bV@?"Uao/':&Y&I]D@Rg.udZNXeAjg2S-&i6?/h62Q@A'Kn:Tendstream +endobj +xref +0 9 +0000000000 65535 f +0000000073 00000 n +0000000114 00000 n +0000000221 00000 n +0000000333 00000 n +0000000526 00000 n +0000000594 00000 n +0000000877 00000 n +0000000936 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (http://www.reportlab.com) + +/Info 6 0 R +/Root 5 0 R +/Size 9 +>> +startxref +1555 +%%EOF diff --git a/uploads/930d548939884089a2ef119b8e181a3f.pdf b/uploads/930d548939884089a2ef119b8e181a3f.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a37272d9ff17cb03d42447f6ee418ace24a78761 GIT binary patch literal 51396 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 literal 0 HcmV?d00001 diff --git a/uploads/990c4a4b1a004a7090e221e401f5b4eb.pdf b/uploads/990c4a4b1a004a7090e221e401f5b4eb.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a37272d9ff17cb03d42447f6ee418ace24a78761 GIT binary patch literal 51396 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 literal 0 HcmV?d00001