Passed
Push — master ( 06ca5d...ce6165 )
by William
03:01 queued 01:24
created

app.main.activate_user()   B

Complexity

Conditions 6

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 18
nop 1
dl 0
loc 28
rs 8.5666
c 0
b 0
f 0
1
from flask import request, redirect, url_for, send_from_directory, flash, send_file, Response
2
from flask_login import login_required, current_user
3
from flask import Blueprint, render_template
4
from .models import Schedule, Balance, User, Settings, Email, Hold, Skip, GlobalEmailSettings
5
from app import db
6
from datetime import datetime
7
import os
8
from sqlalchemy import desc, extract, asc
9
from werkzeug.security import generate_password_hash, check_password_hash
10
from .cashflow import update_cash, plot_cash
11
from .auth import admin_required, global_admin_required, account_owner_required
12
from .files import export, upload, version
13
from .getemail import send_account_activation_notification
14
15
16
main = Blueprint('main', __name__)
17
18
19
def get_effective_user_id():
20
    """
21
    Get the effective user ID for data filtering.
22
    For guest users, returns their account owner's ID.
23
    For account owners, returns their own ID.
24
    """
25
    if current_user.account_owner_id:
26
        # Guest user - return account owner's ID
27
        return current_user.account_owner_id
28
    else:
29
        # Account owner or standalone user - return their own ID
30
        return current_user.id
31
32
33
@main.route('/', methods=('GET', 'POST'))
34
@login_required
35
def index():
36
    # Get effective user ID (account owner for guests, self for owners)
37
    user_id = get_effective_user_id()
38
39
    # get today's date
40
    todaydate = datetime.today().strftime('%A, %B %d, %Y')
41
42
    # query the latest balance information for this user
43
    balance = Balance.query.filter_by(user_id=user_id).order_by(desc(Balance.date), desc(Balance.id)).first()
44
45
    try:
46
        float(balance.amount)
47
        db.session.query(Balance).filter_by(user_id=user_id).delete()
48
        balance = Balance(amount=balance.amount, date=datetime.today(), user_id=user_id)
49
        db.session.add(balance)
50
        db.session.commit()
51
    except:
52
        balance = Balance(amount='0', date=datetime.today(), user_id=user_id)
53
        db.session.add(balance)
54
        db.session.commit()
55
56
    # Pre-filter data by user before passing to cashflow
57
    schedules = Schedule.query.filter_by(user_id=user_id).all()
58
    holds = Hold.query.filter_by(user_id=user_id).all()
59
    skips = Skip.query.filter_by(user_id=user_id).all()
60
61
    trans, run = update_cash(float(balance.amount), schedules, holds, skips)
62
63
    # plot cash flow results
64
    minbalance, graphJSON = plot_cash(run)
65
66
    if current_user.admin:
67
        return render_template('index.html', title='Index', todaydate=todaydate, balance=balance.amount,
68
                           minbalance=minbalance, graphJSON=graphJSON)
69
    else:
70
        return render_template('index_guest.html', title='Index', todaydate=todaydate, balance=balance.amount,
71
                           minbalance=minbalance, graphJSON=graphJSON)
72
73
74
@main.route('/refresh')
75
@login_required
76
def refresh():
77
78
    return redirect(url_for('main.index'))
79
80
81
@main.route('/settings')
82
@login_required
83
def settings():
84
    # get about info - available to all users
85
    about = version()
86
87
    if current_user.admin:
88
        return render_template('settings.html', about=about)
89
    else:
90
        return render_template('settings_guest.html', about=about)
91
92
93
@main.route('/schedule')
94
@login_required
95
@admin_required
96
def schedule():
97
    user_id = get_effective_user_id()
98
    schedule = Schedule.query.filter_by(user_id=user_id).order_by(asc(extract('day', Schedule.startdate)))
99
100
    return render_template('schedule_table.html', title='Schedule Table', schedule=schedule)
101
102
103
@main.route('/holds')
104
@login_required
105
@admin_required
106
def holds():
107
    user_id = get_effective_user_id()
