Issues (419)

app/Http/Controllers/InvitationController.php (3 issues)

1
<?php
2
3
namespace App\Http\Controllers;
4
5
use App\Models\Invitation;
6
use App\Models\Settings; // Added for registerstatus check
7
use App\Services\InvitationService;
8
use Illuminate\Http\JsonResponse;
9
use Illuminate\Http\RedirectResponse;
10
use Illuminate\Http\Request;
11
12
class InvitationController extends BasePageController
13
{
14
    protected InvitationService $invitationService;
15
16
    public function __construct(InvitationService $invitationService)
17
    {
18
        parent::__construct();
19
        $this->invitationService = $invitationService;
20
        $this->middleware('auth')->except(['show', 'accept']);
21
    }
22
23
    /**
24
     * Display a listing of the user's invitations
25
     */
26
    public function index(Request $request): void
27
    {
28
        $this->setPreferences();
29
30
        $inviteMode = (int) Settings::settingValue('registerstatus') === Settings::REGISTER_STATUS_INVITE;
31
        if (! $inviteMode) {
32
            // Invitations disabled: show informational message only, no queries.
33
            $this->smarty->assign([
34
                'invite_mode' => false,
35
                'status' => null,
36
                'stats' => [],
37
                'invitations' => [],
38
                'pagination_links' => null,
39
                'csrf_token' => csrf_token(),
40
            ]);
41
42
            $meta_title = 'Invitations Disabled';
43
            $meta_keywords = 'invitations,disabled';
44
            $meta_description = 'Invitations are currently disabled on this site.';
45
46
            $content = $this->smarty->fetch('invitations_index.tpl');
47
            $this->smarty->assign([
48
                'content' => $content,
49
                'meta_title' => $meta_title,
50
                'meta_keywords' => $meta_keywords,
51
                'meta_description' => $meta_description,
52
            ]);
53
            $this->pagerender();
54
55
            return;
56
        }
57
58
        $status = $request->get('status');
59
        $user = auth()->user();
60
61
        $invitations = $this->invitationService->getUserInvitations($user->id, $status);
62
        $stats = $this->invitationService->getUserInvitationStats($user->id);
63
64
        // Convert paginated results to array for Smarty
65
        $invitationsArray = [];
66
        foreach ($invitations as $invitation) {
67
            $invitationData = $invitation->toArray();
68
69
            // Add related user data
70
            if ($invitation->usedBy) {
71
                $invitationData['used_by_user'] = $invitation->usedBy->toArray();
72
            }
73
74
            // Convert timestamps for Smarty
75
            $invitationData['created_at'] = strtotime($invitation->created_at);
76
            $invitationData['expires_at'] = strtotime($invitation->expires_at);
77
            if ($invitation->used_at) {
78
                $invitationData['used_at'] = strtotime($invitation->used_at);
79
            }
80
81
            $invitationsArray[] = $invitationData;
82
        }
83
84
        $this->smarty->assign([
85
            'invite_mode' => true,
86
            'invitations' => $invitationsArray,
87
            'stats' => $stats,
88
            'status' => $status,
89
            'pagination_links' => $invitations->links(),
90
            'csrf_token' => csrf_token(),
91
        ]);
92
93
        // Set meta information
94
        $meta_title = 'My Invitations';
95
        $meta_keywords = 'invitations,invite,users,manage';
96
        $meta_description = 'Manage your sent invitations and send new invitations to friends';
97
98
        // Fetch the template content
99
        $content = $this->smarty->fetch('invitations_index.tpl');
100
101
        // Assign content and meta data for final rendering
102
        $this->smarty->assign([
103
            'content' => $content,
104
            'meta_title' => $meta_title,
105
            'meta_keywords' => $meta_keywords,
106
            'meta_description' => $meta_description,
107
        ]);
108
109
        // Render the page with proper styling
110
        $this->pagerender();
111
    }
112
113
    /**
114
     * Show the form for creating a new invitation
115
     */
116
    public function create(): void
117
    {
118
        $this->setPreferences();
119
120
        $inviteMode = (int) Settings::settingValue('registerstatus') === Settings::REGISTER_STATUS_INVITE;
121
        if (! $inviteMode) {
122
            $this->smarty->assign([
123
                'invite_mode' => false,
124
                'csrf_token' => csrf_token(),
125
            ]);
126
            $meta_title = 'Invitations Disabled';
127
            $meta_keywords = 'invitations,disabled';
128
            $meta_description = 'Invitations are currently disabled on this site.';
129
            $content = $this->smarty->fetch('invitations_create.tpl');
130
            $this->smarty->assign([
131
                'content' => $content,
132
                'meta_title' => $meta_title,
133
                'meta_keywords' => $meta_keywords,
134
                'meta_description' => $meta_description,
135
            ]);
136
            $this->pagerender();
137
138
            return;
139
        }
140
141
        $user = auth()->user();
142
143
        // Calculate available invites (total - active pending invitations)
144
        $activeInvitations = Invitation::where('invited_by', $user->id)
145
            ->where('is_active', true)
146
            ->where('used_at', null)
147
            ->where('expires_at', '>', now())
148
            ->count();
149
150
        $availableInvites = $user->invites - $activeInvitations;
151
        $this->smarty->assign([
152
            'invite_mode' => true,
153
            'user_roles' => config('nntmux.user_roles', []),
154
            'csrf_token' => csrf_token(),
155
            'old' => old(),
156
            'errors' => session('errors') ? session('errors')->getBag('default')->getMessages() : [],
157
            'user_invites_left' => $availableInvites,
158
            'user_invites_total' => $user->invites,
159
            'user_invites_pending' => $activeInvitations,
160
            'can_send_invites' => $availableInvites > 0,
161
        ]);
162
163
        // Set meta information
164
        $meta_title = 'Send New Invitation';
165
        $meta_keywords = 'invitation,invite,send,new,user';
166
        $meta_description = 'Send a new invitation to invite someone to join the site';
167
168
        // Fetch the template content
169
        $content = $this->smarty->fetch('invitations_create.tpl');
170
171
        // Assign content and meta data for final rendering
172
        $this->smarty->assign([
173
            'content' => $content,
174
            'meta_title' => $meta_title,
175
            'meta_keywords' => $meta_keywords,
176
            'meta_description' => $meta_description,
177
        ]);
178
179
        // Render the page with proper styling
180
        $this->pagerender();
181
    }
182
183
    /**
184
     * Store a newly created invitation
185
     */
186
    public function store(Request $request): RedirectResponse
187
    {
188
        if ((int) Settings::settingValue('registerstatus') !== Settings::REGISTER_STATUS_INVITE) {
189
            return redirect()->route('invitations.index')->with('error', 'Invitations are currently disabled.');
190
        }
191
192
        $request->validate([
193
            'email' => 'required|email|unique:users,email',
194
            'expiry_days' => 'sometimes|integer|min:1|max:30',
195
            'role' => 'sometimes|integer|in:'.implode(',', array_keys(config('nntmux.user_roles', []))),
196
        ]);
197
198
        try {
199
            $expiryDays = $request->get('expiry_days', Invitation::DEFAULT_INVITE_EXPIRY_DAYS);
200
            $metadata = [];
201
202
            if ($request->has('role')) {
203
                $metadata['role'] = $request->get('role');
204
            }
205
206
            $invitation = $this->invitationService->createAndSendInvitation(
0 ignored issues
show
The assignment to $invitation is dead and can be removed.
Loading history...
207
                $request->email,
208
                auth()->id(),
209
                $expiryDays,
210
                $metadata
211
            );
212
213
            return redirect()->route('invitations.index')
214
                ->with('success', 'Invitation sent successfully to '.$request->email);
215
216
        } catch (\Exception $e) {
217
            return redirect()->back()
218
                ->withInput()
219
                ->with('error', $e->getMessage());
220
        }
221
    }
222
223
    /**
224
     * Display the specified invitation
225
     */
226
    public function show(string $token): void
227
    {
228
        // For public invitation page, we need to handle both logged in and guest users
229
        if (auth()->check()) {
230
            $this->setPreferences();
231
        } else {
232
            // Set up basic Smarty configuration for guests
233
            $this->smarty->setTemplateDir([
234
                'user' => config('ytake-laravel-smarty.template_path').'/Gentele',
235
                'shared' => config('ytake-laravel-smarty.template_path').'/shared',
236
                'default' => config('ytake-laravel-smarty.template_path').'/Gentele',
237
            ]);
238
239
            $this->smarty->assign([
240
                'isadmin' => false,
241
                'ismod' => false,
242
                'loggedin' => false,
243
                'theme' => 'Gentele',
244
                'site' => $this->settings,
245
            ]);
246
        }
247
248
        $preview = $this->invitationService->getInvitationPreview($token);
249
250
        // Convert timestamps for Smarty
251
        if ($preview && isset($preview['expires_at'])) {
252
            $preview['expires_at'] = strtotime($preview['expires_at']);
253
        }
254
255
        // Add role name if role is set
256
        if ($preview && isset($preview['metadata']['role'])) {
257
            $roles = config('nntmux.user_roles', []);
258
            $preview['role_name'] = $roles[$preview['metadata']['role']] ?? 'Default';
259
        }
260
261
        $this->smarty->assign([
262
            'preview' => $preview,
263
            'token' => $token,
264
        ]);
265
266
        // Set meta information
267
        $meta_title = $preview ? 'Invitation to Join' : 'Invalid Invitation';
268
        $meta_keywords = 'invitation,join,register,signup';
269
        $meta_description = $preview ? 'You have been invited to join our community' : 'This invitation link is invalid or expired';
270
271
        // Fetch the template content
272
        $content = $this->smarty->fetch('invitations_show.tpl');
273
274
        // Assign content and meta data for final rendering
275
        $this->smarty->assign([
276
            'content' => $content,
277
            'meta_title' => $meta_title,
278
            'meta_keywords' => $meta_keywords,
279
            'meta_description' => $meta_description,
280
        ]);
281
282
        // Render the page with proper styling
283
        $this->pagerender();
284
    }
285
286
    /**
287
     * Resend an invitation
288
     */
289
    public function resend(int $id): RedirectResponse
290
    {
291
        if ((int) Settings::settingValue('registerstatus') !== Settings::REGISTER_STATUS_INVITE) {
292
            return redirect()->route('invitations.index')->with('error', 'Invitations are currently disabled.');
293
        }
294
295
        try {
296
            $invitation = Invitation::findOrFail($id);
297
298
            // Check if user owns this invitation
299
            if ($invitation->invited_by !== auth()->id()) {
0 ignored issues
show
The property invited_by does not seem to exist on Illuminate\Database\Eloq...gHasThroughRelationship.
Loading history...
300
                abort(403, 'Unauthorized');
301
            }
302
303
            $this->invitationService->resendInvitation($id);
304
305
            return redirect()->route('invitations.index')
306
                ->with('success', 'Invitation resent successfully');
307
308
        } catch (\Exception $e) {
309
            return redirect()->back()
310
                ->with('error', $e->getMessage());
311
        }
312
    }
313
314
    /**
315
     * Cancel an invitation
316
     */
317
    public function destroy(int $id): RedirectResponse
318
    {
319
        if ((int) Settings::settingValue('registerstatus') !== Settings::REGISTER_STATUS_INVITE) {
320
            return redirect()->route('invitations.index')->with('error', 'Invitations are currently disabled.');
321
        }
322
323
        try {
324
            $invitation = Invitation::findOrFail($id);
325
326
            // Check if user owns this invitation
327
            if ($invitation->invited_by !== auth()->id()) {
0 ignored issues
show
The property invited_by does not seem to exist on Illuminate\Database\Eloq...gHasThroughRelationship.
Loading history...
328
                abort(403, 'Unauthorized');
329
            }
330
331
            $this->invitationService->cancelInvitation($id);
332
333
            return redirect()->route('invitations.index')
334
                ->with('success', 'Invitation cancelled successfully');
335
336
        } catch (\Exception $e) {
337
            return redirect()->back()
338
                ->with('error', $e->getMessage());
339
        }
340
    }
341
342
    /**
343
     * Get invitation statistics (API endpoint)
344
     */
345
    public function stats(): JsonResponse
346
    {
347
        if ((int) Settings::settingValue('registerstatus') !== Settings::REGISTER_STATUS_INVITE) {
348
            return response()->json(['message' => 'Invitations are disabled'], 404);
349
        }
350
351
        $stats = $this->invitationService->getUserInvitationStats(auth()->id());
352
353
        return response()->json($stats);
354
    }
355
356
    /**
357
     * Clean up expired invitations (admin only)
358
     */
359
    public function cleanup(): JsonResponse
360
    {
361
        // Check if user is admin
362
        if (! auth()->user()->hasRole('admin')) {
363
            abort(403, 'Unauthorized');
364
        }
365
366
        $cleanedCount = $this->invitationService->cleanupExpiredInvitations();
367
368
        return response()->json([
369
            'message' => "Cleaned up {$cleanedCount} expired invitations",
370
            'count' => $cleanedCount,
371
        ]);
372
    }
373
}
374