Passed
Push — master ( 4c8ff5...139cc6 )
by Darko
10:53
created

AdminContentController::destroy()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 30
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 30
rs 8.8333
c 0
b 0
f 0
cc 7
nc 5
nop 1
1
<?php
2
3
namespace App\Http\Controllers\Admin;
4
5
use App\Http\Controllers\BasePageController;
6
use App\Models\Content;
7
use App\Models\User;
8
use Illuminate\Http\RedirectResponse;
9
use Illuminate\Http\Request;
10
11
class AdminContentController extends BasePageController
12
{
13
    /**
14
     * Display list of all content.
15
     *
16
     * @throws \Exception
17
     */
18
    public function index()
19
    {
20
        $this->setAdminPrefs();
21
22
        $contentList = Content::query()
23
            ->orderByRaw('contenttype, COALESCE(ordinal, 1000000)')
24
            ->get();
25
26
        $this->viewData = array_merge($this->viewData, [
27
            'contentlist' => $contentList,
28
            'meta_title' => 'Content List',
29
            'title' => 'Content List',
30
        ]);
31
32
        return view('admin.content.index', $this->viewData);
33
    }
34
35
    /**
36
     * Show form to create or edit content.
37
     *
38
     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
39
     * @throws \Exception
40
     */
41
    public function create(Request $request)
42
    {
43
        $this->setAdminPrefs();
44
        $meta_title = 'Content Add';
45
46
        // Set the current action.
47
        $action = $request->input('action') ?? 'view';
48
49
        $content = [
50
            'id' => '',
51
            'title' => '',
52
            'url' => '',
53
            'body' => '',
54
            'metadescription' => '',
55
            'metakeywords' => '',
56
            'contenttype' => '',
57
            'status' => '',
58
            'ordinal' => '',
59
            'created_at' => '',
60
            'role' => '',
61
        ];
62
63
        switch ($action) {
64
            case 'add':
65
                $meta_title = 'Content Add';
66
                $content['status'] = Content::STATUS_ENABLED;
67
                $content['contenttype'] = Content::TYPE_USEFUL;
68
                break;
69
70
            case 'submit':
71
                // Validate and add or update content
72
                if ($request->missing('id') || empty($request->input('id'))) {
73
                    $returnid = $this->addContent($request->all());
74
                } else {
75
                    $this->updateContent($request->all());
76
                    $returnid = $request->input('id');
77
                }
78
79
                return redirect('admin/content-add?id='.$returnid);
80
81
            case 'view':
82
            default:
83
                if ($request->has('id')) {
84
                    $meta_title = 'Content Edit';
85
                    $id = $request->input('id');
86
                    $content = $this->getContentById($id);
87
                }
88
                break;
89
        }
90
91
        $contenttypelist = [
92
            Content::TYPE_USEFUL => 'Useful Link',
93
            Content::TYPE_INDEX => 'Homepage'
94
        ];
95
96
        $rolelist = [
97
            Content::ROLE_EVERYONE => 'Everyone',
98
            Content::ROLE_LOGGED_IN => 'Logged in Users',
99
            Content::ROLE_ADMIN => 'Admins'
100
        ];
101
102
        $this->viewData = array_merge($this->viewData, [
103
            'status_ids' => [Content::STATUS_ENABLED, Content::STATUS_DISABLED],
104
            'status_names' => ['Enabled', 'Disabled'],
105
            'yesno_ids' => [1, 0],
106
            'yesno_names' => ['Yes', 'No'],
107
            'contenttypelist' => $contenttypelist,
108
            'content' => $content,
109
            'rolelist' => $rolelist,
110
            'meta_title' => $meta_title,
111
            'title' => $meta_title,
112
        ]);
113
114
        return view('admin.content.add', $this->viewData);
115
    }
116
117
    /**
118
     * Toggle content status (enable/disable).
119
     */
120
    public function toggleStatus(Request $request)
121
    {
122
        if ($request->has('id')) {
123
            $content = Content::query()->find($request->input('id'));
124
125
            if ($content) {
126
                $newStatus = $content->status === Content::STATUS_ENABLED
127
                    ? Content::STATUS_DISABLED
128
                    : Content::STATUS_ENABLED;
129
130
                $content->update(['status' => $newStatus, 'updated_at' => now()]);
131
132
                return response()->json([
133
                    'success' => true,
134
                    'status' => $newStatus,
135
                    'message' => $newStatus === Content::STATUS_ENABLED ? 'Content enabled' : 'Content disabled'
136
                ]);
137
            }
138
        }
139
140
        return response()->json([
141
            'success' => false,
142
            'message' => 'Content not found'
143
        ], 404);
144
    }
145
146
    /**
147
     * Delete content by ID.
148
     */
149
    public function destroy(Request $request)
150
    {
151
        if ($request->has('id')) {
152
            $content = Content::query()->find($request->input('id'));
153
154
            if ($content) {
155
                $content->delete();
156
157
                // If AJAX request, return JSON
158
                if ($request->wantsJson() || $request->ajax()) {
159
                    return response()->json([
160
                        'success' => true,
161
                        'message' => 'Content deleted successfully'
162
                    ]);
163
                }
164
165
                return redirect()->route('admin.content-list')->with('success', 'Content deleted successfully');
166
            }
167
168
            // If AJAX request, return error JSON
169
            if ($request->wantsJson() || $request->ajax()) {
170
                return response()->json([
171
                    'success' => false,
172
                    'message' => 'Content not found'
173
                ], 404);
174
            }
175
        }
176
177
        $referrer = $request->server('HTTP_REFERER');
178
        return redirect()->to($referrer)->with('error', 'Invalid request');
0 ignored issues
show
Bug introduced by
It seems like $referrer can also be of type array; however, parameter $path of Illuminate\Routing\Redirector::to() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

178
        return redirect()->to(/** @scrutinizer ignore-type */ $referrer)->with('error', 'Invalid request');
Loading history...
179
    }
180
181
    /**
182
     * Add new content.
183
     */
184
    protected function addContent(array $data): int
185
    {
186
        // Normalize URL
187
        $data = $this->normalizeContentUrl($data);
188
189
        // If ordinal is 1, increment all existing ordinals
190
        if (($data['ordinal'] ?? 0) === 1) {
191
            Content::query()->where('ordinal', '>', 0)->increment('ordinal');
192
        }
193
194
        return Content::query()->insertGetId([
0 ignored issues
show
Bug Best Practice introduced by
The expression return App\Models\Conten...'updated_at' => now())) could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
195
            'role' => $data['role'] ?? Content::ROLE_EVERYONE,
196
            'title' => $data['title'] ?? '',
197
            'url' => $data['url'] ?? '/',
198
            'body' => $data['body'] ?? '',
199
            'metadescription' => $data['metadescription'] ?? '',
200
            'metakeywords' => $data['metakeywords'] ?? '',
201
            'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
202
            'status' => $data['status'] ?? Content::STATUS_ENABLED,
203
            'ordinal' => $data['ordinal'] ?? 0,
204
            'created_at' => now(),
205
            'updated_at' => now(),
206
        ]);