108
    hold = Hold.query.filter_by(user_id=user_id)
109
    skip = Skip.query.filter_by(user_id=user_id)
110
111
    return render_template('holds_table.html', title='Holds Table', hold=hold, skip=skip)
112
113
114
@main.route('/create', methods=('GET', 'POST'))
115
@login_required
116
@admin_required
117
def create():
118
    # create a new schedule item
119
    user_id = get_effective_user_id()
120
    format = '%Y-%m-%d'
121
    if request.method == 'POST':
122
        name = request.form['name']
123
        amount = request.form['amount']
124
        frequency = request.form['frequency']
125
        startdate = request.form['startdate']
126
        type = request.form['type']
127
        schedule = Schedule(name=name,
128
                            type=type,
129
                            amount=amount,
130
                            frequency=frequency,
131
                            startdate=datetime.strptime(startdate, format).date(),
132
                            firstdate=datetime.strptime(startdate, format).date(),
133
                            user_id=user_id)
134
        existing = Schedule.query.filter_by(name=name, user_id=user_id).first()
135
        if existing:
136
            flash("Schedule already exists")
137
            return redirect(url_for('main.schedule'))
138
        db.session.add(schedule)
139
        db.session.commit()
140
        flash("Added Successfully")
141
142
        return redirect(url_for('main.schedule'))
143
144
    return redirect(url_for('main.schedule'))
145
146
147
@main.route('/update', methods=['GET', 'POST'])
148
@login_required
149
@admin_required
150
def update():
151
    # update an existing schedule item
152
    user_id = get_effective_user_id()
153
    format = '%Y-%m-%d'
154
155
    if request.method == 'POST':
156
        current = Schedule.query.filter_by(id=int(request.form['id']), user_id=user_id).first()
157
        existing = Schedule.query.filter_by(name=request.form['name'], user_id=user_id).first()
158
        if existing:
159
            if current.name != request.form['name']:
160
                flash("Schedule name already exists")
161
                return redirect(url_for('main.schedule'))
162
        my_data = Schedule.query.filter_by(id=int(request.form.get('id')), user_id=user_id).first()
163
        my_data.name = request.form['name']
164
        my_data.amount = request.form['amount']
165
        my_data.type = request.form['type']
166
        my_data.frequency = request.form['frequency']
167
        startdate = request.form['startdate']
168
        if (datetime.strptime(startdate, format).date() != my_data.startdate and my_data.startdate.day !=
169
                datetime.strptime(startdate, format).day):
170
            my_data.firstdate = datetime.strptime(startdate, format).date()
171
        my_data.startdate = datetime.strptime(startdate, format).date()
172
        db.session.commit()
173
        flash("Updated Successfully")
174
175
        return redirect(url_for('main.schedule'))
176
177
    return redirect(url_for('main.schedule'))
178
179
180
@main.route('/addhold/<id>')
181
@login_required
182
@admin_required
183
def addhold(id):
184
    # add a hold item from the schedule
185
    user_id = get_effective_user_id()
186
    schedule = Schedule.query.filter_by(id=int(id), user_id=user_id).first()
187
    hold = Hold(name=schedule.name, type=schedule.type, amount=schedule.amount, user_id=user_id)
188
    db.session.add(hold)
189
    db.session.commit()
190
    flash("Added Hold")
191
192
    return redirect(url_for('main.schedule'))
193
194
195
@main.route('/addskip/<id>')
196
@login_required
197
@admin_required
198
def addskip(id):
199
    # add a skip item from the schedule
200
    user_id = get_effective_user_id()
201
    balance = Balance.query.filter_by(user_id=user_id).order_by(desc(Balance.date), desc(Balance.id)).first()
202
203
    # Pre-filter data by user
204
    schedules = Schedule.query.filter_by(user_id=user_id).all()
205
    holds = Hold.query.filter_by(user_id=user_id).all()
206
    skips = Skip.query.filter_by(user_id=user_id).all()
