Passed
Push — master ( 67ec34...2ff9ea )
by Mihail
05:25
created

FormContentUpdate::generateCommentHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 11
Ratio 100 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
nc 2
nop 0
dl 11
loc 11
rs 9.4285
c 1
b 0
f 0
1
<?php
2
3
namespace Apps\Model\Admin\Content;
4
5
use Apps\ActiveRecord\Content;
6
use Apps\ActiveRecord\ContentCategory;
7
use Ffcms\Core\App;
8
use Ffcms\Core\Arch\Model;
9
use Ffcms\Core\Helper\Date;
10
use Ffcms\Core\Helper\FileSystem\Directory;
11
use Ffcms\Core\Helper\FileSystem\File;
12
use Ffcms\Core\Helper\Type\Integer;
13
use Ffcms\Core\Helper\Type\Obj;
14
use Ffcms\Core\Helper\Serialize;
15
use Ffcms\Core\Helper\Type\Str;
16
use Apps\ActiveRecord\ContentTag;
17
18
class FormContentUpdate extends Model
19
{
20
    public $title = [];
21
    public $text = [];
22
    public $path;
23
    public $poster;
24
    public $categoryId;
25
    public $authorId;
26
    public $metaTitle;
27
    public $metaKeywords = [];
28
    public $metaDescription = [];
29
    public $display = '1';
30
    public $source;
31
    public $addRating = 0;
32
    public $createdAt;
33
34
    public $galleryFreeId;
35
36
37
    private $_content;
38
    private $_new = false;
39
40
    public function __construct(Content $content)
41
    {
42
        $this->_content = $content;
43
        parent::__construct();
44
    }
45
46
    /**
47
    * Set model properties from active record data
48
    */
49
    public function before()
50
    {
51
        // is new item?
52
        if ($this->_content->id === null) {
53
            $this->_new = true;
54
            if (null === $this->galleryFreeId) {
55
                $this->galleryFreeId = '_tmp_' . Str::randomLatin(mt_rand(16, 32));
56
            }
57
            if (null === $this->authorId) {
58
                $this->authorId = App::$User->identity()->getId();
59
            }
60
            if (null === $this->categoryId) {
61
                $this->categoryId = 1;
62
            }
63
        } else { // is edit of exist item? define available data
64
            $this->title = Serialize::decode($this->_content->title);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Ffcms\Core\Helper\Seria...$this->_content->title) can also be of type string. However, the property $title is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
65
            $this->text = Serialize::decode($this->_content->text);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Ffcms\Core\Helper\Seria...($this->_content->text) can also be of type string. However, the property $text is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
66
            $this->path = $this->_content->path;
67
            $this->poster = $this->_content->poster;
68
            $this->categoryId = $this->_content->category_id;
69
            $this->authorId = $this->_content->author_id;
70
            $this->metaTitle = Serialize::decode($this->_content->meta_title);
71
            $this->metaKeywords = Serialize::decode($this->_content->meta_keywords);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Ffcms\Core\Helper\Seria...content->meta_keywords) can also be of type string. However, the property $metaKeywords is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
72
            $this->metaDescription = Serialize::decode($this->_content->meta_description);
0 ignored issues
show
Documentation Bug introduced by
It seems like \Ffcms\Core\Helper\Seria...tent->meta_description) can also be of type string. However, the property $metaDescription is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
73
            $this->display = $this->_content->display;
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->_content->display can also be of type integer. However, the property $display is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
74
            $this->source  = $this->_content->source;
75
            $this->createdAt = Date::convertToDatetime($this->_content->created_at, Date::FORMAT_TO_HOUR);
0 ignored issues
show
Bug introduced by
It seems like $this->_content->created_at can also be of type boolean; however, Ffcms\Core\Helper\Date::convertToDatetime() does only seem to accept string|integer, 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...
76
            $this->galleryFreeId = $this->_content->id;
77
        }
78
    }
79
80
    /**
81
     * Validation rules
82
     */
83
    public function rules()
84
    {
85
        $res =  [
86
            ['title.' . App::$Request->getLanguage(), 'required'],
87
            ['text.' . App::$Request->getLanguage(), 'required', null, true, true],
88
            ['text', 'used', null, true, true],
89
            ['path', 'reverse_match', '/[\/\'~`\!@#\$%\^&\*\(\)+=\{\}\[\]\|;:"\<\>,\?\\\]/'],
90
            [['path', 'categoryId', 'authorId', 'display', 'galleryFreeId', 'title'], 'required'],
91
            [['metaTitle', 'metaKeywords', 'metaDescription', 'poster', 'source', 'addRating', 'createdAt'], 'used'],
92
            [['addRating', 'authorId', 'display'], 'int'],
93
            ['display', 'in', ['0', '1']],
94
            ['categoryId', 'in', $this->categoryIds()],
95
            ['path', '\Apps\Model\Admin\Content\FormContentUpdate::validatePath'],
96
            ['authorId', '\App::$User::isExist']
97
        ];
98
99 View Code Duplication
        foreach (App::$Properties->get('languages') as $lang) {
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...
100
            $res[] = ['title.' . $lang, 'length_max', 120, null, true, true];
101
            $res[] = ['keywords.' . $lang, 'length_max', 150];
102
            $res[] = ['description.' . $lang, 'length_max', 250];
103
        }
104
105
        return $res;
106
    }
107
108
    /**
109
    * Labels
110
    */
111
    public function labels()
