Better contact form

This commit is contained in:
2025-06-05 21:05:15 +02:00
parent f65265b4a5
commit 57fa221d47
6 changed files with 106 additions and 120 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -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!')])

View File

@@ -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'

View File

@@ -113,37 +113,45 @@
{% endif %}
</div>
<div class="flex flex-col space-y-4">
<div class="flex items-center space-x-4">
<div class="flex items-center relative group">
{% 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 %}
<input type="hidden" name="is_admin" value="y">
{% endif %}
<div class="absolute left-0 bottom-full mb-2 hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 w-48">
Admin users have full access to manage contacts and system settings.
</div>
</div>
<div class="flex items-center relative group">
{{ 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") }}
<div class="absolute left-0 bottom-full mb-2 hidden group-hover:block bg-gray-800 text-white text-xs rounded py-1 px-2 w-48">
Manager users have similar permissions to admins but with some restrictions.
</div>
<!-- Role Selection Section -->
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
<h4 class="text-lg font-medium text-gray-900 mb-4">Role Selection</h4>
<div class="space-y-3">
{% for value, label in form.role.choices %}
<div class="flex items-center">
<input type="radio"
name="{{ form.role.name }}"
value="{{ value }}"
id="role_{{ value }}"
class="h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300"
{% if form.role.data == value %}checked{% endif %}
{% if value == 'admin' and current_user.is_admin and total_admins <= 1 and form.role.data == 'admin' %}disabled{% endif %}>
<label for="role_{{ value }}" class="ml-3 block text-sm font-medium text-gray-700">
{{ label }}
<span class="text-gray-500 text-xs block mt-0.5">
{% if value == 'admin' %}
Full access to all contacts, rooms and conversations. Can manage system settings and website customization.
{% elif value == 'manager' %}
Can create and manage rooms and conversations. Can view all contacts. Only limited access to system settings.
{% else %}
Basic user access.
{% endif %}
</span>
</label>
</div>
{% endfor %}
{% if form.role.errors %}
{% for error in form.role.errors %}
<p class="mt-1 text-sm text-red-600">{{ error }}</p>
{% endfor %}
{% endif %}
</div>
{% if form.is_admin.errors %}
</div>
<div class="flex flex-col space-y-4">
{% if form.role.errors %}
<div class="p-3 bg-red-50 border border-red-200 rounded-lg">
{% for error in form.is_admin.errors %}
{% for error in form.role.errors %}
<p class="text-sm text-red-700">{{ error }}</p>
{% endfor %}
</div>