207
    }
208
209
    /**
210
     * Update existing content.
211
     */
212
    protected function updateContent(array $data): int
213
    {
214
        // Normalize URL
215
        $data = $this->normalizeContentUrl($data);
216
217
        return Content::query()
218
            ->where('id', $data['id'])
219
            ->update([
220
                'role' => $data['role'] ?? Content::ROLE_EVERYONE,
221
                'title' => $data['title'] ?? '',
222
                'url' => $data['url'] ?? '/',
223
                'body' => $data['body'] ?? '',
224
                'metadescription' => $data['metadescription'] ?? '',
225
                'metakeywords' => $data['metakeywords'] ?? '',
226
                'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
227
                'status' => $data['status'] ?? Content::STATUS_ENABLED,
228
                'ordinal' => $data['ordinal'] ?? 0,
229
                'updated_at' => now(),
230
            ]);
231
    }
232
233
    /**
234
     * Get content by ID for admin viewing.
235
     */
236
    protected function getContentById(int $id): ?Content
237
    {
238
        return Content::query()->find($id);
239
    }
240
241
    /**
242
     * Normalize content URL to ensure proper formatting.
243
     */
244
    protected function normalizeContentUrl(array $data): array
245
    {
246
        if (isset($data['url']) && $data['url'] !== '') {
247
            $url = $data['url'];
248
249
            // Check if URL is external (has protocol or is a domain pattern)
250
            $hasProtocol = str_starts_with($url, 'http://') || str_starts_with($url, 'https://');
251
            $isDomain = !str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url);
252
253
            // Only normalize internal URLs (those starting with / or root)
254
            if (!$hasProtocol && !$isDomain) {
255
                // Ensure internal URL starts with /
256
                if ($url !== '/' && !str_starts_with($url, '/')) {
257
                    $data['url'] = '/' . $url;
258
                }
259
260
                // Ensure internal URL ends with /
261
                if (!str_ends_with($data['url'], '/')) {
262
                    $data['url'] .= '/';
263
                }
264
            }
265
            // External URLs are left as-is
266
        }
267
268
        return $data;
269
    }
270
}
271