journal.php 41.3 KB
Newer Older
Prosper De Laure's avatar
Prosper De Laure committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

/*
 * Copyright (C) 2015 Maarch
 *
 * This file is part of bundle lifeCycle.
 *
 * Bundle lifeCycle is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Bundle lifeCycle is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with bundle lifeCycle.  If not, see <http://www.gnu.org/licenses/>.
 */

namespace bundle\lifeCycle\Controller;

/**
 * Class of archives life cycle journal
 *
 * @author Prosper DE LAURE <prosper.delaure@maarch.org>
 */
class journal
{
31
    protected $sdoFactory;
Prosper De Laure's avatar
Prosper De Laure committed
32
33
    protected $separateInstance;
    protected $interval;
34
    protected $currentOffset;
Prosper De Laure's avatar
Prosper De Laure committed
35
36
37
38
39
40
41
42
43
44
45
46
47

    // Journal files reading
    protected $currentJournalFile;
    protected $currentJournalId;
    protected $currentEvent;
    protected $journalCursor;
    protected $eventFormats;

    protected $journals;

    /**
     * Constructor
     * @param \dependency\sdo\Factory $sdoFactory       The sdo factory
48
49
     * @param boolean                 $separateInstance Read only instance events
     * @param integer                 $interval         The time bewteen 2 journal changes
Prosper De Laure's avatar
Prosper De Laure committed
50
     */
51
    public function __construct(\dependency\sdo\Factory $sdoFactory, $separateInstance = false, $interval = 86400)
Prosper De Laure's avatar
Prosper De Laure committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
    {
        $this->separateInstance = $separateInstance;
        $this->interval = $interval;
        $this->sdoFactory = $sdoFactory;

        $this->currentJournalFile = null;
        $this->currentJournalId = null;
        $this->currentOffset = 0;

        $this->eventFormats = $this->sdoFactory->index('lifeCycle/eventFormat');
        foreach ($this->eventFormats as $eventFormat) {
            $eventFormat->format = explode(' ', $eventFormat->format);
        }
    }

    /**
     * Get event type list
     *
Dylan's avatar
Dylan committed
70
     * @return lifeCycle/eventFormat[] The eventType list
Prosper De Laure's avatar
Prosper De Laure committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
     */
    public function listEventType()
    {
        return $this->sdoFactory->index('lifeCycle/eventFormat', 'type');
    }

    /**
     * Add an event to the journal
     * @param string $eventType       The type of the event
     * @param string $objectClass     The aimed object class
     * @param string $objectId        The aimed object id
     * @param mixed  $context         The description of the event
     * @param bool   $operationResult The operation result
     *
85
86
     * @throws \Exception
     *
Prosper De Laure's avatar
Prosper De Laure committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
     * @return lifeCycle/event The new event
     */
    public function logEvent($eventType, $objectClass, $objectId, $context = null, $operationResult = true)
    {
        $event = \laabs::newInstance('lifeCycle/event');

        $event->eventId = \laabs::newId();
        $event->instanceName = \laabs::getInstanceName();

        $event->timestamp = \laabs::newTimestamp();
        $event->eventType = $eventType;
        $event->objectClass = $objectClass;
        $event->objectId = $objectId;
        $event->operationResult = $operationResult;
        $event->description = "";

        if ($accountToken = \laabs::getToken('AUTH')) {
            $event->accountId = $accountToken->accountId;

        } else {
            $event->accountId = '__system__';
        }

        if ($currentOrganization = \laabs::getToken("ORGANIZATION")) {
            $organizationController = \laabs::newController('organization/organization');
            $organization = $organizationController->read($currentOrganization->ownerOrgId);

            $event->orgRegNumber = $organization->registrationNumber;
            $event->orgUnitRegNumber = $currentOrganization->registrationNumber;
        }

        $arrayToMerge[] = (string) $event->eventId;
        $arrayToMerge[] = $event->eventType;
        $arrayToMerge[] = (string) $event->timestamp;
        $arrayToMerge[] = (string) $event->accountId;
        $arrayToMerge[] = $event->objectClass;
        $arrayToMerge[] = (string) $event->objectId;
        $arrayToMerge[] = $event->operationResult;
        $arrayToMerge[] = $event->description; // 8th line

        $eventInfo = array();

        // Event info from position 9 to ...
        if ($context) {
            if (is_object($context)) {
                $context = get_object_vars($context);
            }

            if (!isset($this->eventFormats[$event->eventType])) {
Prosper De Laure's avatar
Prosper De Laure committed
136
                throw \laabs::newException("lifeCycle/journalException", "Unknown event type: %s", 404, null, [$event->eventType]);
Prosper De Laure's avatar
Prosper De Laure committed
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
            }

            $eventFormat = $this->eventFormats[$event->eventType];
            foreach ($eventFormat->format as $item) {
                if (isset($context[$item])) {
                    $eventInfo[] = $context[$item];
                    $arrayToMerge[] = $context[$item];
                } else {
                    $eventInfo[] = "";
                    $arrayToMerge[] = "";
                }
            }
        }

        if (!empty($eventInfo)) {
            $event->eventInfo = json_encode($eventInfo);
        }

        $event->description = vsprintf($eventFormat->message, $arrayToMerge);

        $this->sdoFactory->create($event);

159
160
        if (!$operationResult && $eventFormat->notification == true) {
            $this->notify($event);
Prosper De Laure's avatar
Prosper De Laure committed
161
162
163
164
165
166
167
168
169
170
        }

        return $event;
    }

