Passed
Push — feature/tests ( 559bb5...be8439 )
by Simon
01:58
created

DataObjectExtension::getViewStatus()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 17
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5.0729

Importance

Changes 9
Bugs 0 Features 1
Metric Value
eloc 6
c 9
b 0
f 1
dl 0
loc 17
ccs 6
cts 7
cp 0.8571
rs 9.6111
cc 5
nc 3
nop 0
crap 5.0729
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
    /**
39
     * @var array
40
     */
41
    protected static $excludedClasses = [
42
        DirtyClass::class,
43
        ChangeSet::class,
44
        ChangeSetItem::class,
45
    ];
46
    /**
47
     * @var array
48
     */
49
    protected static $canViewClasses = [];
50
51
    /**
52
     * @throws ValidationException
53
     */
54 4
    public function onAfterWrite()
55
    {
56
        /** @var DataObject $owner */
57 4
        $owner = $this->owner;
58 4
        parent::onAfterWrite();
59 4
        if (in_array($owner->ClassName, static::$excludedClasses, true) ||
60 4
            (Controller::curr()->getRequest()->getURL() &&
61 4
                strpos('dev/build', Controller::curr()->getRequest()->getURL()) !== false)
62
        ) {
63 4
            return;
64
        }
65
        /** @var DataObject $owner */
66 4
        $record = $this->getDirtyClass($owner, self::WRITE);
67
68 4
        $ids = json_decode($record->IDs, 1) ?: [];
69
        try {
70 4
            $service = new SolrCoreService();
71 4
            $service->setInDebugMode(false);
72 4
            $service->updateItems(ArrayList::create([$owner]), SolrCoreService::UPDATE_TYPE);
73
            // If we don't get an exception, mark the item as clean
74
            // Added bonus, array_flip removes duplicates
75 4
            $values = array_flip($ids);
76 4
            unset($values[$owner->ID]);
77
78 4
            $record->IDs = json_encode(array_keys($values));
79 4
            $record->write();
80
        } catch (Exception $error) {
81
            $this->registerException($ids, $record, $error);
82
        }
83 4
    }
84
85
    /**
86
     * @param DataObject $owner
87
     * @param string $type
88
     * @return DirtyClass
89
     * @throws ValidationException
90
     */
91 4
    protected function getDirtyClass(DataObject $owner, $type)
92
    {
93
        // Get the DirtyClass object for this item
94
        /** @var null|DirtyClass $record */
95 4
        $record = DirtyClass::get()->filter(['Class' => $owner->ClassName, 'Type' => $type])->first();
96 4
        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...
97 4
            $record = DirtyClass::create([
98 4
                'Class' => $owner->ClassName,
99 4
                'Type'  => $type
100
            ]);
101 4
            $record->write();
102
        }
103
104 4
        return $record;
105
    }
106
107
    /**
108
     * @param array $ids
109
     * @param $record
110
     * @param Exception $e
111
     */
112
    protected function registerException(array $ids, $record, Exception $error): void
113
    {
114
        /** @var DataObject $owner */
115
        $owner = $this->owner;
116
        $ids[] = $owner->ID;
117
        // If we don't get an exception, mark the item as clean
118
        $record->IDs = json_encode($ids);
119
        $record->write();
120
        $logger = Injector::inst()->get(LoggerInterface::class);
121
        $logger->warn(
122
            sprintf(
123
                'Unable to alter %s with ID %s',
124
                $owner->ClassName,
125
                $owner->ID
126
            )
127
        );
128
        $logger->error($error->getMessage());
129
    }
130
131
    /**
132
     * @throws ValidationException
133
     */
134 1
    public function onAfterDelete(): void
135
    {
136
        /** @var DataObject $owner */
137 1
        $owner = $this->owner;
138
        /** @var DirtyClass $record */
139 1
        $record = $this->getDirtyClass($owner, self::DELETE);
140
141 1
        $ids = json_decode($record->IDs, 1) ?: [];
142 1
        parent::onAfterDelete();
143
        try {
144 1
            (new SolrCoreService())->updateItems(ArrayList::create([$owner]), SolrCoreService::DELETE_TYPE);
145
            // If successful, remove it from the array
146
            // Added bonus, array_flip removes duplicates
147 1
            $values = array_flip($ids);
148 1
            unset($values[$owner->ID]);
149
150 1
            $record->IDs = json_encode(array_keys($values));
151 1
            $record->write();
152
        } catch (Exception $error) {
153
            $this->registerException($ids, $record, $error);
154
        }
155 1
    }
156
157
    /**
158
     * Get the view status for each member in this object
159
     * @return array
160
     */
161 13
    public function getViewStatus(): array
162
    {
163
        // Return empty if it's not allowed to show in search
164
        // The setting needs to be explicitly false, to avoid any possible collision
165
        // with objects not having the setting, thus being `null`
166
        /** @var DataObject|SiteTree $owner */
167 13
        $owner = $this->owner;
168
        // Return immediately if the owner has ShowInSearch not being `null`
169 13
        if ($owner->ShowInSearch === false || $owner->ShowInSearch === 0) {
170 1
            return [];
171
        }
172
173 13
        if (isset(self::$canViewClasses[$owner->ClassName]) && !$owner->hasField('ShowInSearch')) {
174
            return self::$canViewClasses[$owner->ClassName];
175
        }
176
177 13
        return $this->getMemberPermissions($owner);
178
    }
179
180
    /**
181
     * @param DataObject|SiteTree $owner
182
     * @return array
183
     */
184 13
    protected function getMemberPermissions($owner): array
185
    {
186 13
        $return = [];
187
188
        // Add null users if it's publicly viewable
189 13
        if ($owner->canView()) {
190 13
            return ['1-null'];
191
        }
192
193
        if (!self::$members) {
194
            self::$members = Member::get();
195
        }
196
197
        foreach (self::$members as $member) {
198
            if ($owner->canView($member)) {
199
                $return[] = sprintf('1-%s', $member->ID);
200
            }
201
        }
202
203
        if (!$owner->hasField('ShowInSearch')) {
204
            self::$canViewClasses[$owner->ClassName] = $return;
205
        }
206
207
        return $return;
208
    }
209
}
210