Passed
Push — sheepy/introspection ( e59db3...71b5c6 )
by Simon
05:27
created

DataObjectExtension::onAfterWrite()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 32
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 6

Importance

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