    /**
     * Retrieve an archive event in a lifeCycle journal
     * @param mixed  $date   The journal date
     * @param string $needle The string to search in the journal
     *
Dylan's avatar
Dylan committed
171
     * @return object[] Array of life cycle event
Prosper De Laure's avatar
Prosper De Laure committed
172
173
174
175
176
177
     */
    public function matchEvent($date, $needle)
    {
        $events = [];
        $logController = \laabs::newController('recordsManagement/log');

178
        $tmpDir = \laabs::getTmpDir();
Prosper De Laure's avatar
Prosper De Laure committed
179
180
181
182
183
184
        $journal = $logController->getByDate('lifeCycle', (string) $date);

        if (!$journal) {
            return $events;
        }

185
        $archiveController = \laabs::newController('recordsManagement/archive');
186
        $digitalResourceController = \laabs::newController('digitalResource/digitalResource');
187
188

        if (!\laabs\file_exists($tmpDir . DIRECTORY_SEPARATOR . $journal->archiveId)) {
189
            $resources = $archiveController->getDigitalResources($journal->archiveId, $checkAccess = false);
190
191
            $journalResource = $resources[0];

192
193
194
            $journalContents = $digitalResourceController->contents($journalResource->resId);

            if (!file_put_contents($tmpDir . DIRECTORY_SEPARATOR . $journal->archiveId, $journalContents)) {
195
196
197
198
                throw \laabs::newException("lifeCycle/journalException", "Journal file cannot be written");
            }

        } else {
199
            $journalContents = file_get_contents($tmpDir . DIRECTORY_SEPARATOR . $journal->archiveId);
Prosper De Laure's avatar
Prosper De Laure committed
200
201
202
203
204
        }

        $offset = 0;

        do {
205
            $offset = strpos($journalContents, (string) $needle, $offset);
Prosper De Laure's avatar
Prosper De Laure committed
206
207

            if ($offset) {
208
209
210
211
                $journalLength = strlen($journalContents);
                $startOffset = strrpos($journalContents, "\n", -$journalLength + $offset) + 1;
                $endOffset = strpos($journalContents, "\n", $startOffset);
                $eventLine = substr($journalContents, $startOffset, $endOffset - $startOffset);
Prosper De Laure's avatar
Prosper De Laure committed
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

                $events[] = $this->getEventFromLine($eventLine);
                $offset = $endOffset;
            }
        } while ($offset);

        return $events;
    }

    /**
     * Get the events for a given object id and class
     * @param string $objectId    The identifier of the object
     * @param string $objectClass The class of the object
     * @param mixed  $eventType   An event type or an array of event types to retrieve
     *
227
228
     * @throws \Exception
     *
Dylan's avatar
Dylan committed
229
     * @return object[] Array of life cycle event
Prosper De Laure's avatar
Prosper De Laure committed
230
231
232
233
234
235
236
237
238
239
240
241
242
     */
    public function getObjectEvents($objectId, $objectClass, $eventType = null)
    {
        $query = "objectId='$objectId' AND objectClass='$objectClass'";

        if ($eventType) {
            if (is_array($eventType)) {
                $query .= " AND eventType=['".\laabs\implode("', '", $eventType)."']";
            } else {
                $query .= " AND eventType='$eventType'";
            }
        }

243
        $events = $this->sdoFactory->find('lifeCycle/event', $query, [], 'timestamp');
Prosper De Laure's avatar
Prosper De Laure committed
244

245
246
247
248
        foreach ($events as $key => $event) {
            $events[$key] = $this->decodeEventFormat($event);
        }

Prosper De Laure's avatar
Prosper De Laure committed
249
250
251
252
253
254
255
        return $events;
    }