207
208
    trans, run = update_cash(float(balance.amount), schedules, holds, skips)
209
    transaction = trans.loc[int(id)]
210
    trans_type = ""
211
    if transaction[1] == "Expense":
212
        trans_type = "Income"
213
    elif transaction[1] == "Income":
214
        trans_type = "Expense"
215
    skip = Skip(name=transaction[0] + " (SKIP)", type=trans_type, amount=transaction[2], date=transaction[3], user_id=user_id)
216
    db.session.add(skip)
217
    db.session.commit()
218
    flash("Added Skip")
219
220
    return redirect(url_for('main.transactions'))
221
222
223
@main.route('/deletehold/<id>')
224
@login_required
225
@admin_required
226
def holds_delete(id):
227
    # delete a hold item
228
    user_id = get_effective_user_id()
229
    hold = Hold.query.filter_by(id=int(id), user_id=user_id).first()
230
231
    if hold:
232
        db.session.delete(hold)
233
        db.session.commit()
234
        flash("Deleted Successfully")
235
236
    return redirect(url_for('main.holds'))
237
238
239
@main.route('/deleteskip/<id>')
240
@login_required
241
@admin_required
242
def skips_delete(id):
243
    # delete a skip item
244
    user_id = get_effective_user_id()
245
    skip = Skip.query.filter_by(id=int(id), user_id=user_id).first()
246
247
    if skip:
248
        db.session.delete(skip)
249
        db.session.commit()
250
        flash("Deleted Successfully")
251
252
    return redirect(url_for('main.holds'))
253
254
255
@main.route('/clearholds')
256
@login_required
257
@admin_required
258
def clear_holds():
259
    # clear holds
260
    user_id = get_effective_user_id()
261
    db.session.query(Hold).filter_by(user_id=user_id).delete()
262
    db.session.commit()
263
264
    return redirect(url_for('main.holds'))
265
266
267
@main.route('/clearskips')
268
@login_required
269
@admin_required
270
def clear_skips():
271
    # clear skips
272
    user_id = get_effective_user_id()
273
    db.session.query(Skip).filter_by(user_id=user_id).delete()
274
    db.session.commit()
275
276
    return redirect(url_for('main.holds'))
277
278
279
@main.route('/delete/<id>')
280
@login_required
281
@admin_required
282
def schedule_delete(id):
283
    # delete a schedule item
284
    user_id = get_effective_user_id()
285
    schedule = Schedule.query.filter_by(id=int(id), user_id=user_id).first()
286
287
    if schedule:
288
        db.session.delete(schedule)
289
        db.session.commit()
290
        flash("Deleted Successfully")
291
292
    return redirect(url_for('main.schedule'))
293
294
295
@main.route('/favicon')
296
def favicon():
297
    return send_from_directory(os.path.join(main.root_path, 'static'),
298
                               'favicon.ico', mimetype='image/vnd.microsoft.icon')
299
300
301
@main.route('/appleicon')
302
def appleicon():
303
    return send_from_directory(os.path.join(main.root_path, 'static'),
304
                               'apple-touch-icon.png', mimetype='image/png')
305
306
307
@main.route('/balance', methods=('GET', 'POST'))
308
@login_required
309
@admin_required
310
def balance():
311
    # manually update the balance from the balance button
312
    user_id = get_effective_user_id()
313
    format = '%Y-%m-%d'
314
    if request.method == 'POST':
315
        amount = request.form['amount']
316
        dateentry = request.form['date']
317
        balance = Balance(amount=amount,
318
                          date=datetime.strptime(dateentry, format).date(),
319
                          user_id=user_id)
320
        db.session.add(balance)
321
        db.session.commit()
322
323
        return redirect(url_for('main.index'))
324
325
326
@main.route('/changepw', methods=('GET', 'POST'))
327
@login_required
328
def changepw():
329
    # change the users password from the settings page
330
    if request.method == 'POST':
331
        curr_user = current_user.id
332
        my_user = User.query.filter_by(id=curr_user).first()
333
        current = request.form['current']
