Passed
Push — master ( bda6ce...afc50f )
by Mihail
05:03
created

Content::actionIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace Apps\Controller\Front;
4
5
use Apps\ActiveRecord\ContentCategory;
6
use Apps\ActiveRecord\Content as ContentEntity;
7
use Apps\Model\Front\Content\EntityContentSearch;
8
use Extend\Core\Arch\FrontAppController;
9
use Ffcms\Core\App;
10
use Apps\Model\Front\Content\EntityCategoryList;
11
use Ffcms\Core\Exception\ForbiddenException;
12
use Ffcms\Core\Exception\NativeException;
13
use Ffcms\Core\Exception\NotFoundException;
14
use Apps\Model\Front\Content\EntityContentRead;
15
use Ffcms\Core\Helper\HTML\SimplePagination;
16
use Ffcms\Core\Helper\Type\Str;
17
use Suin\RSSWriter\Channel;
18
use Suin\RSSWriter\Feed;
19
use Suin\RSSWriter\Item;
20
use Ffcms\Core\Helper\Type\Arr;
21
use Apps\Model\Front\Content\FormNarrowContentUpdate;
22
use Apps\ActiveRecord\Content as ContentRecord;
23
24
/**
25
 * Class Content. Controller of content app - content and categories.
26
 * @package Apps\Controller\Front
27
 */