    /**
     * Get an events by id from journal file
     * @param mixed $eventId The event or the identifier of the event
     *
256
257
     * @throws \Exception
     *
Dylan's avatar
Dylan committed
258
     * @return string An event
Prosper De Laure's avatar
Prosper De Laure committed
259
260
261
262
     */
    public function getEventFromJournal($eventId)
    {
        if (is_scalar($eventId) || get_class($eventId) == 'core\Type\Id') {
263
            $eventFromBase = $this->sdoFactory->read('lifeCycle/event', $eventId);
Prosper De Laure's avatar
Prosper De Laure committed
264
        } else {
265
            $eventFromBase = $eventId;
Prosper De Laure's avatar
Prosper De Laure committed
266
267
268
269
270
        }

        $logController = \laabs::newController('recordsManagement/log');

        // Read journal file
271
        $journalReference = $logController->getByDate('lifeCycle', $eventFromBase->timestamp);
Prosper De Laure's avatar
Prosper De Laure committed
272
273
274
275
276

        if ($journalReference) {
            $this->openJournal($journalReference->archiveId);

            // Get the eventId position on the journal file
277
            $this->currentOffset = strpos($this->currentJournalFile, (string) $eventFromBase->eventId);
Prosper De Laure's avatar
Prosper De Laure committed
278
279
280
281
282
283
284
285
286

            // Place cursor to the begin of line
            if ($this->currentOffset) {
                $endOffset = strpos($this->currentJournalFile, "\n", $this->currentOffset);
                $eventLine = substr($this->currentJournalFile, $this->currentOffset, $endOffset - $this->currentOffset);
                $this->currentOffset = $this->currentOffset + strlen($eventLine) + 1;

                // Get the event
                $event = $this->getEventFromLine($eventLine);
287
288
289
290
291
292

                // Retrieves information that is not in the csv file from database
                $event->instanceName = $eventFromBase->instanceName;
                $event->orgRegNumber = $eventFromBase->orgRegNumber;
                $event->orgUnitRegNumber = $eventFromBase->orgUnitRegNumber;

Prosper De Laure's avatar
Prosper De Laure committed
293
            } else {
294
                $event = $eventFromBase;
Prosper De Laure's avatar
Prosper De Laure committed
295
296
297
298
299
                return $event;

                //throw \laabs::newException("lifeCycle/journalException", "Event can't be found.");
            }
        } else {
300
301
            $event = $eventFromBase;
            $this->decodeEventFormat($event);
Prosper De Laure's avatar
Prosper De Laure committed
302
303
304
305
306
307
308
309
310
311
312
313
314
315
        }

        $this->currentEvent = $event;

        return $event;
    }

    /**
     * Search a journal event
     * @param string    $eventType      The type of the event
     * @param string    $objectClass    The object class
     * @param string    $objectId       The identifier of the object
     * @param timestamp $minDate        The minimum date of the event
     * @param timestamp $maxDate        The maximum date of the event
316
317
     * @param string    $sortBy         The event sorting request
     * @param int       $maxResults     The number of result
Prosper De Laure's avatar
Prosper De Laure committed
318
     *
319
320
     * @throws \Exception
     *
Dylan's avatar
Dylan committed
321
     * @return object[] The result of the request
Prosper De Laure's avatar
Prosper De Laure committed
322
     */
Alexandre Morin's avatar
Alexandre Morin committed
323
324
325
326
327
328
329
    public function searchEvent(
        $eventType = null,
        $objectClass = null,
        $objectId = null,
        $minDate = null,
        $maxDate = null,
        $sortBy = ">timestamp",
330
        $maxResults = null
Alexandre Morin's avatar
Alexandre Morin committed
331
    ) {
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
        list($queryParams, $queryString) = $this->queryBuilder($eventType, $objectClass, $objectId, $minDate, $maxDate);

        $events = $this->sdoFactory->find(
            'lifeCycle/event',
            $queryString,
            $queryParams,
            $sortBy,
            null,
            $maxResults
        );

        $userAccountController = \laabs::newController('auth/userAccount');
        $users = $userAccountController->index();
        foreach ($users as $i => $user) {
            $users[(string) $user->accountId] = $user;
            unset($users[$i]);
        }

        $serviceAccountController = \laabs::newController('auth/serviceAccount');
        $services = $serviceAccountController->index();
        foreach ($services as $i => $service) {
            $services[(string) $service->accountId] = $service;
            unset($services[$i]);
        }

        foreach ($events as $i => $event) {
            if (isset($event->accountId) && isset($users[(string) $event->accountId])) {
                $event->accountName = $users[(string) $event->accountId]->accountName;
            } elseif (isset($event->accountId) && isset($services[(string) $event->accountId])) {
                $event->accountName = $services[(string) $event->accountId]->accountName;
            } else {
                $event->accountName = "__system__";
            }
        }

        return $events;
    }

