Passed
Push — feature/tests ( aefab2...5c9f0f )
by Simon
02:27 queued 36s
created

DataObjectExtension   A

Complexity

Total Complexity 24

Size/Duplication

Total Lines 174
Duplicated Lines 0 %

Test Coverage

Coverage 22.22%

Importance

Changes 15
Bugs 0 Features 1
Metric Value
wmc 24
eloc 74
c 15
b 0
f 1
dl 0
loc 174
ccs 16
cts 72
cp 0.2222
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getMemberPermissions() 0 24 6
A onAfterDelete() 0 20 3
A getDirtyClass() 0 14 3
A registerException() 0 17 1
A getViewStatus() 0 17 5
A onAfterWrite() 0 27 6
1
<?php
2
3
4
namespace Firesphere\SolrSearch\Extensions;
5
6
use Exception;
7
use Firesphere\SolrSearch\Models\DirtyClass;
8
use Firesphere\SolrSearch\Services\SolrCoreService;
9
use Psr\Log\LoggerInterface;
10
use SilverStripe\Assets\File;
11
use SilverStripe\CMS\Model\SiteTree;
12
use SilverStripe\Control\Controller;
13
use SilverStripe\Core\Injector\Injector;
14
use SilverStripe\ORM\ArrayList;
15
use SilverStripe\ORM\DataExtension;
16
use SilverStripe\ORM\DataList;
17
use SilverStripe\ORM\DataObject;
18
use SilverStripe\ORM\ValidationException;
19
use SilverStripe\Security\Group;
20
use SilverStripe\Security\Member;
21
use SilverStripe\SiteConfig\SiteConfig;
22
use SilverStripe\Versioned\ChangeSet;
23
use SilverStripe\Versioned\ChangeSetItem;
24
25
/**
26
 * Class \Firesphere\SolrSearch\Compat\DataObjectExtension
27
 *
28
 * @property File|SiteConfig|SiteTree|Group|Member|DataObjectExtension $owner
29
 */
