Passed
Push — sheepy/introspection ( f93ae4...e91b37 )
by Simon
12:51 queued 10:13
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\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
        if (in_array($owner->ClassName, static::$excludedClasses, true) ||
61 63
            (Controller::curr()->getRequest()->getURL() &&
62 63
                strpos('dev/build', Controller::curr()->getRequest()->getURL()) !== false)
63
        ) {
64 62
            return;
65
        }
66 63
        if (!$owner->hasExtension(Versioned::class)) {
67 62
            $this->pushToSolr($owner);
68
        }
69 63
    }
70
71
    /**
72
     * @throws ValidationException
73
     */
74 2
    public function onAfterPublish()
75
    {
76
        /** @var DataObject $owner */
77 2
        $owner = $this->owner;
78 2
        $this->pushToSolr($owner);
79 2
    }
80
81
    /**
82
     * @param DataObject $owner
83
     * @param string $type
84
     * @return DirtyClass
85
     * @throws ValidationException
86
     */
87 63
    protected function getDirtyClass(DataObject $owner, $type)
88
    {
89
        // Get the DirtyClass object for this item
90
        /** @var null|DirtyClass $record */
91 63
        $record = DirtyClass::get()->filter(['Class' => $owner->ClassName, 'Type' => $type])->first();
92 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...
93 62
            $record = DirtyClass::create([
94 62
                'Class' => $owner->ClassName,
95 62
                'Type'  => $type
96
            ]);
97 62
            $record->write();
98
        }
99
100 63
        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 2
    public function onAfterDelete(): void
131
    {
132
        /** @var DataObject $owner */
133 2
        $owner = $this->owner;
134
        /** @var DirtyClass $record */
135 2
        $record = $this->getDirtyClass($owner, self::DELETE);
136
137 2
        $ids = json_decode($record->IDs, 1) ?: [];
138 2
        parent::onAfterDelete();
139
        try {
140 2
            (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 2
            $values = array_flip($ids);
144 2
            unset($values[$owner->ID]);
145
146 2
            $record->IDs = json_encode(array_keys($values));
147 2
            $record->write();
148
        } catch (Exception $error) {
149
            $this->registerException($ids, $record, $error);
150
        }
151 2
    }
152
153
    /**
154
     * Get the view status for each member in this object
155
     * @return array
156
     */
157 5
    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, thus being `null`
162
        /** @var DataObject|SiteTree $owner */
163 5
        $owner = $this->owner;
164
        // Return immediately if the owner has ShowInSearch not being `null`
165 5
        if ($owner->ShowInSearch === false || $owner->ShowInSearch === 0) {
166 1
            return [];
167
        }
168
169 5
        return self::$canViewClasses[$owner->ClassName] ?? $this->getMemberPermissions($owner);
170
    }
171
172
    /**
173
     * @param DataObject|SiteTree $owner
174
     * @return array
175
     */
176 5
    protected function getMemberPermissions($owner): array
177
    {
178
        // Log out the current user to avoid collisions in permissions
179 5
        $currMember = Security::getCurrentUser();
180 5
        Security::setCurrentUser(null);
181
182 5
        $return = [];
183
184 5
        if ($owner->canView(null)) {
185 3
            $return[] = '1-null';
186
        } else {
187
            // Return a default '0-0' to basically say "noboday can view"
188 3
            $return[] = '0-0';
189 3
            if (!self::$members) {
190 1
                self::$members = Member::get();
191
            }
192 3
            foreach (self::$members as $member) {
193 3
                $return[] = sprintf('%s-%s', (int)$owner->canView($member), (int)$member->ID);
194
            }
195
        }
196
197
198 5
        if (!$owner->hasField('ShowInSearch')) {
199 1
            self::$canViewClasses[$owner->ClassName] = $return;
200
        }
201
202 5
        Security::setCurrentUser($currMember);
203
204 5
        return $return;
205
    }
206
207
    /**
208
     * @param DataObject $owner
209
     * @throws ValidationException
210
     */
211 62
    protected function pushToSolr(DataObject $owner): void
212
    {
213
        /** @var DataObject $owner */
214 62
        $record = $this->getDirtyClass($owner, self::WRITE);
215
216 62
        $ids = json_decode($record->IDs, 1) ?: [];
217 62
        $mode = Versioned::get_reading_mode();
218
        try {
219 62
            Versioned::set_reading_mode(Versioned::DEFAULT_MODE);
220 62
            $service = new SolrCoreService();
221 62
            $service->setInDebugMode(false);
222 62
            $service->updateItems(ArrayList::create([$owner]), SolrCoreService::UPDATE_TYPE);
223
            // If we don't get an exception, mark the item as clean
224
            // Added bonus, array_flip removes duplicates
225 62
            $values = array_flip($ids);
226 62
            unset($values[$owner->ID]);
227
228 62
            $record->IDs = json_encode(array_keys($values));
229 62
            $record->write();
230 62
            Versioned::set_reading_mode($mode);
231
        } catch (Exception $error) {
232
            Versioned::set_reading_mode($mode);
233
            $this->registerException($ids, $record, $error);
234
        }
235 62
    }
236
}
237