    /**
     * Build a search sql query
     *
     * @param string    $eventType      The type of the event
     * @param string    $objectClass    The object class
     * @param string    $objectId       The identifier of the object
     * @param timestamp $minDate        The minimum date of the event
     * @param timestamp $maxDate        The maximum date of the event
     */
    protected function queryBuilder(
        $eventType = null,
        $objectClass = null,
        $objectId = null,
        $minDate = null,
        $maxDate = null
    ) {
        $query = [];
        $queryParams = [];
Prosper De Laure's avatar
Prosper De Laure committed
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

        if ($this->separateInstance) {
            $queryParams['instanceName'] = \laabs::getInstanceName();
            $query['instanceName'] = "instanceName = :instanceName";
        }

        if ($eventType) {
            $queryParams['eventType'] = $eventType;
            $query['eventType'] = "eventType = :eventType";
        }

        if ($objectClass) {
            $queryParams['objectClass'] = $objectClass;
            $query['objectClass'] = "objectClass = :objectClass";
        }

        if ($objectId) {
            $queryParams['objectId'] = $objectId;
406
            $query['objectId'] = "objectId = :objectId";
Prosper De Laure's avatar
Prosper De Laure committed
407
408
409
410
411
412
413
414
415
416
417
418
419
420
        }

        if ($minDate) {
            $queryParams['minDate'] = $minDate;
            $query['minDate'] = "timestamp >= :minDate";
        }

        if ($maxDate) {
            $queryParams['maxDate'] = $maxDate->add(new \DateInterval('PT23H59M59S'));
            $query['maxDate'] = "timestamp <= :maxDate";
        }

        $queryString = implode(' AND ', $query);

421
422
        return [$queryParams, $queryString];
    }
Alexandre Morin's avatar
Alexandre Morin committed
423

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
    /**
     * Count journal events
     *
     * @param string    $eventType      The type of the event
     * @param string    $objectClass    The object class
     * @param string    $objectId       The identifier of the object
     * @param timestamp $minDate        The minimum date of the event
     * @param timestamp $maxDate        The maximum date of the event
     * @param string    $sortBy         The event sorting request
     *
     * @throws \Exception
     *
     * @return integer Count results of request
     */
    public function searchCount(
        $eventType = null,
        $objectClass = null,
        $objectId = null,
        $minDate = null,
        $maxDate = null,
        $sortBy = ">timestamp"
    ) {
        list($queryParams, $queryString) = $this->queryBuilder($eventType, $objectClass, $objectId, $minDate, $maxDate);

        $count = $this->sdoFactory->count(
Alexandre Morin's avatar
Alexandre Morin committed
449
450
451
            'lifeCycle/event',
            $queryString,
            $queryParams,
452
            $sortBy
Alexandre Morin's avatar
Alexandre Morin committed
453
        );
Prosper De Laure's avatar
Prosper De Laure committed
454

455
        return $count;
Prosper De Laure's avatar
Prosper De Laure committed
456
457
458
459
460
    }
    /**
     * Load a journal
     * @param string $journalReference The id of the journal or the journal object
     *
461
462
     * @throws \Exception
     *
Prosper De Laure's avatar
Prosper De Laure committed
463
464
465
466
467
     * @return boolean The result of the operation
     */
    public function openJournal($journalReference)
    {
        $logController = \laabs::newController('recordsManagement/log');
468
        $digitalResourceController = \laabs::newController('digitalResource/digitalResource');
Prosper De Laure's avatar
Prosper De Laure committed
469
470
471
472

        if (is_scalar($journalReference) || get_class($journalReference) == 'core\Type\Id') {
            $journalReference = $logController->read($journalReference);
        }
473

Prosper De Laure's avatar
Prosper De Laure committed
474
475
        if (isset($journalReference->toDate)) {
            $archiveController = \laabs::newController('recordsManagement/archive');
476
            $resources = $archiveController->getDigitalResources($journalReference->archiveId, $checkAccess = false);
477
            $journalResource = $digitalResourceController->retrieve($resources[0]->resId);
478

479
480
            $journalFile = $journalResource->getContents();
            $this->journalCursor = 0;
Prosper De Laure's avatar
Prosper De Laure committed
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497

            if ($journalFile == null) {
                throw \laabs::newException("lifeCycle/journalException", "The journal file can't be opened");
            } else {
                $this->checkIntegrity($journalReference);
                $this->currentJournalFile = $journalFile;
                $this->currentJournalId = $journalReference->archiveId;
            }
        }

        $this->currentOffset = 0;

        return true;
    }