30
class DataObjectExtension extends DataExtension
31
{
32
    public const WRITE = 'write';
33
    public const DELETE = 'delete';
34
    /**
35
     * @var DataList
36
     */
37
    protected static $members;
38
    protected static $excludedClasses = [
39
        DirtyClass::class,
40
        ChangeSet::class,
41
        ChangeSetItem::class,
42
    ];
43
    /**
44
     * @var array
45
     */
46
    protected static $canViewClasses = [];
47
48
    /**
49
     * @throws ValidationException
50
     */
51 3
    public function onAfterWrite()
52
    {
53 3
        $owner = $this->owner;
54 3
        parent::onAfterWrite();
55 3
        if (!in_array($owner->ClassName, static::$excludedClasses, true) ||
0 ignored issues
show
Bug Best Practice introduced by
The property ClassName does not exist on Firesphere\SolrSearch\Ex...ons\DataObjectExtension. Did you maybe forget to declare it?
Loading history...
56
            (Controller::curr()->getRequest()->getURL() &&
57 3
                strpos('dev/build', Controller::curr()->getRequest()->getURL()) !== false)
58
        ) {
59 3
            return;
60
        }
61
        /** @var DataObject $owner */
62
        $record = $this->getDirtyClass($owner, self::WRITE);
63
64
        $ids = json_decode($record->IDs, 1) ?: [];
65
        try {
66
            $service = new SolrCoreService();
67
            $service->setInDebugMode(false);
68
            $service->updateItems(ArrayList::create([$owner]), SolrCoreService::UPDATE_TYPE);
69
            // If we don't get an exception, mark the item as clean
70
            // Added bonus, array_flip removes duplicates
71
            $values = array_flip($ids);
72
            unset($values[$owner->ID]);
73
74
            $record->IDs = json_encode(array_keys($values));
75
            $record->write();
76
        } catch (Exception $error) {
77
            $this->registerException($ids, $record, $error);
78
        }
79
    }
80
81
    /**
82
     * @param DataObject $owner
83
     * @param string $type
84
     * @return DirtyClass
85
     * @throws ValidationException
86
     */
87
    protected function getDirtyClass(DataObject $owner, $type)
88
    {
89
        // Get the DirtyClass object for this item
90
        /** @var null|DirtyClass $record */
91
        $record = DirtyClass::get()->filter(['Class' => $owner->ClassName, 'Type' => $type])->first();
92
        if (!$record || !$record->exists()) {
1 ignored issue
show
introduced by
$record is of type Firesphere\SolrSearch\Models\DirtyClass, thus it always evaluated to true.
Loading history...
93
            $record = DirtyClass::create([
94
                'Class' => $owner->ClassName,
95
                'Type'  => $type
96
            ]);
97
            $record->write();
98
        }
99
100
        return $record;
101
    }
102
103
    /**
104
     * @param array $ids
105
     * @param $record
106
     * @param Exception $e
107
     */
108
    protected function registerException(array $ids, $record, Exception $error): void
109
    {
110
        /** @var DataObject $owner */
111
        $owner = $this->owner;
112
        $ids[] = $owner->ID;
113
        // If we don't get an exception, mark the item as clean
114
        $record->IDs = json_encode($ids);
115
        $record->write();
116
        $logger = Injector::inst()->get(LoggerInterface::class);
117
        $logger->warn(
118
            sprintf(
119
                'Unable to alter %s with ID %s',
120
                $owner->ClassName,
121
                $owner->ID
122
            )
123
        );
124
        $logger->error($error->getMessage());
125
    }
126
127
    /**
128
     * @throws ValidationException
129
     */
130
    public function onAfterDelete(): void
131
    {
132
        /** @var DataObject $owner */
133
        $owner = $this->owner;
134
        /** @var DirtyClass $record */
135
        $record = $this->getDirtyClass($owner, self::DELETE);
136
137
        $ids = json_decode($record->IDs, 1) ?: [];
138
        parent::onAfterDelete();
139
        try {
140
            (new SolrCoreService())->updateItems(ArrayList::create([$owner]), SolrCoreService::DELETE_TYPE);
141
            // If successful, remove it from the array
142
            // Added bonus, array_flip removes duplicates
143
            $values = array_flip($ids);
144
            unset($values[$owner->ID]);
145
146
            $record->IDs = json_encode(array_keys($values));
147
            $record->write();
148
        } catch (Exception $error) {
149
            $this->registerException($ids, $record, $error);
150
        }
151
    }
152
153
    /**
154
     * Get the view status for each member in this object
155
     * @return array
156
     */
157 12
    public function getViewStatus(): array
158
    {
159
        // Return empty if it's not allowed to show in search
160
        // The setting needs to be explicitly false, to avoid any possible collision
161
        // with objects not having the setting
162
        /** @var DataObject|SiteTree $owner */
163 12
        $owner = $this->owner;
164
        // Return immediately if the owner has ShowInSearch not being `null`
165 12
        if ($owner->ShowInSearch === false || $owner->ShowInSearch === 0) {
166 1
            return [];
167
        }
168
169 12
        if (isset(self::$canViewClasses[$owner->ClassName]) && !$owner->hasField('ShowInSearch')) {
170
            return self::$canViewClasses[$owner->ClassName];
171
        }
172
173 12
        return $this->getMemberPermissions($owner);
174
    }
175
176
    /**
177
     * @param DataObject|SiteTree $owner
178
     * @return array
179
     */
180 12
    protected function getMemberPermissions($owner): array
181
    {
182 12
        $return = [];
183
184
        // Add null users if it's publicly viewable
185 12
        if ($owner->canView()) {
186 12
            return ['1-null'];
187
        }
188
189
        if (!self::$members) {
190
            self::$members = Member::get();
191
        }
192
193
        foreach (self::$members as $member) {
194
            if ($owner->canView($member)) {
195
                $return[] = sprintf('1-%s', $member->ID);
196
            }
197
        }
198
199
        if (!$owner->hasField('ShowInSearch')) {
200
            self::$canViewClasses[$owner->ClassName] = $return;
201
        }
202
203
        return $return;
204
    }
205
}
206