diff --git a/Web/app.py b/Web/app.py index 2a7c21b..24581f8 100755 --- a/Web/app.py +++ b/Web/app.py @@ -6083,10 +6083,10 @@ def bulk_delete_items(): def edit_item(id): """ Route for editing an existing inventory item. - + Args: id (str): ID of the item to edit - + Returns: flask.Response: Redirect to admin homepage with status message """ @@ -6097,19 +6097,19 @@ def edit_item(id): 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 (Löschen) auszuführen.', 'error') - return redirect(url_for('home_admin')) - + flash('Ihnen fehlen die nötigen Berechtigungen, um diese Aktion auszuführen.', 'error') + return redirect(url_for('home_admin')) + if not cfg.MODULES.is_enabled('inventory'): flash('Bibliotheks-Modul ist deaktiviert.', 'error') return redirect(url_for('library_view')) - - # Strip whitespace from all text fields + + fs = get_gridfs() + name = sanitize_form_value(request.form.get('name')) ort = sanitize_form_value(request.form.get('ort')) beschreibung = sanitize_form_value(request.form.get('beschreibung')) - - # Strip whitespace from all filter values + filter1 = sanitize_form_value(request.form.getlist('filter')) filter2 = sanitize_form_value(request.form.getlist('filter2')) filter3 = sanitize_form_value(request.form.getlist('filter3')) @@ -6117,7 +6117,7 @@ def edit_item(id): # Expand special "all values" selections for predefined filters. filter1 = expand_filter_selection(filter1, 1) filter2 = expand_filter_selection(filter2, 2) - + anschaffungs_jahr = sanitize_form_value(request.form.get('anschaffungsjahr')) anschaffungs_kosten = sanitize_form_value(request.form.get('anschaffungskosten')) code_4 = sanitize_form_value(request.form.get('code_4')) @@ -6133,91 +6133,103 @@ def edit_item(id): return redirect(url_for('home_admin')) if item_isbn: item_type = 'book' - - # Check if code is unique (excluding the current item) + if code_4 and not it.is_code_unique(code_4, exclude_id=id): flash('Der Code wird bereits verwendet. Bitte wählen Sie einen anderen Code.', 'error') return redirect(url_for('home_admin')) - - # Get current item to check availability status + current_item = it.get_item(id) if not current_item: flash('Element nicht gefunden', 'error') return redirect(url_for('home_admin')) - - # Preserve current availability status + verfuegbar = current_item.get('Verfuegbar', True) - - # Handle existing images - get list of images to keep + images_to_keep = request.form.getlist('existing_images') - - # Get the original list of images from the item + original_images = current_item.get('Images', []) - - # Keep only the images that weren't marked for deletion + images = [img for img in original_images if img in images_to_keep] - - # Handle new image uploads + new_images = request.files.getlist('new_images') - - # Process any new image uploads + for image in new_images: if image and image.filename: - is_allowed, error_message = allowed_file(image.filename) + is_allowed, error_message = allowed_file(image.filename, image) + if is_allowed: - # Get the file extension - _, ext_part = os.path.splitext(secure_filename(image.filename)) - - # Generate a completely unique filename using UUID - unique_id = str(uuid.uuid4()) - timestamp = time.strftime("%Y%m%d%H%M%S") - - # New filename format with UUID to ensure uniqueness - filename = f"{unique_id}_{timestamp}{ext_part}" - - image.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) - - # Optimize the image try: - opt_result = generate_optimized_versions(filename, max_original_width=500, target_size_kb=80) - if opt_result['success'] and opt_result['original']: - filename = opt_result['original'] + secure_name = secure_filename(image.filename) + + image.seek(0) + image_bytes = image.read() + + if not image_bytes: + app.logger.error(f"Failed to read image in edit_item (0 bytes) for {secure_name}") + continue + + optimized_io = io.BytesIO() + with Image.open(io.BytesIO(image_bytes)) as img: + if img.mode not in ('RGB', 'RGBA'): + img = img.convert('RGBA') + + max_width = 500 + if img.width > max_width: + ratio = max_width / img.width + new_size = (max_width, int(img.height * ratio)) + img = img.resize(new_size, Image.Resampling.LANCZOS) + + img.save(optimized_io, format='WEBP', quality=85, optimize=True) + + optimized_io.seek(0) + new_filename = f"{uuid.uuid4().hex}_{int(time.time())}.webp" + + fs.put( + optimized_io, + filename=new_filename, + content_type='image/webp', + metadata={ + 'original_filename': secure_name, + 'upload_context': 'edit_item', + 'item_id': id + } + ) + + images.append(new_filename) + except Exception as e: - app.logger.error(f"Error optimizing image in edit_item: {e}") - - images.append(filename) + app.logger.error(f"Error processing new image in edit_item: {str(e)}") else: flash(error_message, 'error') return redirect(url_for('home_admin')) - # If location is not in the predefined list, maybe add it (depending on policy) predefined_locations = it.get_predefined_locations() if ort and ort not in predefined_locations: it.add_predefined_location(ort) - + result = it.update_item( - id=id, - name=name, - ort=ort, - beschreibung=beschreibung, - images=images, - verfuegbar=verfuegbar, - filter1=filter1, - filter2=filter2, + id=id, + name=name, + ort=ort, + beschreibung=beschreibung, + images=images, + verfuegbar=verfuegbar, + filter1=filter1, + filter2=filter2, filter3=filter3, - ansch_jahr=anschaffungs_jahr, - ansch_kost=anschaffungs_kosten, - code_4=code_4, + ansch_jahr=anschaffungs_jahr, + ansch_kost=anschaffungs_kosten, + code_4=code_4, reservierbar=reservierbar, isbn=item_isbn, item_type=item_type ) - + if result: flash('Element erfolgreich aktualisiert (und ggf. Gruppe synchronisiert)', 'success') else: flash('Fehler beim Aktualisieren des Elements', 'error') - + return redirect(url_for('home_admin'))