    /**
     * Get the next event or get the next event which contain a given item
498
499
500
501
     * @param string  $eventType The event type
     * @param boolean $chain     Chain to the next journal
     *
     * @throws \Exception
Prosper De Laure's avatar
Prosper De Laure committed
502
     *
Dylan's avatar
Dylan committed
503
     * @return string The event
Prosper De Laure's avatar
Prosper De Laure committed
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
     */
    public function getNextEvent($eventType = null, $chain = true)
    {
        $nextEvent = null;

        if (!$this->currentJournalFile) {
            $queryString = [];
            if ($eventType) {
                $queryString['eventType'] = "eventType='$eventType'";
            }
            $timestamp = $this->currentEvent->timestamp;

            if ($this->separateInstance) {
                $queryString['instanceName'] = "instanceName = '".\laabs::getInstanceName()."'";
            }

            $queryString['timestamp'] = "timestamp>'$timestamp'";

522
523
524
525
526
527
528
529
            $nextEvent = $this->sdoFactory->find(
                "lifeCycle/event",
                implode(' and ', $queryString),
                [],
                "<timestamp",
                0,
                1
            );
Prosper De Laure's avatar
Prosper De Laure committed
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573

            if (count($nextEvent)) {
                $nextEvent = $nextEvent[0];
                $nextEvent = $this->decodeEventFormat($nextEvent);
            } else {
                $logController = \laabs::newController('recordsManagement/log');
                $journal = $logController->getFirstJournal('lifeCycle');
                if ($journal) {
                    $this->openJournal($journal->archiveId);
                    $this->getNextEvent($eventType, $chain);

                } else {
                    $nextEvent = false;
                }

            }

        } else {
            // Place the cursor to the first event if it not positioned yet
            if ($this->currentOffset == 0) {
                $this->currentOffset = strpos($this->currentJournalFile, "\n") + 1;
            }

            // Search the event
            if ($eventType) {
                $offset = strpos($this->currentJournalFile, $eventType, $this->currentOffset);
            } else {
                $offset = $this->currentOffset;
            }

            // Read the event
            if ($offset != false) {
                $journalLength = strlen($this->currentJournalFile);

                $startOffset = strrpos($this->currentJournalFile, "\n", -$journalLength + $offset) + 1;
                $endOffset = strpos($this->currentJournalFile, "\n", $startOffset);
                $eventLine = substr($this->currentJournalFile, $startOffset, $endOffset - $startOffset);

                $this->currentOffset = $startOffset + strlen($eventLine) + 1;

                $nextEvent = $this->getEventFromLine($eventLine);
            }

            // Search on the next journal
Alexis Ragot's avatar
Alexis Ragot committed
574
            if ($chain && $nextEvent == null) {
Prosper De Laure's avatar
Prosper De Laure committed
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
                if ($this->openNextJournal()) {
                    $nextEvent = $this->getNextEvent($eventType);
                }
            }
        }

        if ($nextEvent) {
            $this->currentEvent = $nextEvent;
        }

        return $nextEvent;
    }

    /**
     * Get event object
     * @param id   $archiveId          The archive identifier
     * @param date $searchingStartDate The searching start date
     *
593
594
     * @throws \Exception
     *
Dylan's avatar
Dylan committed
595
     * @return object[] Array of live cycle event
Prosper De Laure's avatar
Prosper De Laure committed
596
597
598
599
600
601
     */
    public function getEvents($archiveId, $searchingStartDate = null)
    {
        // Open the journal to start with
        $logController = \laabs::newController('recordsManagement/log');
        $journal = $logController->getByDate('lifeCycle', $searchingStartDate);
602
603

        if (!isset($journal) || is_null($journal)) {
604
            $events = $this->sdoFactory->find('lifeCycle/event', "objectClass='recordsManagement/archive' AND  objectId='$archiveId'", [], ">timestamp");
Prosper De Laure's avatar
Prosper De Laure committed
605
606
607
608
609
610
611
            foreach ($events as $key => $event) {
                $events[$key] = $this->decodeEventFormat($event);
            }

            return $events;
        }

612
613
614
        if (is_array($journal)) {
            $journal = end($journal);
        }
Prosper De Laure's avatar
Prosper De Laure committed
615

616
        $this->openJournal($journal->archiveId);
Prosper De Laure's avatar
Prosper De Laure committed
617
618

        // Searching for related events not in the journal yet
619
        $events = $this->sdoFactory->find('lifeCycle/event', "objectClass='recordsManagement/archive' AND  objectId='$archiveId' AND timestamp>'$journal->toDate'", [], ">timestamp");
Prosper De Laure's avatar
Prosper De Laure committed
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
        foreach ($events as $key => $event) {
            $events[$key] = $this->decodeEventFormat($event);
        }

        // Searching for related events
        $offset = 0;
        $nextEvent = 0;
        $lastJournal = false;

        while ($lastJournal == false) {
            if ($nextEvent != null) {
                $offset = strpos($this->currentJournalFile, "\n", $offset);
            }

            if ($this->currentJournalId) {
                $offset = strpos($this->currentJournalFile, (string) $archiveId, $offset);
            }

            // Read the event in the file
            if ($offset != false) {
                $journalLength = strlen($this->currentJournalFile);

                $startOffset = strrpos($this->currentJournalFile, "\n", -$journalLength + $offset) + 1;
                $endOffset = strpos($this->currentJournalFile, "\n", $startOffset);
                $eventLine = substr($this->currentJournalFile, $startOffset, $endOffset - $startOffset);

                $this->currentOffset = $startOffset + strlen($eventLine) + 1;
                $nextEvent = $this->getEventFromLine($eventLine);
                $events[] = $nextEvent;
            } else {
                if (!$this->openNextJournal()) {
                    $lastJournal = true;
                } else {
                    $offset = 0;
                }
            }
        }

        return $events;
    }