112
    {
113
        return [
114
            'title' => __('Content title'),
115
            'text' => __('Content text'),
116
            'path' => __('Path slug'),
117
            'categoryId' => __('Category'),
118
            'metaTitle' => __('Meta title'),
119
            'metaKeywords' => __('Meta keywords'),
120
            'metaDescription' => __('Meta description'),
121
            'display' => __('Public display'),
122
            'createdAt' => __('Publish date'),
123
            'authorId' => __('Author identity'),
124
            'source' => __('Source URL'),
125
            'addRating' => __('Change rating'),
126
            'poster' => __('Poster')
127
        ];
128
    }
129
130
    /**
131
     * Save changes in database
132
     */
133
    public function save()
134
    {
135
        $this->_content->title = Serialize::encode($this->title);
136
        $this->_content->text = Serialize::encode($this->text);
137
        $this->_content->path = $this->path;
138
        $this->_content->category_id = $this->categoryId;
139
        $this->_content->author_id = $this->authorId;
140
        $this->_content->display = $this->display;
141
        $this->_content->meta_title = Serialize::encode($this->metaTitle);
142
        $this->_content->meta_keywords = Serialize::encode($this->metaKeywords);
143
        $this->_content->meta_description = Serialize::encode($this->metaDescription);
144
        $this->_content->source = $this->source;
145
        // check if rating is changed
146
        if ((int)$this->addRating !== 0) {
147
            $this->_content->rating += (int)$this->addRating;
148
        }
149
        // check if special comment hash is exist
150
        if ($this->_new || Str::length($this->_content->comment_hash) < 32) {
151
            $this->_content->comment_hash = $this->generateCommentHash();
152
        }
153
        // check if date is updated
154
        if (!Str::likeEmpty($this->createdAt) && !Str::startsWith('0000', Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP))) {
0 ignored issues
show
Bug introduced by
It seems like $this->createdAt can also be of type boolean; however, Ffcms\Core\Helper\Date::convertToDatetime() does only seem to accept string|integer, 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...
Bug introduced by
It seems like \Ffcms\Core\Helper\Date:...::FORMAT_SQL_TIMESTAMP) targeting Ffcms\Core\Helper\Date::convertToDatetime() can also be of type boolean; however, Ffcms\Core\Helper\Type\Str::startsWith() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
155
            $this->_content->created_at = Date::convertToDatetime($this->createdAt, Date::FORMAT_SQL_TIMESTAMP);
0 ignored issues
show
Bug introduced by
It seems like $this->createdAt can also be of type boolean; however, Ffcms\Core\Helper\Date::convertToDatetime() does only seem to accept string|integer, 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...
156
        }
157
158
        // save poster data
159
        $posterPath = '/upload/gallery/' . $this->galleryFreeId . '/orig/' . $this->poster;
160
        if (File::exist($posterPath)) {
161
            $this->_content->poster = $this->poster;
162
        }
163
164
        // get temporary gallery id
165
        $tmpGalleryId = $this->galleryFreeId;
166
167
        // save row
168
        $this->_content->save();
169
        
170
        // update tags data in special table (relation: content->content_tags = oneToMany)
171
        ContentTag::where('content_id', '=', $this->_content->id)->delete();
172
        $insertData = [];
173
        foreach ($this->metaKeywords as $lang => $keys) {
174
            // split keywords to tag array
175
            $tags = explode(',', $keys);
176
            foreach ($tags as $tag) {
177
                // cleanup tag from white spaces
178
                $tag = trim($tag);
179
                // prepare data to insert
180
                if (Str::length($tag) > 0) {
181
                    $insertData[] = [
182
                        'content_id' => $this->_content->id,
183
                        'lang' => $lang,
184
                        'tag' => $tag
185
                    ];
186
                }
187
            }
188
        }
189
        // insert tags 
190
        ContentTag::insert($insertData);
0 ignored issues
show
Bug introduced by
The method insert() does not exist on Apps\ActiveRecord\ContentTag. Did you maybe mean insertAndSetId()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
191
192
        // move files
193
        if ($tmpGalleryId !== $this->_content->id) {
194
            Directory::rename('/upload/gallery/' . $tmpGalleryId, $this->_content->id);
195
        }
196
    }
197
198
    /**
199
     * Get allowed category ids as array (string values for validation)
200
     * @return array
201
     */
202 View Code Duplication
    public function categoryIds()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
203
    {
204
        $data = ContentCategory::getSortedCategories();
205
        $response = [];
206
        foreach ($data as $key=>$val) {
207
            $response[] = (string)$key;
208
        }
209
        return $response;
210
    }
211
212
    /**
213
     * Validate path filter
214
     * @return bool
215
     */
216 View Code Duplication
    public function validatePath()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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
    {
218
        // try to find this item
219
        $find = Content::where('path', '=', $this->path);
220
        // exclude self id
221
        if ($this->_content->id !== null && Obj::isLikeInt($this->_content->id)) {
222
            $find->where('id', '!=', $this->_content->id);
223
        }
224
        // limit only current category id
225
        $find->where('category_id', '=', $this->categoryId);
226
227
        return $find->count() < 1;
228
    }
229
230
    /**
231
     * Generate random string for comment hash value
232
     * @return string
233
     */
234 View Code Duplication
    private function generateCommentHash()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
235
    {
236
        $hash = Str::randomLatinNumeric(mt_rand(32, 128));
237
        $find = Content::where('comment_hash', '=', $hash)->count();
238
        // hmmm, is always exist? Chance of it is TOOOO low, but lets recursion re-generate
239
        if ($find !== 0) {
240
            return $this->generateCommentHash();
241
        }
242
243
        return $hash;
244
    }
245
}