Passed
Push — hans/logtests ( 76c086...71695d )
by Simon
06:24 queued 02:27
created

DataObjectExtension::onAfterDelete()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0987

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 9
c 2
b 0
f 0
dl 0
loc 16
ccs 7
cts 9
cp 0.7778
rs 9.9666
cc 3
nc 3
nop 0
crap 3.0987
1
<?php
2
3
4
namespace Firesphere\SolrSearch\Extensions;
5
6
use Exception;
7
use Firesphere\SolrSearch\Helpers\SolrLogger;
8
use Firesphere\SolrSearch\Models\DirtyClass;
9
use Firesphere\SolrSearch\Services\SolrCoreService;
10
use GuzzleHttp\Exception\GuzzleException;
11
use Psr\Log\LoggerInterface;
12
use SilverStripe\Assets\File;
13
use SilverStripe\CMS\Model\SiteTree;
14
use SilverStripe\Control\Controller;
15
use SilverStripe\Core\Injector\Injector;
16
use SilverStripe\ORM\ArrayList;
17
use SilverStripe\ORM\DataExtension;
18
use SilverStripe\ORM\DataList;
19
use SilverStripe\ORM\DataObject;
20
use SilverStripe\ORM\ValidationException;
21
use SilverStripe\Security\Group;
22
use SilverStripe\Security\Member;
23
use SilverStripe\Security\Security;
24
use SilverStripe\SiteConfig\SiteConfig;
25
use SilverStripe\Versioned\ChangeSet;
26
use SilverStripe\Versioned\ChangeSetItem;
27
use SilverStripe\Versioned\Versioned;
28
29
/**
30
 * Class \Firesphere\SolrSearch\Compat\DataObjectExtension
31
 *
32
 * @property CarouselItem|BlocksPage|Item|File|Image|SiteConfig|ChangeSetItem|SecurityAlert|Package|ElementalArea|ElementForm|Blog|SiteTree|Group|Member|EditableCustomRule|EditableFormField|UserDefinedForm|EditableOption|DataObjectExtension $owner
33
 */