    /**
     * Get the previous event or get the previous event whitch contain a givven item
663
664
     * @param string  $eventItem The event item to search
     * @param boolean $chain     Chain to the previous journal
Prosper De Laure's avatar
Prosper De Laure committed
665
     *
666
667
668
     * @throws \Exception
     *
     * @return mixed The event or null
Prosper De Laure's avatar
Prosper De Laure committed
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
     */
    public function getPreviousEvent($eventItem = null, $chain = true)
    {
        $event = null;

        // Open a journal if there is not any
        if (!$this->currentJournalFile) {
            return false;
        }

        // Place the cursor to the first event if it not positioned yet
        if ($this->currentOffset == 0) {
            if ($this->openPreviousJournal()) {
                return $event = $this->getPreviousEvent($eventItem);
            } else {
                return null;
            }
        }

        $journalLength = strlen($this->currentJournalFile);

        // Search the event
        if ($eventItem) {
            $offset = strrpos($this->currentJournalFile, $eventItem, $this->currentOffset - $journalLength);
        } else {
            $offset = $this->currentOffset - 2;
        }

        // Read the event
        if ($offset != false) {
            $startOffset = strrpos($this->currentJournalFile, "\n", $offset - $journalLength) + 1;
            $endOffset = strpos($this->currentJournalFile, "\n", $startOffset);
            $eventLine = substr($this->currentJournalFile, $startOffset, $endOffset - $startOffset);

            $this->currentOffset = $startOffset - 1;

            $this->getEventFromLine($eventLine);
        }

        // Search on the next journal
        if ($chain && $event == null) {
            if ($this->openPreviousJournal()) {
                $event = $this->getPreviousEvent($eventItem);
            }
        }

        return $event;
    }


    /**
     * Get the last usable journal
     *
     * @return lifeCycle/journal The journal object
     */
    public function getLastJournal()
    {
        $logController = \laabs::newController('recordsManagement/log');
        $journals = $logController->query("type='lifeCycle'", ">fromDate", 1);

        if (empty($journals)) {
            return null;
        }

        $journal = end($journals);

        return $journal;
    }

    /**
     * Load the previous journal
     *
741
742
743
     * @throws \Exception
     *
     * @return string The opened journal identifier
Prosper De Laure's avatar
Prosper De Laure committed
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
     */
    public function openPreviousJournal()
    {
        $journalId = null;

        if ($this->currentJournalFile) {
            $this->currentOffset = strpos($this->currentJournalFile, "\n");
            $eventLine = substr($this->currentJournalFile, 0, -2);

            $journalArray = str_getcsv($eventLine);

            if (count($journalArray) < 7) {
                if (count($journalArray)) {
                    $journalId = $journalArray[3];
                    $hashAlgorithm = $journalArray[4];
                    $hash = $journalArray[5];
                    $this->openJournal($journalId);

762
                    $currentHash = hash($hashAlgorithm, $this->currentJournalFile);
Prosper De Laure's avatar
Prosper De Laure committed
763
764
765
766
767
768
769
770
771
772
773
774
775
776
                    if ($currentHash != $hash) {
                        throw \laabs::newException("lifeCycle/journalException", "Journal hash is incorrect.");
                    }

                    $this->currentOffset = strrpos($this->currentJournalFile, "\n", -2);
                }

            } else {
                if (count($journalArray)) {
                    $journalId = $journalArray[8];
                    $hashAlgorithm = $journalArray[9];
                    $hash = $journalArray[10];
                    $this->openJournal($journalId);

777
                    $currentHash = hash($hashAlgorithm, $this->currentJournalFile);
Prosper De Laure's avatar
Prosper De Laure committed
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
                    if ($currentHash != $hash) {
                        throw \laabs::newException("lifeCycle/journalException", "Journal hash is incorrect.");
                    }

                    $this->currentOffset = strrpos($this->currentJournalFile, "\n", -2);
                }
            }

        }

        return $journalId;
    }

