diff --git a/__pycache__/forms.cpython-313.pyc b/__pycache__/forms.cpython-313.pyc index beb9363..ee0f021 100644 Binary files a/__pycache__/forms.cpython-313.pyc and b/__pycache__/forms.cpython-313.pyc differ diff --git a/__pycache__/models.cpython-313.pyc b/__pycache__/models.cpython-313.pyc index cc98e34..ec974e1 100644 Binary files a/__pycache__/models.cpython-313.pyc and b/__pycache__/models.cpython-313.pyc differ diff --git a/forms.py b/forms.py index 77bcf0b..de158f7 100644 --- a/forms.py +++ b/forms.py @@ -1,5 +1,5 @@ from flask_wtf import FlaskForm -from wtforms import StringField, TextAreaField, BooleanField, SubmitField, PasswordField, SelectMultipleField +from wtforms import StringField, TextAreaField, BooleanField, SubmitField, PasswordField, SelectMultipleField, SelectField from wtforms.validators import DataRequired, Email, Length, Optional, ValidationError from models import User from flask_login import current_user @@ -13,8 +13,11 @@ class UserForm(FlaskForm): company = StringField('Company (Optional)', validators=[Optional(), Length(max=100)]) position = StringField('Position (Optional)', validators=[Optional(), Length(max=100)]) notes = TextAreaField('Notes (Optional)', validators=[Optional()]) - is_admin = BooleanField('Admin Role', default=False) - is_manager = BooleanField('Manager Role', default=False) + role = SelectField('Role', choices=[ + ('user', 'Standard User'), + ('manager', 'Manager'), + ('admin', 'Administrator') + ], validators=[DataRequired()]) new_password = PasswordField('New Password (Optional)') confirm_password = PasswordField('Confirm Password (Optional)') profile_picture = FileField('Profile Picture (Optional)', validators=[FileAllowed(['jpg', 'jpeg', 'png', 'gif'], 'Images only!')]) diff --git a/routes/__pycache__/contacts.cpython-313.pyc b/routes/__pycache__/contacts.cpython-313.pyc index 7bea8e6..21bd10c 100644 Binary files a/routes/__pycache__/contacts.cpython-313.pyc and b/routes/__pycache__/contacts.cpython-313.pyc differ diff --git a/routes/contacts.py b/routes/contacts.py index c08824f..6403e64 100644 --- a/routes/contacts.py +++ b/routes/contacts.py @@ -98,95 +98,61 @@ def new_contact(): form = UserForm() total_admins = User.query.filter_by(is_admin=True).count() if request.method == 'GET': - form.is_admin.data = False # Ensure admin role is unchecked by default - form.is_manager.data = False # Ensure manager role is unchecked by default + form.role.data = 'user' # Default to standard user elif request.method == 'POST': - if 'is_admin' not in request.form: - form.is_admin.data = False - if 'is_manager' not in request.form: - form.is_manager.data = False + if form.validate_on_submit(): + # Check if a user with this email already exists + existing_user = User.query.filter_by(email=form.email.data).first() + if existing_user: + flash('A user with this email already exists.', 'error') + return render_template('contacts/form.html', form=form, title='New User', total_admins=total_admins) + + # Handle profile picture upload + profile_picture = None + file = request.files.get('profile_picture') + if file and file.filename: + filename = secure_filename(file.filename) + file_path = os.path.join(UPLOAD_FOLDER, filename) + file.save(file_path) + profile_picture = filename - if form.validate_on_submit(): - # Check if a user with this email already exists - existing_user = User.query.filter_by(email=form.email.data).first() - if existing_user: - flash('A user with this email already exists.', 'error') - return render_template('contacts/form.html', form=form, title='New User', total_admins=total_admins) - - # Handle profile picture upload - profile_picture = None - file = request.files.get('profile_picture') - if file and file.filename: - filename = secure_filename(file.filename) - file_path = os.path.join(UPLOAD_FOLDER, filename) - file.save(file_path) - profile_picture = filename + # Generate a random password + alphabet = string.ascii_letters + string.digits + string.punctuation + random_password = ''.join(secrets.choice(alphabet) for _ in range(32)) - # Generate a random password - alphabet = string.ascii_letters + string.digits + string.punctuation - random_password = ''.join(secrets.choice(alphabet) for _ in range(32)) + # Create new user + user = User( + username=form.first_name.data, + last_name=form.last_name.data, + email=form.email.data, + phone=form.phone.data, + company=form.company.data, + position=form.position.data, + notes=form.notes.data, + is_admin=(form.role.data == 'admin'), + is_manager=(form.role.data == 'manager'), + profile_picture=profile_picture + ) + user.set_password(random_password) + db.session.add(user) - # Create new user account - user = User( - username=form.first_name.data, - last_name=form.last_name.data, - email=form.email.data, - phone=form.phone.data, - company=form.company.data, - position=form.position.data, - notes=form.notes.data, - is_active=True, # Set default value - is_admin=form.is_admin.data, - is_manager=form.is_manager.data, - profile_picture=profile_picture - ) - user.set_password(random_password) - db.session.add(user) - db.session.commit() + # Log user creation event + log_event( + event_type='user_create', + details={ + 'created_by': current_user.id, + 'created_by_name': f"{current_user.username} {current_user.last_name}", + 'user_id': user.id, + 'user_name': f"{user.username} {user.last_name}", + 'email': user.email, + 'role': form.role.data, + 'method': 'admin_creation' + } + ) + db.session.commit() - # Create password setup token - token = secrets.token_urlsafe(32) - setup_token = PasswordSetupToken( - user_id=user.id, - token=token, - expires_at=datetime.utcnow() + timedelta(hours=24) - ) - db.session.add(setup_token) - db.session.commit() - - # Create notification for the new user - create_notification( - notif_type='account_created', - user_id=user.id, - sender_id=current_user.id, # Admin who created the account - details={ - 'message': 'Your DocuPulse account has been created by an administrator.', - 'username': user.username, - 'email': user.email, - 'created_by': f"{current_user.username} {current_user.last_name}", - 'timestamp': datetime.utcnow().isoformat(), - 'setup_link': url_for('auth.setup_password', token=token, _external=True) - } - ) - - # Log user creation event - log_event( - event_type='user_create', - details={ - 'created_by': current_user.id, - 'created_by_name': f"{current_user.username} {current_user.last_name}", - 'user_id': user.id, - 'user_name': f"{user.username} {user.last_name}", - 'email': user.email, - 'is_admin': user.is_admin, - 'is_manager': user.is_manager, - 'method': 'admin_creation' - } - ) - db.session.commit() - - flash('User created successfully! They will receive an email with a link to set up their password.', 'success') - return redirect(url_for('contacts.contacts_list')) + flash('User created successfully! They will receive an email with a link to set up their password.', 'success') + return redirect(url_for('contacts.contacts_list')) return render_template('contacts/form.html', form=form, title='New User', total_admins=total_admins) @contacts_bp.route('/profile/edit', methods=['GET', 'POST']) @@ -287,7 +253,13 @@ def edit_contact(id): form.company.data = user.company form.position.data = user.position form.notes.data = user.notes - form.is_admin.data = user.is_admin + # Set role based on current permissions + if user.is_admin: + form.role.data = 'admin' + elif user.is_manager: + form.role.data = 'manager' + else: + form.role.data = 'user' if form.validate_on_submit(): # Handle profile picture removal if 'remove_picture' in request.form: @@ -315,9 +287,10 @@ def edit_contact(id): user.profile_picture = filename # Prevent removing admin from the last admin - if not form.is_admin.data and user.is_admin and total_admins <= 1: + if form.role.data != 'admin' and user.is_admin and total_admins <= 1: flash('There must be at least one admin user in the system.', 'error') return render_template('contacts/form.html', form=form, title='Edit User', total_admins=total_admins, user=user) + # Check if the new email is already used by another user if form.email.data != user.email: existing_user = User.query.filter_by(email=form.email.data).first() @@ -332,7 +305,7 @@ def edit_contact(id): 'phone': user.phone, 'company': user.company, 'position': user.position, - 'is_admin': user.is_admin + 'role': 'admin' if user.is_admin else 'manager' if user.is_manager else 'user' } user.username = form.first_name.data @@ -342,7 +315,8 @@ def edit_contact(id): user.company = form.company.data user.position = form.position.data user.notes = form.notes.data - user.is_admin = form.is_admin.data + user.is_admin = (form.role.data == 'admin') + user.is_manager = (form.role.data == 'manager') # Set password if provided password_changed = False @@ -366,6 +340,7 @@ def edit_contact(id): 'phone': user.phone, 'company': user.company, 'position': user.position, + 'role': form.role.data, 'password_changed': password_changed }, 'timestamp': datetime.utcnow().isoformat() @@ -387,7 +362,7 @@ def edit_contact(id): 'phone': user.phone, 'company': user.company, 'position': user.position, - 'is_admin': user.is_admin + 'role': form.role.data }, 'password_changed': password_changed, 'method': 'admin_update' diff --git a/templates/contacts/form.html b/templates/contacts/form.html index 7ad5f73..da8a337 100644 --- a/templates/contacts/form.html +++ b/templates/contacts/form.html @@ -113,37 +113,45 @@ {% endif %} -
-
-
- {% set is_last_admin = current_user.is_admin and total_admins <= 1 %} - {{ form.is_admin( - class="h-4 w-4 focus:ring-blue-500 border-gray-300 rounded", - style="accent-color: #16767b;", - disabled=is_last_admin and form.is_admin.data - ) }} - {{ form.is_admin.label(class="ml-2 block text-sm text-gray-900") }} - {% if is_last_admin and form.is_admin.data %} - - {% endif %} - -
-
- {{ form.is_manager( - class="h-4 w-4 focus:ring-blue-500 border-gray-300 rounded", - style="accent-color: #16767b;" - ) }} - {{ form.is_manager.label(class="ml-2 block text-sm text-gray-900") }} - + +
+

Role Selection

+
+ {% for value, label in form.role.choices %} +
+ +
+ {% endfor %} + {% if form.role.errors %} + {% for error in form.role.errors %} +

{{ error }}

+ {% endfor %} + {% endif %}
- {% if form.is_admin.errors %} +
+ +
+ {% if form.role.errors %}
- {% for error in form.is_admin.errors %} + {% for error in form.role.errors %}

{{ error }}

{% endfor %}