diff --git a/Web/app.py b/Web/app.py index fa91a3a..2160936 100755 --- a/Web/app.py +++ b/Web/app.py @@ -3927,7 +3927,6 @@ def upload_admin(): """ Upload page route for inventory items. Accessible to users with insert permission. - Supports duplication by passing duplicate_from parameter. Returns: flask.Response: Rendered template or redirect @@ -3940,54 +3939,9 @@ def upload_admin(): flash('Ihnen ist es nicht gestattet auf dieser Internetanwendung, die eben besuchte Adrrese zu nutzen, versuchen sie es erneut nach dem sie sich mit einem berechtigten Nutzer angemeldet haben!', 'error') return redirect(url_for('login')) - # Check if this is a duplication request - duplicate_from = request.args.get('duplicate_from') - duplicate_flag = request.args.get('duplicate') # Check for sessionStorage-based duplication - duplicate_data = None - - # Handle the old method (duplicate_from parameter with item ID) - if duplicate_from: - try: - original_item = it.get_item(duplicate_from) - if original_item: - duplicate_data = { - 'name': original_item.get('Name', ''), - 'description': original_item.get('Beschreibung', ''), - 'location': original_item.get('Ort', ''), - 'room': original_item.get('Raum', ''), - 'category': original_item.get('Kategorie', ''), - 'year': original_item.get('Anschaffungsjahr', ''), - 'cost': original_item.get('Anschaffungskosten', ''), - 'filter1': original_item.get('Filter1', ''), - 'filter2': original_item.get('Filter2', ''), - 'filter3': original_item.get('Filter3', ''), - 'images': original_item.get('Images', []), - 'original_id': duplicate_from - } - # Copy all filter fields (Filter1_1 through Filter3_5) - for i in range(1, 4): # Filter1, Filter2, Filter3 - for j in range(1, 6): # _1 through _5 - filter_key = f'Filter{i}_{j}' - if filter_key in original_item: - duplicate_data[f'filter{i}_{j}'] = original_item[filter_key] - - flash('Element wird dupliziert. Bitte überprüfen Sie die Daten und passen Sie sie bei Bedarf an.', 'info') - else: - flash('Ursprungs-Element für Duplizierung nicht gefunden.', 'error') - except Exception as e: - app.logger.warning(f"Error loading item for duplication: {e}") - flash('Fehler beim Laden der Duplizierungsdaten.', 'error') - - # Handle the new method (sessionStorage-based duplication) - elif duplicate_flag == 'true': - # No server-side processing needed - JavaScript will handle sessionStorage data - # Just indicate that duplication mode is active - flash('Element wird dupliziert. Die Daten werden aus dem Session-Speicher geladen.', 'info') - return render_template( 'upload_admin.html', username=session['username'], - duplicate_data=duplicate_data, library_module_enabled=cfg.MODULES.is_enabled('library'), student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'), show_library_features=False, @@ -4014,46 +3968,9 @@ def library_admin(): flash('Bibliotheks-Modul ist deaktiviert.', 'error') return redirect(url_for('home_admin')) - duplicate_from = request.args.get('duplicate_from') - duplicate_flag = request.args.get('duplicate') - duplicate_data = None - - if duplicate_from: - try: - original_item = it.get_item(duplicate_from) - if original_item: - duplicate_data = { - 'name': original_item.get('Name', ''), - 'description': original_item.get('Beschreibung', ''), - 'location': original_item.get('Ort', ''), - 'room': original_item.get('Raum', ''), - 'category': original_item.get('Kategorie', ''), - 'year': original_item.get('Anschaffungsjahr', ''), - 'cost': original_item.get('Anschaffungskosten', ''), - 'filter1': original_item.get('Filter1', ''), - 'filter2': original_item.get('Filter2', ''), - 'filter3': original_item.get('Filter3', ''), - 'images': original_item.get('Images', []), - 'original_id': duplicate_from - } - for i in range(1, 4): - for j in range(1, 6): - filter_key = f'Filter{i}_{j}' - if filter_key in original_item: - duplicate_data[f'filter{i}_{j}'] = original_item[filter_key] - flash('Buch wird dupliziert. Bitte überprüfen Sie die Daten und passen Sie sie bei Bedarf an.', 'info') - else: - flash('Ursprungs-Element für Duplizierung nicht gefunden.', 'error') - except Exception as e: - app.logger.warning(f"Error loading item for duplication: {e}") - flash('Fehler beim Laden der Duplizierungsdaten.', 'error') - elif duplicate_flag == 'true': - flash('Buch wird dupliziert. Die Daten werden aus dem Session-Speicher geladen.', 'info') - return render_template( 'upload_admin.html', username=session['username'], - duplicate_data=duplicate_data, library_module_enabled=cfg.MODULES.is_enabled('library'), student_cards_module_enabled=cfg.MODULES.is_enabled('student_cards'), show_library_features=True, @@ -5388,18 +5305,18 @@ def upload_item(): individual_codes = [] if individual_codes_raw: individual_codes = [c.strip() for c in str(individual_codes_raw).replace(',', '\n').splitlines() if c.strip()] - + # Check if this is a duplication is_duplicating = request.form.get('is_duplicating') == 'true' - + # Get duplicate_images if duplicating duplicate_images = request.form.getlist('duplicate_images') if is_duplicating else [] - + # Make sure duplicate_images is always a list, even if there's only one if is_duplicating and duplicate_images and not isinstance(duplicate_images, list): duplicate_images = [duplicate_images] - + # Log details about each image if is_duplicating and duplicate_images: for i, img in enumerate(duplicate_images): @@ -5819,121 +5736,6 @@ def upload_item(): flash(error_msg, 'error') return redirect(url_for(success_redirect_endpoint)) - -@app.route('/duplicate_item', methods=['POST']) -def duplicate_item(): - """ - Route for duplicating an existing item. - Returns JSON response with success status. - Enhanced for mobile browser compatibility. - - Returns: - flask.Response: JSON response with success status and data - """ - try: - # Check authentication - if 'username' not in session: - return jsonify({'success': False, 'message': 'Nicht angemeldet'}), 401 - - # Check if user is admin - username = session['username'] - current_permissions = us.get_effective_permissions(session['username']) - - if not current_permissions['actions'].get('can_edit', False): - flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error') - return redirect(url_for('home_admin')) - - # Detect if request is from mobile device - is_mobile = 'Mobile' in request.headers.get('User-Agent', '') - is_ios = 'iPhone' in request.headers.get('User-Agent', '') or 'iPad' in request.headers.get('User-Agent', '') - - # Log mobile duplication for debugging - if is_mobile: - app.logger.info(f"Mobile duplication from {request.headers.get('User-Agent', 'unknown')} by {encrypt_text(username)}") - - # Get original item ID - original_item_id = request.form.get('original_item_id') - if not original_item_id: - return jsonify({'success': False, 'message': 'Ursprungs-Element-ID fehlt'}), 400 - - # Fetch original item data - original_item = it.get_item(original_item_id) - if not original_item: - return jsonify({'success': False, 'message': 'Ursprungs-Element nicht gefunden'}), 404 - - # Process filters as arrays (same as stored in database) - filter1_array = original_item.get('Filter', []) - filter2_array = original_item.get('Filter2', []) - filter3_array = original_item.get('Filter3', []) - - # Ensure filters are arrays - if not isinstance(filter1_array, list): - filter1_array = [filter1_array] if filter1_array else [] - if not isinstance(filter2_array, list): - filter2_array = [filter2_array] if filter2_array else [] - if not isinstance(filter3_array, list): - filter3_array = [filter3_array] if filter3_array else [] - - # Verify image paths for mobile devices to avoid issues with non-existent images - images = original_item.get('Images', []) - verified_images = [] - - if is_mobile: - for img in images: - img_path = os.path.join(app.config['UPLOAD_FOLDER'], img) - if os.path.exists(img_path) and os.path.isfile(img_path): - verified_images.append(img) - else: - app.logger.warning(f"Image not found for duplication: {img}") - - # If we lost images in verification, log it - if len(verified_images) < len(images): - app.logger.warning(f"Only {len(verified_images)} of {len(images)} images verified for mobile duplication") - else: - verified_images = images - - # For iOS devices, add more diagnostics and reduce data size if needed - if is_ios: - # Check if images exist (we now use main images as thumbnails) - images_exist = [] - for img in verified_images[:5]: # Only check first 5 to save time - img_path = os.path.join(app.config['UPLOAD_FOLDER'], img) - if os.path.exists(img_path): - images_exist.append(True) - else: - images_exist.append(False) - - # Log detailed diagnostics - app.logger.info(f"iOS duplication details: {len(verified_images)} images, " - f"images available: {all(images_exist)}, " - f"filter sizes: {len(filter1_array)}, {len(filter2_array)}, {len(filter3_array)}") - - return jsonify({ - 'success': True, - 'message': 'Duplizierungsdaten erfolgreich vorbereitet', - 'item_data': { - 'name': original_item.get('Name', ''), - 'description': original_item.get('Beschreibung', ''), - 'location': original_item.get('Ort', ''), - 'room': original_item.get('Raum', ''), - 'category': original_item.get('Kategorie', ''), - 'year': original_item.get('Anschaffungsjahr', ''), - 'cost': original_item.get('Anschaffungskosten', ''), - 'filter1': filter1_array, - 'filter2': filter2_array, - 'filter3': filter3_array, - 'images': verified_images, # Using verified images instead of original - 'isMobile': is_mobile, - 'isIOS': is_ios - } - }) - - except Exception as e: - print(f"Error in duplicate_item: {e}") - - return jsonify({'success': False, 'message': 'Serverfehler beim Duplizieren'}), 500 - - def _soft_delete_item_groups(db, root_item_ids, username): """Soft-delete one or more item groups and their borrow records.""" now = datetime.datetime.now() diff --git a/Web/templates/main_admin.html b/Web/templates/main_admin.html index 534ac8a..c6aae7a 100755 --- a/Web/templates/main_admin.html +++ b/Web/templates/main_admin.html @@ -819,7 +819,7 @@ flex: 0 0 auto; } - .ausleihen, .edit-button, .delete-button, .details-button, .duplicate-button, .schedule-button, .damage-button { + .ausleihen, .edit-button, .delete-button, .details-button, .schedule-button, .damage-button { padding: 10px 18px; border: none; border-radius: 5px; @@ -942,27 +942,6 @@ box-shadow: 0 2px 4px rgba(23, 162, 184, 0.3); } - /* Duplicate Button */ - .duplicate-button { - background-color: #6f42c1; - color: white; - border: 2px solid #6f42c1; - } - - .duplicate-button:hover { - background-color: #5a32a3; - border-color: #4e2a8e; - transform: translateY(-1px); - box-shadow: 0 4px 8px rgba(111, 66, 193, 0.3); - color: white; - text-decoration: none; - } - - .duplicate-button:active { - transform: translateY(0); - box-shadow: 0 2px 4px rgba(111, 66, 193, 0.3); - } - .damage-button { background-color: #fd7e14; color: white; @@ -1645,7 +1624,7 @@ gap: 10px !important; } - .ausleihen, .edit-button, .delete-button, .details-button, .duplicate-button, .schedule-button { + .ausleihen, .edit-button, .delete-button, .details-button, .schedule-button { width: 100% !important; min-height: 44px !important; padding: 12px 20px !important; @@ -1777,7 +1756,7 @@ justify-content: flex-start; } - .ausleihen, .edit-button, .delete-button, .details-button, .duplicate-button, .schedule-button { + .ausleihen, .edit-button, .delete-button, .details-button, .schedule-button { padding: 10px 20px; font-size: 0.9rem; min-width: 110px; @@ -1790,7 +1769,7 @@ gap: 8px; } - .ausleihen, .edit-button, .delete-button, .details-button, .duplicate-button, .schedule-button { + .ausleihen, .edit-button, .delete-button, .details-button, .schedule-button { padding: 8px 16px; font-size: 0.85rem; min-width: 95px; @@ -1808,7 +1787,7 @@ gap: 15px; } - .ausleihen, .edit-button, .delete-button, .details-button, .duplicate-button, .schedule-button { + .ausleihen, .edit-button, .delete-button, .details-button, .schedule-button { padding: 12px 24px; font-size: 1rem; min-width: 120px; @@ -1980,7 +1959,7 @@ } /* Standardize form and document buttons */ - .save-button, .cancel-button, .remove-book-cover-button, .remove-duplicate-image-button, + .save-button, .cancel-button, .remove-book-cover-button, .import-book-button, .fetch-isbn-button, .nav-back-button { display: inline-flex; align-items: center; @@ -3465,9 +3444,6 @@ document.addEventListener('DOMContentLoaded', ()=>{ {% if current_permissions.actions.get('can_edit', False) %} {% endif %} - {% if current_permissions.actions.get('can_insert', False) %} - - {% endif %} {% if current_permissions.pages.get('admin_school_settings', False) %} ${canScheduleItem ? `` : ''} {% endif %} @@ -4244,7 +4220,6 @@ document.addEventListener('DOMContentLoaded', ()=>{ : `` } - ${damageReports && damageReports.length > 0 ? `` : ``} ${canScheduleItem ? `` : ''}