    /**
     * Load the next journal
     *
794
795
796
     * @throws \Exception
     *
     * @return string The opened journal identifier
Prosper De Laure's avatar
Prosper De Laure committed
797
798
799
800
801
802
803
804
805
806
     */
    public function openNextJournal()
    {
        $journalId = null;

        if ($this->currentJournalId) {
            $this->currentOffset = strpos($this->currentJournalFile, "\n");

            $logController = \laabs::newController('recordsManagement/log');

807
            $nextJournal = $logController->getNextJournal($this->currentJournalId);
Prosper De Laure's avatar
Prosper De Laure committed
808
809

            if (isset($nextJournal)) {
810
811
                $this->openJournal($nextJournal->archiveId);
                $journalId = $nextJournal->archiveId;
Prosper De Laure's avatar
Prosper De Laure committed
812
813
814
815
816
817
818
819
820
821
822
823
            }
        }

        return $journalId;
    }

    /**
     * Get the current journal
     * @param string  $journalId The journal identifier
     * @param integer $offset    The reading offset
     * @param integer $limit     The maximum number of event to load
     *
824
825
     * @throws \Exception
     *
Dylan's avatar
Dylan committed
826
     * @return object[] Array of life cycle event
Prosper De Laure's avatar
Prosper De Laure committed
827
     */
Alexandre Morin's avatar
Alexandre Morin committed
828
    public function readJournal($journalId, $offset = 0, $limit = null)
Prosper De Laure's avatar
Prosper De Laure committed
829
830
831
832
833
    {
        $this->openJournal($journalId);

        $events = array();

Alexandre Morin's avatar
Alexandre Morin committed
834
835
836
837
        if (!$limit) {
            $limit = \laabs::configuration('presentation.maarchRM')['maxResults'];
        }

Prosper De Laure's avatar
Prosper De Laure committed
838
839
840
841
842
843
844
845
846
847
848
849
        while ($limit > 0 && $event = $this->getNextEvent(null, false)) {
            $events[] = $event;
            $limit--;
        }

        return $events;
    }

    /**
     * Check integrity
     * @param string $archiveId
     *
850
851
     * @throws \Exception
     *
Dylan's avatar
Dylan committed
852
     * @return bool The result of the operation
Prosper De Laure's avatar
Prosper De Laure committed
853
854
855
856
857
     */
    public function checkIntegrity($archiveId)
    {
        $logController = \laabs::newController('recordsManagement/log');
        $archiveController = \laabs::newController('recordsManagement/archive');
Alexandre Morin's avatar
Alexandre Morin committed
858
        $digitalResourceController = \laabs::newController('digitalResource/digitalResource');
Prosper De Laure's avatar
Prosper De Laure committed
859
860
861
862
863
864
865
866

        // Read journal
        if (is_scalar($archiveId) || get_class($archiveId) == 'core\Type\Id') {
            $journal = $logController->read($archiveId);
        } else {
            $journal = $archiveId;
            $archiveId = (string) $journal->archiveId;
        }
867
        $resources = $archiveController->getDigitalResources($journal->archiveId, $checkAccess = false);
Alexandre Morin's avatar
Alexandre Morin committed
868
        $journalResource = $digitalResourceController->retrieve($resources[0]->resId);
869
        $resIntegrity = $archiveController->verifyIntegrity($journal->archiveId);
Prosper De Laure's avatar
Prosper De Laure committed
870

871
        if (is_array($resIntegrity["error"]) && !empty($resIntegrity["error"])) {
872
873
874
875
            throw \laabs::newException(
                'recordsManagement/journalException',
                "Invalid journal: invalid hash integrity."
            );
Prosper De Laure's avatar
Prosper De Laure committed
876
877
878
879
880
881
882
883
884
885
886
887
        }

        $nextJournal = $logController->getNextJournal($journal);

        // Journal is the last... simply check its hash against min rotate
        if ($nextJournal == null) {
            $now = \laabs::newTimestamp();

            $diff = $journal->toDate->diff($now);

            // In the future ????
            if ($diff->invert) {
888
889
890
891
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal date: latest date is in the future."
                );
Prosper De Laure's avatar
Prosper De Laure committed
892
893
894
895
896
            }

            return true;
        }

897
        $resources = $archiveController->getDigitalResources($nextJournal->archiveId, $checkAccess = false);
Alexandre Morin's avatar
Alexandre Morin committed
898
        $nextJournalResource = $digitalResourceController->retrieve($resources[0]->resId);
899
        $nextJournalContents = $nextJournalResource->getContents();
Prosper De Laure's avatar
Prosper De Laure committed
900

Alexandre Morin's avatar
Alexandre Morin committed
901
        $chainEvent = str_getcsv(strtok($nextJournalContents, "\n"));
Prosper De Laure's avatar
Prosper De Laure committed
902
903
904
905