34
class DataObjectExtension extends DataExtension
35
{
36
    public const WRITE = 'write';
37
    public const DELETE = 'delete';
38
    /**
39
     * @var array
40
     */
41
    public static $canViewClasses = [];
42
    /**
43
     * @var DataList
44
     */
45
    protected static $members;
46
    /**
47
     * @var array
48
     */
49
    protected static $excludedClasses = [
50
        DirtyClass::class,
51
        ChangeSet::class,
52
        ChangeSetItem::class,
53
    ];
54
55
    /**
56
     * @throws ValidationException
57
     */
58 67
    public function onAfterWrite()
59
    {
60
        /** @var DataObject $owner */
61 67
        $owner = $this->owner;
62 67
        if (in_array($owner->ClassName, static::$excludedClasses, true) ||
63 67
            (Controller::curr()->getRequest()->getURL() &&
64 67
                strpos('dev/build', Controller::curr()->getRequest()->getURL()) !== false)
65
        ) {
66 65
            return;
67
        }
68 67
        if (!$owner->hasExtension(Versioned::class)) {
69 67
            $this->pushToSolr($owner);
70
        }
71 67
    }
72
73
    /**
74
     * @param DataObject $owner
75
     * @throws ValidationException
76
     */
77 67
    protected function pushToSolr(DataObject $owner): void
78
    {
79
        /** @var DataObject $owner */
80 67
        $record = $this->getDirtyClass($owner, self::WRITE);
81
82 67
        $ids = json_decode($record->IDs, 1) ?: [];
83 67
        $mode = Versioned::get_reading_mode();
84
        try {
85 67
            Versioned::set_reading_mode(Versioned::DEFAULT_MODE);
86 67
            $service = new SolrCoreService();
87 67
            $service->setInDebugMode(false);
88 67
            $service->updateItems(ArrayList::create([$owner]), SolrCoreService::UPDATE_TYPE);
89
            // If we don't get an exception, mark the item as clean
90
            // Added bonus, array_flip removes duplicates
91 67
            $this->clearIDs($owner, $ids, $record);
92 67
            Versioned::set_reading_mode($mode);
93
        } catch (Exception $error) {
94
            Versioned::set_reading_mode($mode);
95
            $solrLogger = new SolrLogger();
96
            $solrLogger->saveSolrLog('Config');
97
98
            $this->registerException($ids, $record, $error);
99
        }
100 67
    }
101
102
    /**
103
     * @param DataObject $owner
104
     * @param string $type
105
     * @return DirtyClass
106
     * @throws ValidationException
107
     */
108 67
    protected function getDirtyClass(DataObject $owner, $type)
109
    {
110
        // Get the DirtyClass object for this item
111
        /** @var null|DirtyClass $record */
112 67
        $record = DirtyClass::get()->filter(['Class' => $owner->ClassName, 'Type' => $type])->first();
113 67
        if (!$record || !$record->exists()) {
114 65
            $record = DirtyClass::create([
115 65
                'Class' => $owner->ClassName,
116 65
                'Type'  => $type
117
            ]);
118 65
            $record->write();
119
        }
120
121 67
        return $record;
122
    }
123
124
    /**
125
     * @param DataObject $owner
126
     * @param array $ids
127
     * @param DirtyClass $record
128
     * @throws ValidationException
129
     */
130 67
    protected function clearIDs(DataObject $owner, array $ids, DirtyClass $record): void
131
    {
132 67
        $values = array_flip($ids);
133 67
        unset($values[$owner->ID]);
134
135 67
        $record->IDs = json_encode(array_keys($values));
136 67
        $record->write();
137 67
    }
138
139
    /**
140
     * @param array $ids
141
     * @param $record
142
     * @param Exception $error
143
     * @throws ValidationException
144
     * @throws GuzzleException
145
     */
146
    protected function registerException(array $ids, $record, Exception $error): void
147
    {
148
        /** @var DataObject $owner */
149
        $owner = $this->owner;
150
        $ids[] = $owner->ID;
151
        // If we don't get an exception, mark the item as clean
152
        $record->IDs = json_encode($ids);
153
        $record->write();
154
        $logger = Injector::inst()->get(LoggerInterface::class);
155
        $logger->warn(
156
            sprintf(
157
                'Unable to alter %s with ID %s',
158
                $owner->ClassName,
159
                $owner->ID
160
            )
161
        );
162
        $solrLogger = new SolrLogger();
163
        $solrLogger->saveSolrLog('Index');
164
165
        $logger->error($error->getMessage());
166
    }
167
168
    /**
169
     * @throws ValidationException
170
     */
171 2
    public function onAfterPublish()
172
    {
173
        /** @var DataObject $owner */
174 2
        $owner = $this->owner;
175 2
        $this->pushToSolr($owner);
176 2
    }
177
178
    /**
179
     * @throws ValidationException
180
     * @throws GuzzleException
181
     */
182 2
    public function onAfterDelete(): void
183
    {
184
        /** @var DataObject $owner */
185 2
        $owner = $this->owner;
186
        /** @var DirtyClass $record */
187 2
        $record = $this->getDirtyClass($owner, self::DELETE);
188
189 2
        $ids = json_decode($record->IDs, 1) ?: [];
190 2
        parent::onAfterDelete();
191
        try {
192 2
            (new SolrCoreService())->updateItems(ArrayList::create([$owner]), SolrCoreService::DELETE_TYPE);
193
            // If successful, remove it from the array
194
            // Added bonus, array_flip removes duplicates
195 2
            $this->clearIDs($owner, $ids, $record);
196
        } catch (Exception $error) {
197
            $this->registerException($ids, $record, $error);
198
        }
199 2
    }
200
201
    /**
202
     * Get the view status for each member in this object
203
     * @return array
204
     */
205 5
    public function getViewStatus(): array
206
    {
207
        // Return empty if it's not allowed to show in search
208
        // The setting needs to be explicitly false, to avoid any possible collision
209
        // with objects not having the setting, thus being `null`
210
        /** @var DataObject|SiteTree $owner */
211 5
        $owner = $this->owner;
212
        // Return immediately if the owner has ShowInSearch not being `null`
213 5
        if ($owner->ShowInSearch === false || $owner->ShowInSearch === 0) {
214 1
            return [];
215
        }
216
217 5
        return self::$canViewClasses[$owner->ClassName] ?? $this->getMemberPermissions($owner);
218
    }
219
220
    /**
221
     * @param DataObject|SiteTree $owner
222
     * @return array
223
     */
224 4
    protected function getMemberPermissions($owner): array
225
    {
226
        // Log out the current user to avoid collisions in permissions
227 4
        $currMember = Security::getCurrentUser();
228 4
        Security::setCurrentUser(null);
229
230 4
        if ($owner->canView(null)) {
231 3
            self::$canViewClasses[$owner->ClassName] = ['1-null'];
232
233
            // Anyone can view
234 3
            return ['1-null'];
235
        }
236
        // Return a default '0-0' to basically say "noboday can view"
237 2
        $return = ['0-0'];
238 2
        foreach (self::getMembers() as $member) {
239 2
            $return[] = sprintf('%s-%s', (int)$owner->canView($member), (int)$member->ID);
240
        }
241
242 2
        if (!$owner->hasField('ShowInSearch')) {
243
            self::$canViewClasses[$owner->ClassName] = $return;
244
        }
245
246 2
        Security::setCurrentUser($currMember);
247
248 2
        return $return;
249
    }
250
251
    /**
252
     * @return DataList
253
     */
254 2
    protected static function getMembers()
255
    {
256 2
        if (empty(self::$members)) {
257 1
            self::$members = Member::get();
258
        }
259
260 2
        return self::$members;
261
    }
262
}
263