Passed
Push — master ( 7dcfdb...0cf6ea )
by Darko
09:47
created

AdminContentController::index()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 9
dl 0
loc 15
rs 9.9666
c 0
b 0
f 0
cc 1
nc 1
nop 0
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
     * Delete content by ID.
119
     */
120
    public function destroy(Request $request): \Illuminate\Routing\Redirector|RedirectResponse|\Illuminate\Contracts\Foundation\Application
121
    {
122
        if ($request->has('id')) {
123
            Content::query()->where('id', $request->input('id'))->delete();
124
        }
125
126
        $referrer = $request->server('HTTP_REFERER');
127
128
        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

128
        return redirect()->to(/** @scrutinizer ignore-type */ $referrer);
Loading history...
129
    }
130
131
    /**
132
     * Add new content.
133
     */
134
    protected function addContent(array $data): int
135
    {
136
        // Normalize URL
137
        $data = $this->normalizeContentUrl($data);
138
139
        // If ordinal is 1, increment all existing ordinals
140
        if (($data['ordinal'] ?? 0) === 1) {
141
            Content::query()->where('ordinal', '>', 0)->increment('ordinal');
142
        }
143
144
        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...
145
            'role' => $data['role'] ?? Content::ROLE_EVERYONE,
146
            'title' => $data['title'] ?? '',
147
            'url' => $data['url'] ?? '/',
148
            'body' => $data['body'] ?? '',
149
            'metadescription' => $data['metadescription'] ?? '',
150
            'metakeywords' => $data['metakeywords'] ?? '',
151
            'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
152
            'status' => $data['status'] ?? Content::STATUS_ENABLED,
153
            'ordinal' => $data['ordinal'] ?? 0,
154
            'created_at' => now(),
155
            'updated_at' => now(),
156
        ]);
157
    }
158
159
    /**
160
     * Update existing content.
161
     */
162
    protected function updateContent(array $data): int
163
    {
164
        // Normalize URL
165
        $data = $this->normalizeContentUrl($data);
166
167
        return Content::query()
168
            ->where('id', $data['id'])
169
            ->update([
170
                'role' => $data['role'] ?? Content::ROLE_EVERYONE,
171
                'title' => $data['title'] ?? '',
172
                'url' => $data['url'] ?? '/',
173
                'body' => $data['body'] ?? '',
174
                'metadescription' => $data['metadescription'] ?? '',
175
                'metakeywords' => $data['metakeywords'] ?? '',
176
                'contenttype' => $data['contenttype'] ?? Content::TYPE_USEFUL,
177
                'status' => $data['status'] ?? Content::STATUS_ENABLED,
178
                'ordinal' => $data['ordinal'] ?? 0,
179
                'updated_at' => now(),
180
            ]);
181
    }
182
183
    /**
184
     * Get content by ID for admin viewing.
185
     */
186
    protected function getContentById(int $id): ?Content
187
    {
188
        return Content::query()->find($id);
189
    }
190
191
    /**
192
     * Normalize content URL to ensure proper formatting.
193
     */
194
    protected function normalizeContentUrl(array $data): array
195
    {
196
        if (isset($data['url']) && $data['url'] !== '') {
197
            $url = $data['url'];
198
199
            // Check if URL is external (has protocol or is a domain pattern)
200
            $hasProtocol = str_starts_with($url, 'http://') || str_starts_with($url, 'https://');
201
            $isDomain = !str_starts_with($url, '/') && preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*\.[a-zA-Z]{2,}/', $url);
202
203
            // Only normalize internal URLs (those starting with / or root)
204
            if (!$hasProtocol && !$isDomain) {
205
                // Ensure internal URL starts with /
206
                if ($url !== '/' && !str_starts_with($url, '/')) {
207
                    $data['url'] = '/' . $url;
208
                }
209
210
                // Ensure internal URL ends with /
211
                if (!str_ends_with($data['url'], '/')) {
212
                    $data['url'] .= '/';
213
                }
214
            }
215
            // External URLs are left as-is
216
        }
217
218
        return $data;
219
    }
220
}
221