        // For older version compatibility
        if (count($chainEvent) < 7) {
            if (empty($chainEvent[3]) || empty($chainEvent[4]) || empty($chainEvent[5])) {
906
907
908
909
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal: Next journal chaining event is incomplete."
                );
Prosper De Laure's avatar
Prosper De Laure committed
910
911
912
913
            }

            $chainedJournalId = $chainEvent[3];
            if ($chainedJournalId != $archiveId) {
914
915
916
917
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal: Next journal is missing or chaining event has an invalid journal identifier."
                );
Prosper De Laure's avatar
Prosper De Laure committed
918
919
920
921
922
            }

            $chainedJournalHashAlgo = $chainEvent[4];
            $chainedJournalHash = $chainEvent[5];

923
            $calcJournalHash = hash($chainedJournalHashAlgo, $journalResource->getContents());
Prosper De Laure's avatar
Prosper De Laure committed
924
925

            if ($calcJournalHash != $chainedJournalHash) {
926
927
928
929
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal: Chaining event has a different hash."
                );
Prosper De Laure's avatar
Prosper De Laure committed
930
931
932
933
            }

        } else {
            if (empty($chainEvent[8]) || empty($chainEvent[9]) || empty($chainEvent[10])) {
934
935
936
937
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal: Next journal chaining event is incomplete."
                );
Prosper De Laure's avatar
Prosper De Laure committed
938
939
940
941
            }

            $chainedJournalId = $chainEvent[8];
            if ($chainedJournalId != $archiveId) {
942
943
944
945
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal: Next journal is missing or chaining event has an invalid journal identifier."
                );
Prosper De Laure's avatar
Prosper De Laure committed
946
947
948
949
950
            }

            $chainedJournalHashAlgo = $chainEvent[9];
            $chainedJournalHash = $chainEvent[10];

951
            $calcJournalHash = hash($chainedJournalHashAlgo, $journalResource->getContents());
Prosper De Laure's avatar
Prosper De Laure committed
952
953

            if ($calcJournalHash != $chainedJournalHash) {
954
955
956
957
                throw \laabs::newException(
                    'recordsManagement/journalException',
                    "Invalid journal: Chaining event has a different hash."
                );
Prosper De Laure's avatar
Prosper De Laure committed
958
959
960
961
962
963
964
965
966
967
968
969
            }
        }

        return $chainEvent;
    }

    /**
     * Chain the last journal
     *
     * @return string The chained journal file name
     */
    public function chainJournal()
970
971
972
    {
        $journalArray = [];

973
        if (isset(\laabs::configuration('lifeCycle')['chainJournalByOrganization']) && \laabs::configuration('lifeCycle')['chainJournalByOrganization']) {
974
975
976
977
978
            $orgController = \laabs::newController('organization/organization');

            $organizations = $orgController->index("isOrgUnit=false");

            foreach ($organizations as $organization) {
Prosper De Laure's avatar
Prosper De Laure committed
979
                $journalArray[] = $this->processChaining($organization->registrationNumber);
980
981
982
983
            }
        }

        $journalArray[] = $this->processChaining();
984

985
986
987
988
989
990
991
992
993
        if (count($journalArray) == 1) {
            $journalArray = $journalArray[0];
        }

        return $journalArray;
    }

    /**
     * process the chaining of the last journal
994
     * @param string $ownerOrgRegNumber The journal owner organization registration number
995
996
997
     *
     * @return string The chained journal file name
     */
Prosper De Laure's avatar
Prosper De Laure committed
998
    protected function processChaining($ownerOrgRegNumber = null)
Prosper De Laure's avatar
Prosper De Laure committed
999
1000
    {
        $tmpdir = \laabs::getTmpDir();
For faster browsing, not all history is shown. View entire blame