Passed
Push — feature/Logging ( d4b3c6...39dd77 )
by Simon
04:52
created

DataObjectExtension::onAfterDelete()   A

Complexity

Conditions 3
Paths 6

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3.0416

Importance

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