334
        password = request.form['password']
335
        password2 = request.form['password2']
336
        if password == password2 and check_password_hash(my_user.password, current):
337
            my_user.password = generate_password_hash(password, method='scrypt')
338
            db.session.commit()
339
            flash('Password change successful')
340
        elif password != password2:
341
            flash('Passwords do not match')
342
        elif not check_password_hash(my_user.password, current):
343
            flash('Incorrect password')
344
345
        return redirect(url_for('main.settings'))
346
347
    return redirect(url_for('main.settings'))
348
349
350
@main.route('/signups', methods=('GET', 'POST'))
351
@login_required
352
@global_admin_required
353
def signups():
354
    # set the settings options, in this case disable signups, from the profile page
355
    if request.method == 'POST':
356
        signupsettingname = Settings.query.filter_by(name='signup').first()
357
358
        if signupsettingname:
359
            if request.form['signupvalue'] == "True":
360
                signupvalue = True
361
            else:
362
                signupvalue = False
363
            signupsettingname.value = signupvalue
364
            db.session.commit()
365
366
            return redirect(url_for('main.settings'))
367
368
        # store the signup option value in the database to check when the user clicks signup
369
        if request.form['signupvalue'] == "True":
370
            signupvalue = True
371
        else:
372
            signupvalue = False
373
        settings = Settings(name="signup", value=signupvalue)
374
        db.session.add(settings)
375
        db.session.commit()
376
377
        return redirect(url_for('main.settings'))
378
379
    return redirect(url_for('main.settings'))
380
381
382
@main.route('/transactions')
383
@login_required
384
@admin_required
385
def transactions():
386
    user_id = get_effective_user_id()
387
    balance = Balance.query.filter_by(user_id=user_id).order_by(desc(Balance.date), desc(Balance.id)).first()
388
389
    # Pre-filter data by user
390
    schedules = Schedule.query.filter_by(user_id=user_id).all()
391
    holds = Hold.query.filter_by(user_id=user_id).all()
392
    skips = Skip.query.filter_by(user_id=user_id).all()
393
394
    trans, run = update_cash(float(balance.amount), schedules, holds, skips)
395
396
    return render_template('transactions_table.html', total=trans.to_dict(orient='records'))
397
398
399
@main.route('/email', methods=('GET', 'POST'))
400
@login_required
401
@admin_required
402
def email():
403
    # set the users email address, password, and server for the auto email balance update
404
    user_id = get_effective_user_id()
405
406
    if request.method == 'POST':
407
        emailsettings = Email.query.filter_by(user_id=user_id).first()
408
409
        if emailsettings:
410
            email = request.form['email']
411
            password = request.form['password']
412
            server = request.form['server']
413
            subjectstr = request.form['subject_str']
414
            startstr = request.form['start_str']
415
            endstr = request.form['end_str']
416
            emailsettings.email = email
417
            emailsettings.password = password
418
            emailsettings.server = server
419
            emailsettings.subjectstr = subjectstr
420
            emailsettings.startstr = startstr
421
            emailsettings.endstr = endstr
422
            db.session.commit()
423
424
            return redirect(url_for('main.settings'))
425
426
        email = request.form['email']
427
        password = request.form['password']
428
        server = request.form['server']
429
        subjectstr = request.form['subject_str']
430
        startstr = request.form['start_str']
431
        endstr = request.form['end_str']
432
        emailentry = Email(email=email, password=password, server=server, subjectstr=subjectstr, startstr=startstr,
433
                           endstr=endstr, user_id=user_id)
434
        db.session.add(emailentry)
435
        db.session.commit()
436
437
        return redirect(url_for('main.settings'))
438
439
    return redirect(url_for('main.settings'))
440
441
442
@main.route('/update_user', methods=['GET', 'POST'])
443
@login_required
444
@admin_required
445
def update_user():
446
    # update an existing user
447
    if request.method == 'POST':
448
        current = User.query.filter_by(id=int(request.form['id'])).first()
