Passed
Push — master ( ba7877...74673c )
by Nicolaas
02:22
created

SortOutFolders   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 157
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 30
eloc 79
c 1
b 0
f 0
dl 0
loc 157
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getListOfImages() 0 27 5
B getFieldDetails() 0 24 9
A getFolderArray() 0 16 4
B removeUnusedFiles() 0 26 8
A run() 0 16 4
1
<?php
2
3
namespace Sunnysideup\PerfectCmsImages\Api;
4
5
use SilverStripe\Control\Director;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\HTTPRequest;
8
use SilverStripe\Dev\BuildTask;
9
use SilverStripe\ORM\DB;
10
11
use SilverStripe\Assets\Image;
12
use SilverStripe\Assets\Folder;
13
14
use Sunnysideup\PerfectCmsImages\Api\PerfectCMSImages;
15
16
use SilverStripe\Core\Config\Config;
17
use SilverStripe\Core\Config\Configurable;
18
19
use SilverStripe\Core\ClassInfo;
20
21
use SilverStripe\Core\Injector\Injector;
22
class SortOutFolders
23
{
24
25
    use Configurable;
26
27
    /**
28
     * @var Folder
29
     */
30
    protected $unusedImagesFolder = null;
31
32
    /**
33
     *
34
     * @var bool
35
     */
36
    protected $debug = false;
37
38
    /**
39
     *
40
     * @var bool
41
     */
42
    protected $verbose = false;
43
44
    private static $unused_images_folder_name = 'unusedimages';
45
46
    /**
47
     * Create test jobs for the purposes of testing.
48
     * The array must contains arrays with
49
     * - folder
50
     * - used_by (has_one / has_many / many_many relation)
51
     *
52
     * @param HTTPRequest $request
53
     */
54
    public function run(string $unusedFolderName, array $data) // phpcs:ignore
0 ignored issues
show
Unused Code introduced by
The parameter $unusedFolderName is not used and could be removed. ( Ignorable by Annotation )

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

54
    public function run(/** @scrutinizer ignore-unused */ string $unusedFolderName, array $data) // phpcs:ignore

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
55
    {
56
57
        $folderArray = $this->getFolderArray($data);
58
        if ($this->verbose) {
59
            print_r($folderArray);
60
        }
61
62
        $listOfImageIds = $this->getListOfImages($folderArray);
63
        if ($this->verbose) {
64
            print_r($listOfImageIds);
65
        }
66
67
        // remove
68
        foreach($listOfImageIds as $folderName => $listOfIds) {
69
            $this->removeUnusedFiles($folderName, $listOfIds);
70
        }
71
    }
72
73
74
    protected function getFolderArray(array $data) :array
75
    {
76
77
        // check folders
78
        $folderArray = [];
79
        foreach($data as $dataInner) {
80
            $folder = $dataInner['folder'] ?? '';
81
            if($folder) {
82
                $folderArray[$folder] = [];
83
                $classes = $dataInner['used_by'] ?? [];
84
                foreach($classes as $classAndMethodList) {
85
                    $folderArray[$folder][$classAndMethodList] = $classAndMethodList;
86
                }
87
            }
88
        }
89
        return $folderArray;
90
    }
91
92
    protected function getListOfImages(array $folderArray) : array
93
    {
94
        $listOfImageIds = [];
95
        foreach($folderArray as $folderName => $classAndMethodList) {
96
97
            // find all images that should be there...
98
            $listOfIds = [];
99
            foreach($classAndMethodList as $classAndMethod) {
100
                list($className, $method) = explode('.', $classAndMethod);
101
                $fieldDetails = $this->getFieldDetails($className, $method);
102
                if(empty($field)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field seems to never exist and therefore empty should always be true.
Loading history...
103
                    user_error('Could not find relation: '.$className.'.'.$method);
104
                }
105
                if($fieldDetails['dataType'] === 'has_one') {
106
                    $list = $className::get()->columnUnique($method.'ID');
107
                } else {
108
                    $dataClassName = $fieldDetails['dataClassName'];
109
                    $list = $dataClassName::get()->relation($method)->columnUnique('ID');
110
                }
111
                $listOfImageIds = array_merge(
112
                    $listOfImageIds,
113
                    $list
114
                );
115
            }
116
            $listOfImageIds[$folderName] = $listOfIds;
117
        }
118
        return $listOfImageIds;
119
    }
120
121
    protected function removeUnusedFiles(string $folderName, array $listOfImageIds)
0 ignored issues
show
Unused Code introduced by
The parameter $listOfImageIds is not used and could be removed. ( Ignorable by Annotation )

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

121
    protected function removeUnusedFiles(string $folderName, /** @scrutinizer ignore-unused */ array $listOfImageIds)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $folderName is not used and could be removed. ( Ignorable by Annotation )

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

121
    protected function removeUnusedFiles(/** @scrutinizer ignore-unused */ string $folderName, array $listOfImageIds)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
122
    {
123
        $folder = Folder::find_or_make($this->config()->get('unused_images_folder_name'));
124
        $unusedFolderName = $this->unusedImagesFolder->Name;
125
        $where = " ParentID = " . $folder->ID. ' AND File.ID NOT IN('.implode('.$listOfImageIds.').')';
0 ignored issues
show
Bug introduced by
'.$listOfImageIds.' of type string is incompatible with the type array expected by parameter $pieces of implode(). ( Ignorable by Annotation )

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

125
        $where = " ParentID = " . $folder->ID. ' AND File.ID NOT IN('.implode(/** @scrutinizer ignore-type */ '.$listOfImageIds.').')';
Loading history...
126
        $unused = Image::get()->where($where);
127
        if ($unused->exists()) {
128
            foreach ($unused as $file) {
129
                $oldName = $file->getFullPath();
130
                if($this->verbose) {
131
                    DB::alteration_message('DEBUG ONLY '.$file->getFileName().' to '.$unusedFolderName);
132
                }
133
                if($this->debug) {
134
                    echo 'skipping as we are in debug mode';
135
                } else {
136
                    $file->ParentID = $this->unusedImagesFolder->ID;
137
                    $file->write();
138
                    $file->doPublish();
139
                    $newName = str_replace($folder->Name, $unusedFolderName, $oldName);
140
                    $oldNameFull = Controller::join_links(ASSETS_PATH, $oldName);
141
                    $newNameFull = Controller::join_links(ASSETS_PATH, $newName);
142
                    if (file_exists($oldNameFull) && $newNameFull !== $oldNameFull) {
143
                        if(file_exists($newNameFull)) {
144
                            unlink($newNameFull);
145
                        }
146
                        rename($oldNameFull, $newNameFull);
147
                    }
148
                }
149
            }
150
        }
151
    }
152
153
    protected static $my_cache = [];
154
155
    protected function getFieldDetails(string $originClassName, string $originMethod) : array
156
    {
157
        $key = $originClassName.'_'.$originMethod;
158
        if(! isset(self::$my_cache[$key])) {
159
            $types = ['has_one', 'has_many', 'many_many'];
160
            $classNames = ClassInfo::ancestry($originClassName, true);
161
            foreach ($classNames as $className) {
162
                $obj = Injector::inst()->get($className);
0 ignored issues
show
Unused Code introduced by
The assignment to $obj is dead and can be removed.
Loading history...
163
                foreach ($types as $type) {
164
                    $rels = Config::inst()->get($className, $type, Config::UNINHERITED);
165
                    if (is_array($rels) && ! empty($rels)) {
166
                        foreach ($rels as $relName => $relType) {
167
                            if (Image::class === $relType && $relName === $originatingFieldName) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $originatingFieldName seems to be never defined.
Loading history...
168
                                self::$my_cache[$key] = [
169
                                    'dataClassName' => $className,
170
                                    'dataType' => $type,
171
                                ];
172
                            }
173
                        }
174
                    }
175
                }
176
            }
177
        }
178
        return self::$my_cache[$key];
179
    }
180
181
182
}
183