Passed
Push — sheepy/introspection ( 17b395...901708 )
by Simon
08:33 queued 05:46
created

DataObjectExtension   A

Complexity

Total Complexity 25

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Test Coverage

Coverage 78.31%

Importance

Changes 11
Bugs 0 Features 1
Metric Value
wmc 25
eloc 83
c 11
b 0
f 1
dl 0
loc 201
ccs 65
cts 83
cp 0.7831
rs 10

8 Methods

Rating   Name   Duplication   Size   Complexity  
A onAfterPublish() 0 3 1
A onAfterDelete() 0 20 3
A getDirtyClass() 0 14 3
A registerException() 0 17 1
A pushToSolr() 0 23 3
A getViewStatus() 0 13 3
A onAfterWrite() 0 13 6
A getMemberPermissions() 0 29 5
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\Security\Security;
22
use SilverStripe\SiteConfig\SiteConfig;
23
use SilverStripe\Versioned\ChangeSet;
24
use SilverStripe\Versioned\ChangeSetItem;
25
use SilverStripe\Versioned\Versioned;
26
27
/**
28
 * Class \Firesphere\SolrSearch\Compat\DataObjectExtension
29
 *
30
 * @property CarouselItem|BlocksPage|Item|File|Image|SiteConfig|ChangeSetItem|SecurityAlert|Package|ElementalArea|ElementForm|Blog|SiteTree|Group|Member|EditableCustomRule|EditableFormField|UserDefinedForm|EditableOption|DataObjectExtension $owner
31
 */
32
class DataObjectExtension extends DataExtension
33
{
34
    public const WRITE = 'write';
35
    public const DELETE = 'delete';
36
    /**
37
     * @var array
38
     */
39
    public static $canViewClasses = [];
40
    /**
41
     * @var DataList
42
     */
43
    protected static $members;
44
    /**
45
     * @var array
46
     */
47
    protected static $excludedClasses = [
48
        DirtyClass::class,
49
        ChangeSet::class,
50
        ChangeSetItem::class,
51
    ];
52
53
    /**
54
     * @throws ValidationException
55
     */
56 63
    public function onAfterWrite()
57
    {
58
        /** @var DataObject $owner */
59 63
        $owner = $this->owner;
60 63
        parent::onAfterWrite();
61 63
        if (in_array($owner->ClassName, static::$excludedClasses, true) ||
62 63
            (Controller::curr()->getRequest()->getURL() &&
63 63
                strpos('dev/build', Controller::curr()->getRequest()->getURL()) !== false)
64
        ) {
65 62
            return;
66
        }
67 63
        if (!$owner->hasExtension(Versioned::class) || !$owner->isPublished()) {
68 63
            $this->pushToSolr($owner);
69
        }
70 63
    }
71
72
    /**
73
     * @throws ValidationException
74
     */
75 2
    public function onAfterPublish()
76
    {
77 2
        $this->pushToSolr($this->owner);
0 ignored issues
show
Bug introduced by
It seems like $this->owner can also be of type Firesphere\SolrSearch\Ex...ons\DataObjectExtension; however, parameter $owner of Firesphere\SolrSearch\Ex...Extension::pushToSolr() does only seem to accept SilverStripe\ORM\DataObject, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

77
        $this->pushToSolr(/** @scrutinizer ignore-type */ $this->owner);
Loading history...
78 2
    }
79
80
    /**
81
     * @param DataObject $owner
82
     * @param string $type
83
     * @return DirtyClass
84
     * @throws ValidationException
85
     */
86 63
    protected function getDirtyClass(DataObject $owner, $type)
87
    {
88
        // Get the DirtyClass object for this item
89
        /** @var null|DirtyClass $record */
90 63
        $record = DirtyClass::get()->filter(['Class' => $owner->ClassName, 'Type' => $type])->first();
91 63
        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...
92 62
            $record = DirtyClass::create([
93 62
                'Class' => $owner->ClassName,
94 62
                'Type'  => $type
95
            ]);
96 62
            $record->write();
97
        }
98
99 63
        return $record;
100
    }
101
102
    /**
103
     * @param array $ids
104
     * @param $record
105
     * @param Exception $e
106
     */
107
    protected function registerException(array $ids, $record, Exception $error): void