449
450
        # Check if the current user has permission to update this user
451
        if not current_user.is_global_admin and current.account_owner_id != current_user.id:
452
            flash("You don't have permission to update this user")
453
            if current_user.is_global_admin:
454
                return redirect(url_for('main.global_admin_panel'))
455
            else:
456
                return redirect(url_for('main.manage_guests'))
457
458
        existing = User.query.filter_by(email=request.form['email']).first()
459
        if existing:
460
            if current.email != request.form['email']:
461
                flash("Email already exists")
462
                if current_user.is_global_admin:
463
                    return redirect(url_for('main.global_admin_panel'))
464
                else:
465
                    return redirect(url_for('main.manage_guests'))
466
        my_data = User.query.get(request.form.get('id'))
467
        my_data.name = request.form['name']
468
        my_data.email = request.form['email']
469
470
        # Global admins can change roles, regular admins cannot
471
        if current_user.is_global_admin:
472
            # Handle role assignment
473
            role = request.form.get('role', 'user')
474
            if role == 'global_admin':
475
                my_data.admin = True
476
                my_data.is_global_admin = True
477
                # IMPORTANT: Global admins must always be active
478
                my_data.is_active = True
479
            elif role == 'admin':
480
                my_data.admin = True
481
                my_data.is_global_admin = False
482
            else:  # user
483
                my_data.admin = False
484
                my_data.is_global_admin = False
485
486
        db.session.commit()
487
        flash("Updated Successfully")
488
489
        # Redirect based on user role
490
        if current_user.is_global_admin:
491
            return redirect(url_for('main.global_admin_panel'))
492
        else:
493
            return redirect(url_for('main.manage_guests'))
494
495
    # Redirect based on user role
496
    if current_user.is_global_admin:
497
        return redirect(url_for('main.global_admin_panel'))
498
    else:
499
        return redirect(url_for('main.manage_guests'))
500
501
502 View Code Duplication
@main.route('/delete_user/<id>')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
503
@login_required
504
@admin_required
505
def delete_user(id):
506
    # delete a user
507
    user = User.query.filter_by(id=int(id)).first()
508
509
    if user:
510
        # Global admins can delete any user (except themselves), regular admins can only delete their guests
511
        if current_user.is_global_admin or user.account_owner_id == current_user.id:
512
            # Prevent deleting yourself
513
            if user.id == current_user.id:
514
                flash("You cannot delete your own account")
515
                if current_user.is_global_admin:
516
                    return redirect(url_for('main.global_admin_panel'))
517
                else:
518
                    return redirect(url_for('main.manage_guests'))
519
520
            db.session.delete(user)
521
            db.session.commit()
522
            flash("Deleted Successfully")
523
        else:
524
            flash("You don't have permission to delete this user")
525
526
    # Redirect based on user role
527
    if current_user.is_global_admin:
528
        return redirect(url_for('main.global_admin_panel'))
529
    else:
530
        return redirect(url_for('main.manage_guests'))
531
532
533
@main.route('/activate_user/<id>')
534
@login_required
535
@admin_required
536
def activate_user(id):
537
    # activate a user account
538
    user = User.query.filter_by(id=int(id)).first()
539
540
    if user:
541
        # Global admins can activate any user, regular admins can only activate their guests
542
        if current_user.is_global_admin or user.account_owner_id == current_user.id:
543
            user.is_active = True
544
            db.session.commit()
545
            flash(f"User {user.name} has been activated successfully")
546
547
            # Send activation notification email to the user
548
            try:
549
                send_account_activation_notification(user.name, user.email)
550
            except Exception as e:
551
                # Don't fail activation if email notification fails
552
                print(f"Failed to send activation notification to {user.email}: {e}")
553
        else:
554
            flash("You don't have permission to activate this user")
555
556
    # Redirect based on user role
557
    if current_user.is_global_admin:
558
        return redirect(url_for('main.global_admin_panel'))
559
    else:
560
        return redirect(url_for('main.manage_guests'))