28
class Content extends FrontAppController
29
{
30
    const TAG_PER_PAGE = 50;
31
32
    const EVENT_CONTENT_READ = 'content.read';
33
    const EVENT_RSS_READ = 'content.rss.read';
34
    const EVENT_CONTENT_LIST = 'content.list';
35
    const EVENT_TAG_LIST = 'content.tags';
36
37
    /**
38
     * Index is forbidden
39
     * @throws NotFoundException
40
     */
41
    public function actionIndex()
42
    {
43
        throw new NotFoundException();
44
    }
45
46
    /**
47
     * List category content
48
     * @throws NotFoundException
49
     * @throws \Ffcms\Core\Exception\SyntaxException
50
     * @throws \Ffcms\Core\Exception\NativeException
51
     * @return string
52
     */
53
    public function actionList()
54
    {
55
        $path = App::$Request->getPathWithoutControllerAction();
56
        $configs = $this->getConfigs();
57
        $page = (int)App::$Request->query->get('page', 0);
58
        $sort = (string)App::$Request->query->get('sort', 'newest');
59
        $itemCount = (int)$configs['itemPerCategory'];
60
61
        // build special model with content list and category list information
62
        $model = new EntityCategoryList($path, $configs, $page, $sort);
63
64
        // prepare query string (?a=b) for pagination if sort is defined
65
        $sortQuery = null;
66
        if (Arr::in($sort, ['rating', 'views'])) {
67
            $sortQuery = ['sort' => $sort];
68
        }
69
70
        // build pagination
71
        $pagination = new SimplePagination([
72
            'url' => ['content/list', $path, null, $sortQuery],
73
            'page' => $page,
74
            'step' => $itemCount,
75
            'total' => $model->getContentCount()
76
        ]);
77
78
        // define list event
79
        App::$Event->run(static::EVENT_CONTENT_LIST, [
80
            'model' => $model
81
        ]);
82
83
        // draw response view
84
        return App::$View->render('list', [
85
            'model' => $model,
86
            'pagination' => $pagination,
87
            'configs' => $configs,
88
        ]);
89
    }
90
91
    /**
92
     * Show content item
93
     * @throws NotFoundException
94
     * @throws \Ffcms\Core\Exception\SyntaxException
95
     * @throws \Ffcms\Core\Exception\NativeException
96
     * @return string
97
     */
98
    public function actionRead()
99
    {
100
        // get raw path without controller-action
101
        $rawPath = App::$Request->getPathWithoutControllerAction();
102
        $arrayPath = explode('/', $rawPath);
103
        // get category and content item path as string
104
        $contentPath = array_pop($arrayPath);
105
        $categoryPath = implode('/', $arrayPath);
106
107
        // try to find category object by string pathway
108
        $categoryRecord = ContentCategory::getByPath($categoryPath);
109
110
        // if no categories are available for this path - throw exception
111
        if ($categoryRecord === null || $categoryRecord->count() < 1) {
112
            throw new NotFoundException();
113
        }
114
115
        // try to find content entity record
116
        $contentRecord = ContentEntity::where('path', '=', $contentPath)->where('category_id', '=', $categoryRecord->id);
117
        $trash = false;
118
119
        // if no entity is founded for this path lets try to find on trashed
120
        if ($contentRecord === null || $contentRecord->count() !== 1) {
121
            // check if user can access to content list on admin panel
122 View Code Duplication
            if (!App::$User->isAuth() || !App::$User->identity()->getRole()->can('Admin/Content/Index')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
123
                throw new NotFoundException();
124
            }
125
            // lets try to find in trashed
126
            $contentRecord->withTrashed();
127
            // no way, lets throw exception
128
            if ($contentRecord->count() !== 1) {
129
                throw new NotFoundException();
130
            }
131
            // set trashed marker for this item
132
            $trash = true;
133
        }
134
135
        // lets init entity model for content transfer to view
136
        $model = new EntityContentRead($categoryRecord, $contentRecord->first());
0 ignored issues
show
Documentation introduced by
$contentRecord->first() is of type object<Ffcms\Core\Arch\ActiveModel>|null, but the function expects a object<Apps\ActiveRecord\Content>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
137
        $search = null;
138
        // check if similar search is enabled for item category
139
        if ((int)$model->getCategory()->getProperty('showSimilar') === 1 && $trash === false) {
140
            $search = new EntityContentSearch($model->title, $model->id);
141
        }
142
143
        // define read event
144
        App::$Event->run(static::EVENT_CONTENT_READ, [
145
            'model' => $model
146
        ]);
147
148
        // render view output
149
        return App::$View->render('read', [
150
            'model' => $model,
151
            'search' => $search,
152
            'trash' => $trash,
153
            'configs' => $this->getConfigs()
154
        ]);
155
    }
156
157
    /**
158
     * List latest by created_at content items contains tag name
159
     * @param string $tagName
160
     * @return string
161
     * @throws NotFoundException
162
     * @throws \Ffcms\Core\Exception\SyntaxException
163
     * @throws \Ffcms\Core\Exception\NativeException
164
     */
165
    public function actionTag($tagName)
166
    {
167
        $configs = $this->getConfigs();
168
        // check if tags is enabled
169
        if ((int)$configs['keywordsAsTags'] !== 1) {
170
            throw new NotFoundException(__('Tag system is disabled'));
171
        }
172
173
        // remove spaces and other shits
174
        $tagName = trim($tagName);
175
176
        // check if tag is not empty
177
        if (Str::likeEmpty($tagName) || Str::length($tagName) < 2) {
178
            throw new NotFoundException(__('Tag is empty or is too short!'));
179
        }
180
181
        // get equal rows order by creation date
182
        $records = ContentEntity::where('meta_keywords', 'like', '%' . $tagName . '%')->orderBy('created_at', 'DESC')->take(self::TAG_PER_PAGE);
183
        // check if result is not empty
184
        if ($records->count() < 1) {
185
            throw new NotFoundException(__('Nothing founded'));
186
        }
187
188
        // define tag list event
189
        App::$Event->run(static::EVENT_TAG_LIST, [
190
            'records' => $records
191
        ]);
192
193
        // render response
194
        return App::$View->render('tag', [
195
            'records' => $records->get(),
196
            'tag' => App::$Security->strip_tags($tagName)
197
        ]);
198
    }
199
200
    /**
201
     * Display rss feeds from content category
202
     * @return string
203
     * @throws ForbiddenException
204
     */
205
    public function actionRss()
206
    {
207
        $path = App::$Request->getPathWithoutControllerAction();
208
        $configs = $this->getConfigs();
209
210
        // build model data
211
        $model = new EntityCategoryList($path, $configs, 0);
212
        // remove global layout
213
        $this->layout = null;
214
215
        // check if rss display allowed for this category
216 View Code Duplication
        if ((int)$model->category['configs']['showRss'] !== 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
217
            throw new ForbiddenException(__('Rss feed is disabled for this category'));
218
        }
219
220
        // initialize rss feed objects
221
        $feed = new Feed();
222
        $channel = new Channel();
223
224
        // set channel data
225
        $channel->title($model->category['title'])
0 ignored issues
show
Bug introduced by
It seems like $model->category['title'] can also be of type array; however, Suin\RSSWriter\Channel::title() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
226
            ->description($model->category['description'])
0 ignored issues
show
Bug introduced by
It seems like $model->category['description'] can also be of type array; however, Suin\RSSWriter\Channel::description() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
227
            ->url(App::$Alias->baseUrl . '/content/list/' . $model->category['path'])
228
            ->appendTo($feed);
229
230
        // add content data
231
        if ($model->getContentCount() > 0) {
232
            foreach ($model->items as $row) {
233
                $item = new Item();
234
                // add title, short text, url
235
                $item->title($row['title'])
0 ignored issues
show
Bug introduced by
It seems like $row['title'] can also be of type array; however, Suin\RSSWriter\Item::title() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
236
                    ->description($row['text'])
237
                    ->url(App::$Alias->baseUrl . $row['uri']);
238
                // add poster
239
                if ($row['thumb'] !== null) {
240
                    $item->enclosure(App::$Alias->scriptUrl . $row['thumb'], $row['thumbSize'], 'image/jpeg');
241
                }
242
243
                // append response to channel
244
                $item->appendTo($channel);
245
            }
246
        }
247
        // define rss read event
248
        App::$Event->run(static::EVENT_RSS_READ, [
249
            'model' => $model,
250
            'feed' => $feed,
251
            'channel' => $channel
252
        ]);
253
254
        // render response from feed object
255
        return $feed->render();
256
    }
257
258
    public function actionMy()
259
    {
260
        // check if user is auth
261
        if (!App::$User->isAuth()) {
262
            throw new ForbiddenException(__('Only authorized users can manage content'));
263
        }
264
265
        // check if user add enabled
266
        $configs = $this->getConfigs();
267
        if (!(bool)$configs['userAdd']) {
268
            throw new NotFoundException(__('User add is disabled'));
269
        }
270
271
        // prepare query
272
        $page = (int)App::$Request->query->get('page', 0);
273
        $offset = $page * 10;
274
        $query = ContentRecord::where('author_id', '=', App::$User->identity()->getId());
275
276
        // build pagination
277
        $pagination = new SimplePagination([
278
            'url' => ['content/my'],
279
            'page' => $page,
280
            'step' => 10,
281
            'total' => $query->count()
282
        ]);
283
284
        // build records object
285
        $records = $query->skip($offset)->take(10)->orderBy('id', 'DESC')->get();
286
287
        // render output view
288
        return App::$View->render('my', [
289
            'records' => $records,
290
            'pagination' => $pagination
291
        ]);
292
    }
293
294
    /**
295
     * Update personal content items or add new content item
296
     * @param null|int $id
297
     * @return string
298
     * @throws ForbiddenException
299
     * @throws NotFoundException
300
     * @throws NativeException
301
     * @throws \Ffcms\Core\Exception\SyntaxException
302
     */
303
    public function actionUpdate($id = null)
304
    {
305
        // check if user is auth
306
        if (!App::$User->isAuth()) {
307
            throw new ForbiddenException(__('Only authorized users can add content'));
308
        }
309
310
        // check if user add enabled
311
        $configs = $this->getConfigs();
312
        if (!(bool)$configs['userAdd']) {
313
            throw new NotFoundException(__('User add is disabled'));
314
        }
315
316
        // find record in db
317
        $record = ContentRecord::findOrNew($id);
318
        $new = $record->id === null;
319
320
        // reject edit published items and items from other authors
321 View Code Duplication
        if (($new === false && (int)$record->author_id !== App::$User->identity()->getId()) || (int)$record->display === 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
            throw new ForbiddenException(__('You have no permissions to edit this content'));
323
        }
324
325
        // initialize model
326
        $model = new FormNarrowContentUpdate($record, $configs);
327
        if ($model->send() && $model->validate()) {
328
            $model->make();
329
            // if is new - make redirect to listing & add notify
330
            if ($new === true) {
331
                App::$Session->getFlashBag()->add('success', __('Content successfully added'));
332
                App::$Response->redirect('content/my');
333
            } else {
334
                App::$Session->getFlashBag()->add('success', __('Content successfully updated'));
335
            }
336
        }
337
338
        // render view output
339
        return App::$View->render('update', [
340
            'model' => $model,
341
            'configs' => $configs
342
        ]);
343
    }
344
}
345