108
    {
109
        /** @var DataObject $owner */
110
        $owner = $this->owner;
111
        $ids[] = $owner->ID;
112
        // If we don't get an exception, mark the item as clean
113
        $record->IDs = json_encode($ids);
114
        $record->write();
115
        $logger = Injector::inst()->get(LoggerInterface::class);
116
        $logger->warn(
117
            sprintf(
118
                'Unable to alter %s with ID %s',
119
                $owner->ClassName,
120
                $owner->ID
121
            )
122
        );
123
        $logger->error($error->getMessage());
124
    }
125
126
    /**
127
     * @throws ValidationException
128
     */
129 2
    public function onAfterDelete(): void
130
    {
131
        /** @var DataObject $owner */
132 2
        $owner = $this->owner;
133
        /** @var DirtyClass $record */
134 2
        $record = $this->getDirtyClass($owner, self::DELETE);
135
136 2
        $ids = json_decode($record->IDs, 1) ?: [];
137 2
        parent::onAfterDelete();
138
        try {
139 2
            (new SolrCoreService())->updateItems(ArrayList::create([$owner]), SolrCoreService::DELETE_TYPE);
140
            // If successful, remove it from the array
141
            // Added bonus, array_flip removes duplicates
142 2
            $values = array_flip($ids);
143 2
            unset($values[$owner->ID]);
144
145 2
            $record->IDs = json_encode(array_keys($values));
146 2
            $record->write();
147
        } catch (Exception $error) {
148
            $this->registerException($ids, $record, $error);
149
        }
150 2
    }
151
152
    /**
153
     * Get the view status for each member in this object
154
     * @return array
155
     */
156 18
    public function getViewStatus(): array
157
    {
158
        // Return empty if it's not allowed to show in search
159
        // The setting needs to be explicitly false, to avoid any possible collision
160
        // with objects not having the setting, thus being `null`
161
        /** @var DataObject|SiteTree $owner */
162 18
        $owner = $this->owner;
163
        // Return immediately if the owner has ShowInSearch not being `null`
164 18
        if ($owner->ShowInSearch === false || $owner->ShowInSearch === 0) {
165 1
            return [];
166
        }
167
168 18
        return self::$canViewClasses[$owner->ClassName] ?? $this->getMemberPermissions($owner);
169
    }
170
171
    /**
172
     * @param DataObject|SiteTree $owner
173
     * @return array
174
     */
175 18
    protected function getMemberPermissions($owner): array
176
    {
177
        // Log out the current user to avoid collisions in permissions
178 18
        $currMember = Security::getCurrentUser();
179 18
        Security::setCurrentUser(null);
180
181 18
        $return = [];
182
183 18
        if ($owner->canView(null)) {
184 17
            $return[] = '1-null';
185
        } else {
186
            // Return a default '0-0' to basically say "noboday can view"
187 3
            $return[] = '0-0';
188 3
            if (!self::$members) {
189 1
                self::$members = Member::get();
190
            }
191 3
            foreach (self::$members as $member) {
192 3
                $return[] = sprintf('%s-%s', (int)$owner->canView($member), (int)$member->ID);
193
            }
194
        }
195
196
197 18
        if (!$owner->hasField('ShowInSearch')) {
198 1
            self::$canViewClasses[$owner->ClassName] = $return;
199
        }
200
201 18
        Security::setCurrentUser($currMember);
202
203 18
        return $return;
204
    }
205
206
    /**
207
     * @param DataObject $owner
208
     * @throws ValidationException
209
     */
210 63
    protected function pushToSolr(DataObject $owner): void
211
    {
212
        /** @var DataObject $owner */
213 63
        $record = $this->getDirtyClass($owner, self::WRITE);
214
215 63
        $ids = json_decode($record->IDs, 1) ?: [];
216 63
        $mode = Versioned::get_reading_mode();
217
        try {
218 63
            Versioned::set_reading_mode(Versioned::DEFAULT_MODE);
219 63
            $service = new SolrCoreService();
220 63
            $service->setInDebugMode(false);
221 63
            $service->updateItems(ArrayList::create([$owner]), SolrCoreService::UPDATE_TYPE);
222
            // If we don't get an exception, mark the item as clean
223
            // Added bonus, array_flip removes duplicates
224 63
            $values = array_flip($ids);
225 63
            unset($values[$owner->ID]);
226
227 63
            $record->IDs = json_encode(array_keys($values));
228 63
            $record->write();
229 63
            Versioned::set_reading_mode($mode);
230
        } catch (Exception $error) {
231
            Versioned::set_reading_mode($mode);
232
            $this->registerException($ids, $record, $error);
233
        }
234 63
    }
235
}
236