561
562
563 View Code Duplication
@main.route('/deactivate_user/<id>')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
564
@login_required
565
@admin_required
566
def deactivate_user(id):
567
    # deactivate a user account
568
    user = User.query.filter_by(id=int(id)).first()
569
570
    if user:
571
        # IMPORTANT: Global admins are always active and cannot be deactivated
572
        if user.is_global_admin:
573
            flash("Cannot deactivate a global admin. Global admins must always remain active.")
574
            if current_user.is_global_admin:
575
                return redirect(url_for('main.global_admin_panel'))
576
            else:
577
                return redirect(url_for('main.manage_guests'))
578
579
        # Global admins can deactivate any user, regular admins can only deactivate their guests
580
        if current_user.is_global_admin or user.account_owner_id == current_user.id:
581
            user.is_active = False
582
            db.session.commit()
583
            flash(f"User {user.name} has been deactivated successfully")
584
        else:
585
            flash("You don't have permission to deactivate this user")
586
587
    # Redirect based on user role
588
    if current_user.is_global_admin:
589
        return redirect(url_for('main.global_admin_panel'))
590
    else:
591
        return redirect(url_for('main.manage_guests'))
592
593
594
@main.route('/create_user', methods=('GET', 'POST'))
595
@login_required
596
@admin_required
597
def create_user():
598
    # create a new user
599
    if request.method == 'POST':
600
        name = request.form['name']
601
        email = request.form['email']
602
        password = generate_password_hash(request.form['password'], method='scrypt')
603
604
        # Handle role assignment
605
        role = request.form.get('role', 'user')
606
607
        # Global admins can create any type of user
608
        if current_user.is_global_admin:
609
            if role == 'global_admin':
610
                admin = True
611
                is_global_admin = True
612
            elif role == 'admin':
613
                admin = True
614
                is_global_admin = False
615
            else:  # user
616
                admin = False
617
                is_global_admin = False
618
            account_owner_id = None
619
            # Users created by global admin are active by default
620
            is_active = True
621
        else:
622
            # Regular admins can only create guest users (non-admin users)
623
            admin = False
624
            is_global_admin = False
625
            account_owner_id = current_user.id
626
            # Guests created by regular admins are active by default
627
            is_active = True
628
629
        user = User(name=name, email=email, admin=admin, is_global_admin=is_global_admin,
630
                    is_active=is_active, password=password, account_owner_id=account_owner_id)
631
        existing = User.query.filter_by(email=email).first()
632
        if existing:
633
            flash("User already exists")
634
            if current_user.is_global_admin:
635
                return redirect(url_for('main.global_admin_panel'))
636
            else:
637
                return redirect(url_for('main.manage_guests'))
638
        db.session.add(user)
639
        db.session.commit()
640
        flash("Added Successfully")
641
642
        # Redirect based on user role
643
        if current_user.is_global_admin:
644
            return redirect(url_for('main.global_admin_panel'))
645
        else:
646
            return redirect(url_for('main.manage_guests'))
647
648
    # Redirect based on user role
649
    if current_user.is_global_admin:
650
        return redirect(url_for('main.global_admin_panel'))
651
    else:
652
        return redirect(url_for('main.manage_guests'))
653
654
655
@main.route('/export', methods=('GET', 'POST'))
656
@login_required
657
@admin_required
658
def export_csv():
659
    user_id = get_effective_user_id()
660
661
    csv_data = export(user_id)
662
663
    # Create a direct download response with the CSV data and appropriate headers
664
    response = Response(csv_data, content_type="text/csv")
665
    response.headers["Content-Disposition"] = "attachment; filename=schedule_export.csv"
666
667
    return response
668
669
670
@main.route('/import', methods=('GET', 'POST'))
671
@login_required
672
@admin_required
673
def import_csv():
674
    if request.method == 'POST':
675
        user_id = get_effective_user_id()
676
        csv_file = request.files.get('file')
677
678
        upload(csv_file, user_id)
679
680
        flash("Import Successful")
681
682
    return redirect(url_for('main.schedule'))
