Passed
Push — master ( 0cf6ea...c066d0 )
by Darko
09:55
created

AdminContentController::toggleStatus()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 24
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
dl 0
loc 24
rs 9.4555
c 0
b 0
f 0
cc 5
nc 4
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): \Illuminate\Routing\Redirector|RedirectResponse|\Illuminate\Contracts\Foundation\Application
150
    {
151
        if ($request->has('id')) {
152
            Content::query()->where('id', $request->input('id'))->delete();
153
        }
154
155
        $referrer = $request->server('HTTP_REFERER');
156
157
        return redirect()->to($referrer);
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

157
        return redirect()->to(/** @scrutinizer ignore-type */ $referrer);
Loading history...
158
    }
159
160
    /**
161
     * Add new content.
162
     */
163
    protected function addContent(array $data): int
164
    {
165
        // Normalize URL
166
        $data = $this->normalizeContentUrl($data);
167
168
        // If ordinal is 1, increment all existing ordinals
169
        if (($data['ordinal'] ?? 0) === 1) {
170
            Content::query()->where('ordinal', '>', 0)->increment('ordinal');
171
        }
172
173
        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...
174
            'role' => $data['role'] ?? Content::ROLE_EVERYONE,
175
            'title' => $data['title'] ?? '',
176
            'url' => $data['url'] ?? '/',
177
            'body' => $data['body'] ?? '',
178
            'metadescription' => $data['metadescription'] ?? '',
179
            'metakeywords' => $data['metakeywords'] ?? '',
180
            'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
181
            'status' => $data['status'] ?? Content::STATUS_ENABLED,
182
            'ordinal' => $data['ordinal'] ?? 0,
183
            'created_at' => now(),
184
            'updated_at' => now(),
185
        ]);
186
    }
187
188
    /**
189
     * Update existing content.
190
     */
191
    protected function updateContent(array $data): int
192
    {
193
        // Normalize URL
194
        $data = $this->normalizeContentUrl($data);
195
196
        return Content::query()
197
            ->where('id', $data['id'])
198
            ->update([
199
                'role' => $data['role'] ?? Content::ROLE_EVERYONE,
200
                'title' => $data['title'] ?? '',
201
                'url' => $data['url'] ?? '/',
202
                'body' => $data['body'] ?? '',
203
                'metadescription' => $data['metadescription'] ?? '',
204
                'metakeywords' => $data['metakeywords'] ?? '',
205
                'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
206
                'status' => $data['status'] ?? Content::STATUS_ENABLED,
207
                'ordinal' => $data['ordinal'] ?? 0,
208
                'updated_at' => now(),
209
            ]);
210
    }
211
212
    /**
213
     * Get content by ID for admin viewing.
214
     */
215
    protected function getContentById(int $id): ?Content
216
    {
217
        return Content::query()->find($id);
218
    }
219
220
    /**
221
     * Normalize content URL to ensure proper formatting.
222
     */
223
    protected function normalizeContentUrl(array $data): array
224
    {
225
        if (isset($data['url']) && $data['url'] !== '') {
226
            $url = $data['url'];
227
228
            // Check if URL is external (has protocol or is a domain pattern)
229
            $hasProtocol = str_starts_with($url, 'http://') || str_starts_with($url, 'https://');
230
            $isDomain = !str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url);
231
232
            // Only normalize internal URLs (those starting with / or root)
233
            if (!$hasProtocol && !$isDomain) {
234
                // Ensure internal URL starts with /
235
                if ($url !== '/' && !str_starts_with($url, '/')) {
236
                    $data['url'] = '/' . $url;
237
                }
238
239
                // Ensure internal URL ends with /
240
                if (!str_ends_with($data['url'], '/')) {
241
                    $data['url'] .= '/';
242
                }
243
            }
244
            // External URLs are left as-is
245
        }
246
247
        return $data;
248
    }
249
}
250