Passed
Branch master (350f1b)
by Jan
04:53
created

AbstractLogEntry::getIDString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 2
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU Affero General Public License as published
9
 * by the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 * GNU Affero General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Affero General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 - 2020 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software; you can redistribute it and/or
29
 * modify it under the terms of the GNU General Public License
30
 * as published by the Free Software Foundation; either version 2
31
 * of the License, or (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU General Public License
39
 * along with this program; if not, write to the Free Software
40
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
41
 */
42
43
namespace App\Entity\LogSystem;
44
45
use App\Entity\Attachments\Attachment;
46
use App\Entity\Attachments\AttachmentType;
47
use App\Entity\Base\AbstractDBElement;
48
use App\Entity\Devices\Device;
49
use App\Entity\Devices\DevicePart;
50
use App\Entity\LabelSystem\LabelProfile;
51
use App\Entity\Parameters\AbstractParameter;
52
use App\Entity\Parts\Category;
53
use App\Entity\Parts\Footprint;
54
use App\Entity\Parts\Manufacturer;
55
use App\Entity\Parts\MeasurementUnit;
56
use App\Entity\Parts\Part;
57
use App\Entity\Parts\PartLot;
58
use App\Entity\Parts\Storelocation;
59
use App\Entity\Parts\Supplier;
60
use App\Entity\PriceInformations\Currency;
61
use App\Entity\PriceInformations\Orderdetail;
62
use App\Entity\PriceInformations\Pricedetail;
63
use App\Entity\UserSystem\Group;
64
use App\Entity\UserSystem\User;
65
use DateTime;
66
use Doctrine\ORM\Mapping as ORM;
67
use Psr\Log\LogLevel;
68
69
/**
70
 * This entity describes a entry in the event log.
71
 *
72
 * @ORM\Entity(repositoryClass="App\Repository\LogEntryRepository")
73
 * @ORM\Table("log")
74
 * @ORM\InheritanceType("SINGLE_TABLE")
75
 * @ORM\DiscriminatorColumn(name="type", type="smallint")
76
 * @ORM\DiscriminatorMap({
77
 *  1 = "UserLoginLogEntry",
78
 *  2 = "UserLogoutLogEntry",
79
 *  3 = "UserNotAllowedLogEntry",
80
 *  4 = "ExceptionLogEntry",
81
 *  5 = "ElementDeletedLogEntry",
82
 *  6 = "ElementCreatedLogEntry",
83
 *  7 = "ElementEditedLogEntry",
84
 *  8 = "ConfigChangedLogEntry",
85
 *  9 = "InstockChangedLogEntry",
86
 *  10 = "DatabaseUpdatedLogEntry",
87
 *  11 = "CollectionElementDeleted",
88
 *  12 = "SecurityEventLogEntry",
89
 * })
90
 */
91
abstract class AbstractLogEntry extends AbstractDBElement
92
{
93
    public const LEVEL_EMERGENCY = 0;
94
    public const LEVEL_ALERT = 1;
95
    public const LEVEL_CRITICAL = 2;
96
    public const LEVEL_ERROR = 3;
97
    public const LEVEL_WARNING = 4;
98
    public const LEVEL_NOTICE = 5;
99
    public const LEVEL_INFO = 6;
100
    public const LEVEL_DEBUG = 7;
101
102
    protected const TARGET_TYPE_NONE = 0;
103
    protected const TARGET_TYPE_USER = 1;
104
    protected const TARGET_TYPE_ATTACHEMENT = 2;
105
    protected const TARGET_TYPE_ATTACHEMENTTYPE = 3;
106
    protected const TARGET_TYPE_CATEGORY = 4;
107
    protected const TARGET_TYPE_DEVICE = 5;
108
    protected const TARGET_TYPE_DEVICEPART = 6;
109
    protected const TARGET_TYPE_FOOTPRINT = 7;
110
    protected const TARGET_TYPE_GROUP = 8;
111
    protected const TARGET_TYPE_MANUFACTURER = 9;
112
    protected const TARGET_TYPE_PART = 10;
113
    protected const TARGET_TYPE_STORELOCATION = 11;
114
    protected const TARGET_TYPE_SUPPLIER = 12;
115
    protected const TARGET_TYPE_PARTLOT = 13;
116
    protected const TARGET_TYPE_CURRENCY = 14;
117
    protected const TARGET_TYPE_ORDERDETAIL = 15;
118
    protected const TARGET_TYPE_PRICEDETAIL = 16;
119
    protected const TARGET_TYPE_MEASUREMENTUNIT = 17;
120
    protected const TARGET_TYPE_PARAMETER = 18;
121
    protected const TARGET_TYPE_LABEL_PROFILE = 19;
122
123
    /**
124
     * @var array This const is used to convert the numeric level to a PSR-3 compatible log level
125
     */
126
    protected const LEVEL_ID_TO_STRING = [
127
        self::LEVEL_EMERGENCY => LogLevel::EMERGENCY,
128
        self::LEVEL_ALERT => LogLevel::ALERT,
129
        self::LEVEL_CRITICAL => LogLevel::CRITICAL,
130
        self::LEVEL_ERROR => LogLevel::ERROR,
131
        self::LEVEL_WARNING => LogLevel::WARNING,
132
        self::LEVEL_NOTICE => LogLevel::NOTICE,
133
        self::LEVEL_INFO => LogLevel::INFO,
134
        self::LEVEL_DEBUG => LogLevel::DEBUG,
135
    ];
136
137
    protected const TARGET_CLASS_MAPPING = [
138
        self::TARGET_TYPE_USER => User::class,
139
        self::TARGET_TYPE_ATTACHEMENT => Attachment::class,
140
        self::TARGET_TYPE_ATTACHEMENTTYPE => AttachmentType::class,
141
        self::TARGET_TYPE_CATEGORY => Category::class,
142
        self::TARGET_TYPE_DEVICE => Device::class,
143
        self::TARGET_TYPE_DEVICEPART => DevicePart::class,
144
        self::TARGET_TYPE_FOOTPRINT => Footprint::class,
145
        self::TARGET_TYPE_GROUP => Group::class,
146
        self::TARGET_TYPE_MANUFACTURER => Manufacturer::class,
147
        self::TARGET_TYPE_PART => Part::class,
148
        self::TARGET_TYPE_STORELOCATION => Storelocation::class,
149
        self::TARGET_TYPE_SUPPLIER => Supplier::class,
150
        self::TARGET_TYPE_PARTLOT => PartLot::class,
151
        self::TARGET_TYPE_CURRENCY => Currency::class,
152
        self::TARGET_TYPE_ORDERDETAIL => Orderdetail::class,
153
        self::TARGET_TYPE_PRICEDETAIL => Pricedetail::class,
154
        self::TARGET_TYPE_MEASUREMENTUNIT => MeasurementUnit::class,
155
        self::TARGET_TYPE_PARAMETER => AbstractParameter::class,
156
        self::TARGET_TYPE_LABEL_PROFILE => LabelProfile::class,
157
    ];
158
159
    /** @var User The user which has caused this log entry
160
     * @ORM\ManyToOne(targetEntity="App\Entity\UserSystem\User", fetch="EAGER")
161
     * @ORM\JoinColumn(name="id_user", nullable=false)
162
     */
163
    protected $user;
164
165
    /** @var DateTime The datetime the event associated with this log entry has occured.
166
     * @ORM\Column(type="datetime", name="datetime")
167
     */
168
    protected $timestamp;
169
170
    /** @var int The priority level of the associated level. 0 is highest, 7 lowest
171
     * @ORM\Column(type="integer", name="level", columnDefinition="TINYINT(4) NOT NULL")
172
     */
173
    protected $level;
174
175
    /** @var int The ID of the element targeted by this event
176
     * @ORM\Column(name="target_id", type="integer", nullable=false)
177
     */
178
    protected $target_id = 0;
179
180
    /** @var int The Type of the targeted element
181
     * @ORM\Column(name="target_type", type="smallint", nullable=false)
182
     */
183
    protected $target_type = 0;
184
185
    /** @var string The type of this log entry, aka the description what has happened.
186
     * The mapping between the log entry class and the discriminator column is done by doctrine.
187
     * Each subclass should override this string to specify a better string.
188
     */
189
    protected $typeString = 'unknown';
190
191
    /** @var array The extra data in raw (short form) saved in the DB
192
     * @ORM\Column(name="extra", type="json")
193
     */
194
    protected $extra = [];
195
196
    public function __construct()
197
    {
198
        $this->timestamp = new DateTime();
199
        $this->level = self::LEVEL_WARNING;
200
    }
201
202
    /**
203
     * Get the user that caused the event associated with this log entry.
204
     *
205
     * @return User
206
     */
207
    public function getUser(): ?User
208
    {
209
        return $this->user;
210
    }
211
212
    /**
213
     * Sets the user that caused the event.
214
     *
215
     * @return $this
216
     */
217
    public function setUser(User $user): self
218
    {
219
        $this->user = $user;
220
221
        return $this;
222
    }
223
224
    /**
225
     * Returns the timestamp when the event that caused this log entry happened.
226
     *
227
     * @return DateTime
228
     */
229
    public function getTimestamp(): DateTime
230
    {
231
        return $this->timestamp;
232
    }
233
234
    /**
235
     * Sets the timestamp when the event happened.
236
     *
237
     * @return $this
238
     */
239
    public function setTimestamp(DateTime $timestamp): self
240
    {
241
        $this->timestamp = $timestamp;
242
243
        return $this;
244
    }
245
246
    /**
247
     * Get the priority level of this log entry. 0 is highest and 7 lowest level.
248
     * See LEVEL_* consts in this class for more info.
249
     *
250
     * @return int
251
     */
252
    public function getLevel(): int
253
    {
254
        //It is always alerting when a wrong int is saved in DB...
255
        if ($this->level < 0 || $this->level > 7) {
256
            return self::LEVEL_ALERT;
257
        }
258
259
        return $this->level;
260
    }
261
262
    /**
263
     * Sets the new level of this log entry.
264
     *
265
     * @return $this
266
     */
267
    public function setLevel(int $level): self
268
    {
269
        if ($level < 0 || $this->level > 7) {
270
            throw new \InvalidArgumentException(sprintf('$level must be between 0 and 7! %d given!', $level));
271
        }
272
        $this->level = $level;
273
274
        return $this;
275
    }
276
277
    /**
278
     * Get the priority level of this log entry as PSR3 compatible string.
279
     *
280
     * @return string
281
     */
282
    public function getLevelString(): string
283
    {
284
        return self::levelIntToString($this->getLevel());
285
    }
286
287
    /**
288
     * Sets the priority level of this log entry as PSR3 compatible string.
289
     *
290
     * @return $this
291
     */
292
    public function setLevelString(string $level): self
293
    {
294
        $this->setLevel(self::levelStringToInt($level));
295
296
        return $this;
297
    }
298
299
    /**
300
     * Returns the type of the event this log entry is associated with.
301
     *
302
     * @return string
303
     */
304
    public function getType(): string
305
    {
306
        return $this->typeString;
307
    }
308
309
    /**
310
     * Returns the class name of the target element associated with this log entry.
311
     * Returns null, if this log entry is not associated with an log entry.
312
     *
313
     * @return string|null The class name of the target class.
314
     */
315
    public function getTargetClass(): ?string
316
    {
317
        if (self::TARGET_TYPE_NONE === $this->target_type) {
318
            return null;
319
        }
320
321
        return self::targetTypeIdToClass($this->target_type);
322
    }
323
324
    /**
325
     * Returns the ID of the target element associated with this log entry.
326
     * Returns null, if this log entry is not associated with an log entry.
327
     *
328
     * @return int|null The ID of the associated element.
329
     */
330
    public function getTargetID(): ?int
331
    {
332
        if (0 === $this->target_id) {
333
            return null;
334
        }
335
336
        return $this->target_id;
337
    }
338
339
    /**
340
     * Checks if this log entry is associated with an element.
341
     *
342
     * @return bool True if this log entry is associated with an element, false otherwise.
343
     */
344
    public function hasTarget(): bool
345
    {
346
        return null !== $this->getTargetID() && null !== $this->getTargetClass();
347
    }
348
349
    /**
350
     * Sets the target element associated with this element.
351
     *
352
     * @param AbstractDBElement $element The element that should be associated with this element.
353
     *
354
     * @return $this
355
     */
356
    public function setTargetElement(?AbstractDBElement $element): self
357
    {
358
        if (null === $element) {
359
            $this->target_id = 0;
360
            $this->target_type = self::TARGET_TYPE_NONE;
361
362
            return $this;
363
        }
364
365
        $this->target_type = static::targetTypeClassToID(get_class($element));
366
        $this->target_id = $element->getID();
367
368
        return $this;
369
    }
370
371
    /**
372
     * Sets the target ID of the element associated with this element.
373
     *
374
     * @return $this
375
     */
376
    public function setTargetElementID(int $target_id): self
377
    {
378
        $this->target_id = $target_id;
379
380
        return $this;
381
    }
382
383
    public function getExtraData(): array
384
    {
385
        return $this->extra;
386
    }
387
388
    /**
389
     * This function converts the internal numeric log level into an PSR3 compatible level string.
390
     *
391
     * @param int $level The numerical log level
392
     *
393
     * @return string The PSR3 compatible level string
394
     */
395
    final public static function levelIntToString(int $level): string
396
    {
397
        if (! isset(self::LEVEL_ID_TO_STRING[$level])) {
398
            throw new \InvalidArgumentException('No level with this int is existing!');
399
        }
400
401
        return self::LEVEL_ID_TO_STRING[$level];
402
    }
403
404
    /**
405
     * This function converts a PSR3 compatible string to the internal numeric level string.
406
     *
407
     * @param string $level the PSR3 compatible string that should be converted
408
     *
409
     * @return int The internal int representation.
410
     */
411
    final public static function levelStringToInt(string $level): int
412
    {
413
        $tmp = array_flip(self::LEVEL_ID_TO_STRING);
414
        if (! isset($tmp[$level])) {
415
            throw new \InvalidArgumentException('No level with this string is existing!');
416
        }
417
418
        return $tmp[$level];
419
    }
420
421
    /**
422
     * Converts an target type id to an full qualified class name.
423
     *
424
     * @param int $type_id The target type ID
425
     *
426
     * @return string
427
     */
428
    final public static function targetTypeIdToClass(int $type_id): string
429
    {
430
        if (! isset(self::TARGET_CLASS_MAPPING[$type_id])) {
431
            throw new \InvalidArgumentException('No target type with this ID is existing!');
432
        }
433
434
        return self::TARGET_CLASS_MAPPING[$type_id];
435
    }
436
437
    /**
438
     * Convert a class name to a target type ID.
439
     *
440
     * @param string $class The name of the class (FQN) that should be converted to id
441
     *
442
     * @return int The ID of the associated target type ID.
443
     */
444
    final public static function targetTypeClassToID(string $class): int
445
    {
446
        $tmp = array_flip(self::TARGET_CLASS_MAPPING);
447
        //Check if we can use a key directly
448
        if (isset($tmp[$class])) {
449
            return $tmp[$class];
450
        }
451
452
        //Otherwise we have to iterate over everything and check for inheritance
453
        foreach ($tmp as $compare_class => $class_id) {
0 ignored issues
show
Bug introduced by
The expression $tmp of type null is not traversable.
Loading history...
454
            if (is_a($class, $compare_class, true)) {
455
                return $class_id;
456
            }
457
        }
458
459
        throw new \InvalidArgumentException('No target ID for this class is existing!');
460
    }
461
}
462