683
684
685
@main.route('/manage_guests')
686
@login_required
687
@account_owner_required
688
def manage_guests():
689
    """Account owners can manage their guest users"""
690
    guests = User.query.filter_by(account_owner_id=current_user.id).all()
691
    return render_template('manage_guests.html', guests=guests)
692
693
694
@main.route('/add_guest', methods=['POST'])
695
@login_required
696
@account_owner_required
697
def add_guest():
698
    """Create a guest user linked to current account owner"""
699
    email = request.form.get('email')
700
    name = request.form.get('name')
701
702
    # Check if user already exists
703
    existing_user = User.query.filter_by(email=email).first()
704
    if existing_user:
705
        flash('A user with this email already exists')
706
        return redirect(url_for('main.manage_guests'))
707
708
    # Generate a random password for guest (they can change it later)
709
    import secrets
710
    temp_password = secrets.token_urlsafe(16)
711
712
    new_guest = User(
713
        email=email,
714
        name=name,
715
        password=generate_password_hash(temp_password, method='scrypt'),
716
        admin=False,  # Guests are not admins
717
        is_global_admin=False,
718
        account_owner_id=current_user.id
719
    )
720
    db.session.add(new_guest)
721
    db.session.commit()
722
723
    flash(f'Guest user {name} added successfully. Temporary password: {temp_password}')
724
    return redirect(url_for('main.manage_guests'))
725
726
727
@main.route('/remove_guest/<int:guest_id>', methods=['POST'])
728
@login_required
729
@account_owner_required
730
def remove_guest(guest_id):
731
    """Remove a guest user (account owner only)"""
732
    guest = User.query.filter_by(id=int(guest_id), account_owner_id=current_user.id).first()
733
734
    if guest:
735
        db.session.delete(guest)
736
        db.session.commit()
737
        flash('Guest user removed successfully')
738
    else:
739
        flash('Guest user not found or you do not have permission to remove them')
740
741
    return redirect(url_for('main.manage_guests'))
742
743
744
@main.route('/global_admin')
745
@login_required
746
@global_admin_required
747
def global_admin_panel():
748
    """Global admin can see all users and accounts"""
749
    all_users = User.query.all()
750
751
    # Organize users by account owners
752
    account_owners = [u for u in all_users if u.account_owner_id is None and not u.is_global_admin]
753
    global_admins = [u for u in all_users if u.is_global_admin]
754
    standalone_users = [u for u in all_users if u.account_owner_id is None and not u.admin]
755
756
    return render_template('global_admin.html',
757
                         all_users=all_users,
758
                         account_owners=account_owners,
759
                         global_admins=global_admins,
760
                         standalone_users=standalone_users)
761
762
763
@main.route('/global_email_settings', methods=['GET', 'POST'])
764
@login_required
765
@global_admin_required
766
def global_email_settings():
767
    """Configure global email settings for all email notifications"""
768
    if request.method == 'POST':
769
        email = request.form.get('email')
770
        password = request.form.get('password')
771
        smtp_server = request.form.get('smtp_server')
772
773
        # Check if global settings already exist
774
        settings = GlobalEmailSettings.query.first()
775
776
        if settings:
777
            # Update existing settings
778
            settings.email = email
779
            settings.password = password
780
            settings.smtp_server = smtp_server
781
        else:
782
            # Create new settings
783
            settings = GlobalEmailSettings(
784
                id=1,  # Enforce single row
785
                email=email,
786
                password=password,
787
                smtp_server=smtp_server
788
            )
789
            db.session.add(settings)
790
791
        db.session.commit()
792
        flash('Global email settings saved successfully')
793
        return redirect(url_for('main.settings'))
794
795
    return redirect(url_for('main.settings'))
796
797
798
@main.route('/manifest.json')
799
def serve_manifest():
800
    return send_file('manifest.json', mimetype='application/manifest+json')
801
802
803
@main.route('/sw.js')
804
def serve_sw():
805
    return send_file('sw.js', mimetype='application/javascript')
806