Completed
Push — master ( b84cd2...e67b3f )
by Andrii
12:52
created

src/models/Thread.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * HiPanel tickets module
4
 *
5
 * @link      https://github.com/hiqdev/hipanel-module-ticket
6
 * @package   hipanel-module-ticket
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hipanel\modules\ticket\models;
12
13
use hipanel\behaviors\File;
14
use hipanel\helpers\Markdown;
15
use hipanel\modules\client\models\Client;
16
use stdClass;
17
use Yii;
18
use yii\helpers\HtmlPurifier;
19
use yii\web\NotFoundHttpException;
20
21
/**
22
 * Class Ticket.
23
 *
24
 * @property string $priority
25
 * @property string $responsible_id
26
 */
27
class Thread extends \hipanel\base\Model
28
{
29
    use \hipanel\base\ModelTrait;
30
31
    public static $i18nDictionary = 'hipanel:ticket';
32
33
    const STATE_OPEN = 'opened';
34
    const STATE_CLOSE = 'closed';
35
36
    const PRIORITY_HIGH = 'high';
37
38
    public $search_form;
39
40
    public function init()
41
    {
42
        $this->on(static::EVENT_BEFORE_INSERT, [$this, 'beforeChange']);
43
        $this->on(static::EVENT_BEFORE_UPDATE, [$this, 'beforeChange']);
44
    }
45
46
    public function beforeChange($event)
47
    {
48
        $this->prepareSpentTime();
49
        $this->prepareTopic();
50
51
        return true;
52
    }
53
54
    /**
55
     * @return array
56
     */
57 View Code Duplication
    public function behaviors()
0 ignored issues
show
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...
58
    {
59
        return [
60
            [
61
                'class' => File::class,
62
                'attribute' => 'file',
63
                'targetAttribute' => 'file_ids',
64
                'scenarios' => ['create', 'answer'],
65
            ],
66
        ];
67
    }
68
69
    /**
70
     * @return array
71
     */
72
    public function attributes()
73
    {
74
        return [
75
            'id',
76
            'subject',
77
            'state',
78
            'state_label',
79
            'author_email',
80
            'author',
81
            'author_id',
82
            'responsible_id',
83
            'responsible_email',
84
            'author_seller',
85
            'author_seller_id',
86
            'recipient_id',
87
            'recipient',
88
            'recipient_seller',
89
            'recipient_seller_id',
90
            'replier_id',
91
            'replier',
92
            'replier_seller',
93
            'replier_name',
94
            'responsible',
95
            'priority',
96
            'priority_label',
97
            'spent', 'spent_hours',
98
            'answer_count',
99
            'status',
100
            'reply_time',
101
            'create_time',
102
            'a_reply_time',
103
            'elapsed',
104
            'topics',
105
            'topic',
106
            'watchers',
107
            'watcher_ids',
108
            'watcher',
109
            'watcher_id',
110
            'add_tag_ids',
111
            'file_ids',
112
            'file',
113
            'message', // 'answer_message',
114
            'is_private',
115
116
            'anonym_name',
117
            'anonym_email',
118
            'anonym_seller',
119
120
            'lastanswer',
121
            'time',
122
            'add_watchers', 'del_watchers',
123
124
            'time_from',
125
            'time_till',
126
127
            'contact',
128
        ];
129
    }
130
131
    /**
132
     * {@inheritdoc}
133
     */
134
    public function rules()
135
    {
136
        $rules = [
137
            [['author_id', 'responsible_id'], 'integer'],
138
            [['subject', 'message'], 'required', 'on' => ['create']],
139
            [['subject'], 'string', 'min' => 3],
140
            [['id'], 'required', 'on' => ['answer', 'update-answer', 'open', 'close']],
141
            [['recipient_id'], 'required', 'when' => function () {
142
                return Yii::$app->user->can('support');
143
            }, 'on' => 'create'],
144
            [
145
                [
146
                    'topics',
147
                    'state',
148
                    'priority',
149
                    'responsible',
150
                    'recipient_id',
151
                    'watchers',
152
                    'spent', 'spent_hours',
153
                    'file_ids',
154
                ],
155
                'safe',
156
                'on' => 'create',
157
            ],
158
            [
159
                [
160
                    'message',
161
                    'topics', 'state', 'priority',
162
                    'responsible', 'recipient_id',
163
                    'watchers', 'add_watchers', 'del_watchers', 'watcher_ids',
164
                    'is_private',
165
                    'file_ids',
166
                    'spent', 'spent_hours',
167
                ],
168
                'safe',
169
                'on' => 'answer',
170
            ],
171
            [['state'], 'safe', 'on' => ['close', 'open']],
172
            // only client-side validation. Answer is actually possible without a message,
173
            // but does not make any sense.
174
            [['message'], 'required', 'on' => ['answer'], 'when' => function () {
175
                return false;
176
            }],
177
            [['id'], 'integer', 'on' => 'answer'],
178
            [['file'], 'file', 'maxFiles' => 15],
179
            [['lastanswer', 'create_time', 'recipient'], 'safe'],
180
            [['author', 'author_seller'], 'safe', 'when' => Yii::$app->user->can('support')],
181
        ];
182
183
        return $rules;
184
    }
185
186
    /**
187
     * {@inheritdoc}
188
     */
189
    public function attributeLabels()
190
    {
191
        return $this->mergeAttributeLabels([
192
            'author' => Yii::t('hipanel:ticket', 'Author'),
193
            'author_id' => Yii::t('hipanel:ticket', 'Author'),
194
            'recipient' => Yii::t('hipanel:ticket', 'Recipient'),
195
            'is_private' => Yii::t('hipanel:ticket', 'Make private'),
196
            'responsible' => Yii::t('hipanel:ticket', 'Assignee'),
197
            'responsible_id' => Yii::t('hipanel:ticket', 'Assignee'),
198
            'spent' => Yii::t('hipanel:ticket', 'Spent time'),
199
            'create_time' => Yii::t('hipanel:ticket', 'Created'),
200
            'a_reply_time' => Yii::t('hipanel:ticket', 'a_reply_time'),
201
            'file' => Yii::t('hipanel:ticket', 'Files'),
202
            'lastanswer' => Yii::t('hipanel:ticket', 'Last answer'),
203
            'author_seller' => Yii::t('hipanel:ticket', 'Seller'),
204
            'watcher_ids' => Yii::t('hipanel:ticket', 'Watchers'),
205
        ]);
206
    }
207
208
    public function getClient()
209
    {
210
        return $this->author;
211
    }
212
213
    public function getClient_id()
214
    {
215
        return $this->author_id;
216
    }
217
218
    public function getSeller()
219
    {
220
        return $this->author_seller;
221
    }
222
223
    public function getSeller_id()
224
    {
225
        return $this->author_seller_id;
226
    }
227
228
    public function getThreadUrl()
229
    {
230
        return ['@ticket/view', 'id' => $this->id];
231
    }
232
233
    public static function parseMessage($message)
234
    {
235
        $message = str_replace(["\n\r", "\r\r", "\r\n"], "\n", $message); // "\n\n",
236
        $message = Markdown::process($message);
237
        $message = HtmlPurifier::process($message, [
238
            'HTML.SafeObject' => true, // Allow safe HTML entities
239
            'Core.EscapeInvalidTags' => true, // Escape not allowed tags instead of stripping them
240
            'Core.LexerImpl' => 'DirectLex', // Do not try to close unclosed tags
241
        ]);
242
243
        return $message;
244
    }
245
246
    public function prepareSpentTime()
247
    {
248
        list($this->spent_hours, $this->spent) = explode(':', $this->spent, 2);
249
    }
250
251
    public function prepareTopic()
252
    {
253
        $this->topics = is_array($this->topics) ? implode(',', $this->topics) : $this->topics;
254
    }
255
256
    public function getWatchersLogin()
257
    {
258
        $results = [];
259
        foreach ((array)$this->watchers as $id => $watcher) {
260
            list($login, $email) = explode(' ', $watcher);
261
            $results[$id] = $login;
262
        }
263
264
        return $results;
265
    }
266
267
    public function xFormater(array $items)
268
    {
269
        $result = [];
270
        foreach ($items as $id => $label) {
271
            $object = new stdClass();
272
            $object->value = $id;
273
            $object->text = $label;
274
            $result[] = $object;
275
        }
276
277
        return $result;
278
    }
279
280
    public function getAnswers()
281
    {
282
        // TODO: redo API in order to have different `Thread` and `ThreadMessage` models
283
        return $this->hasMany(Answer::class, ['id' => 'id'])->joinWith('files')->indexBy('answer_id');
284
    }
285
286
    /**
287
     * Returns array of client types, that can be set as responsible for the thread.
288
     *
289
     * @return array
290
     */
291
    public static function getResponsibleClientTypes()
292
    {
293
        return [Client::TYPE_SELLER, Client::TYPE_ADMIN, Client::TYPE_MANAGER, Client::TYPE_OWNER];
294
    }
295
296
    /**
297
     * @param integer $id
298
     * @param bool $throwOnError whether to throw an exception when answer is not found in thread
299
     * @throws NotFoundHttpException
300
     * @return Answer
301
     */
302
    public function getAnswer($id, $throwOnError = true)
303
    {
304
        if (!isset($this->answers[$id]) && $throwOnError) {
305
            throw new NotFoundHttpException('Answer does not belong to this model');
306
        }
307
308
        return $this->answers[$id];
309
    }
310
311
    public function getMaxAnswerId()
312
    {
313
        if ($this->isRelationPopulated('answers')) {
314
            return max(array_keys($this->answers));
315
        }
316
317
        return $this->id;
318
    }
319
320
    public function scenarioActions()
321
    {
322
        return [
323
            'open' => 'answer',
324
            'close' => 'answer',
325
        ];
326
    }
327
328
    public function canSetSpent()
329
    {
330
        if (isset(Yii::$app->params['ticket.canSetSpent']) && is_callable(Yii::$app->params['ticket.canSetSpent'])) {
331
            return call_user_func(Yii::$app->params['ticket.canSetSpent'], $this);
332
        }
333
334
        return true;
335
    }
336
337
    public function isOpen()
338
    {
339
        return $this->state === self::STATE_OPEN;
340
    }
341
342
    public function isHighPriority(): bool
343
    {
344
        return $this->priority === self::PRIORITY_HIGH;
345
    }
346
347
    public function isUserAssigned(): bool
348
    {
349
        return $this->responsible_id === Yii::$app->getUser()->getId();
350
    }
351
}
352