Issues (2564)

app/Services/HousekeepingService.php (1 issue)

Labels
Severity
1
<?php
2
3
/**
4
 * webtrees: online genealogy
5
 * Copyright (C) 2025 webtrees development team
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
 * GNU General Public License for more details.
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 */
17
18
declare(strict_types=1);
19
20
namespace Fisharebest\Webtrees\Services;
21
22
use Fisharebest\Webtrees\DB;
23
use League\Flysystem\FilesystemException;
24
use League\Flysystem\FilesystemOperator;
25
use League\Flysystem\FilesystemReader;
26
use League\Flysystem\UnableToDeleteDirectory;
27
use League\Flysystem\UnableToDeleteFile;
28
29
use function date;
30
use function time;
31
32
/**
33
 * Clean up old data, files and folders.
34
 */
35
class HousekeepingService
36
{
37
    // This is a list of old files and directories, from earlier versions of webtrees.
38
    // git diff 1.7.9..master --name-status | grep ^D
39
    private const array OLD_PATHS = [
0 ignored issues
show
A parse error occurred: Syntax error, unexpected T_STRING, expecting '=' on line 39 at column 24
Loading history...
40
        // Removed in 1.0.3
41
        'themechange.php',
42
        // Removed in 1.1.0
43
        'addremotelink.php',
44
        'addsearchlink.php',
45
        'client.php',
46
        'dir_editor.php',
47
        'editconfig_gedcom.php',
48
        'editgedcoms.php',
49
        'edit_merge.php',
50
        'edit_news.php',
51
        'genservice.php',
52
        'logs.php',
53
        'manageservers.php',
54
        'media.php',
55
        'module_admin.php',
56
        //'modules', // Do not delete - users may have stored custom modules/data here
57
        'opensearch.php',
58
        'PEAR.php',
59
        'pgv_to_wt.php',
60
        'places',
61
        //'robots.txt', // Do not delete this - it may contain user data
62
        'serviceClientTest.php',
63
        'siteconfig.php',
64
        'SOAP',
65
        'uploadmedia.php',
66
        'useradmin.php',
67
        'webservice',
68
        'wtinfo.php',
69
        // Removed in 1.1.2
70
        'treenav.php',
71
        // Removed in 1.2.3
72
        'modules_v2',
73
        // Removed in 1.2.4
74
        'search_engine.php',
75
        // Removed in 1.2.5
76
        'sidebar.php',
77
        // Removed in 1.2.6
78
        // Removed in 1.2.7
79
        'login_register.php',
80
        // Removed in 1.3.0
81
        'admin_site_ipaddress.php',
82
        'downloadgedcom.php',
83
        'export_gedcom.php',
84
        'gedcheck.php',
85
        'images',
86
        // Removed in 1.3.1
87
        'imageflush.php',
88
        '/lightbox/js/tip_balloon_RTL.js',
89
        // Removed in 1.4.0
90
        'imageview.php',
91
        'media/MediaInfo.txt',
92
        'media/thumbs/ThumbsInfo.txt',
93
        // Removed in 1.5.3
94
        'readme.html',
95
        // Removed in 1.6.0
96
        'downloadbackup.php',
97
        'site-php-version.php',
98
        // Removed in 1.7.0
99
        'admin_site_other.php',
100
        'js',
101
        'library',
102
        'save.php',
103
        // Removed in 1.7.2
104
        'assets/js-1.7.0',
105
        // Removed in 1.7.4
106
        'assets/js-1.7.2',
107
        // Removed in 1.7.7
108
        'assets/js-1.7.4',
109
        // Removed in 2.0.0
110
        'action.php',
111
        'addmedia.php',
112
        'addmin.php',
113
        'admin_media.php',
114
        'admin_media_upload.php',
115
        'admin_module_blocks.php',
116
        'admin_module_charts.php',
117
        'admin_module_menus.php',
118
        'admin_module_reports.php',
119
        'admin_module_sidebar.php',
120
        'admin_module_tabs.php',
121
        'admin_modules.php',
122
        'admin_pgv_to_wt.php',
123
        'admin_site_access.php',
124
        'admin_site_change.php',
125
        'admin_site_clean.php',
126
        'admin_site_config.php',
127
        'admin_site_info.php',
128
        'admin_site_logs.php',
129
        'admin_site_merge.php',
130
        'admin_site_readme.php',
131
        'admin_site_upgrade.php',
132
        'admin_trees_check.php',
133
        'admin_trees_config.php',
134
        'admin_trees_download.php',
135
        'admin_trees_duplicates.php',
136
        'admin_trees_export.php',
137
        'admin_trees_manage.php',
138
        'admin_trees_merge.php',
139
        'admin_trees_places.php',
140
        'admin_trees_renumber.php',
141
        'admin_trees_unconnected.php',
142
        'admin_users.php',
143
        'admin_users_bulk.php',
144
        'ancestry.php',
145
        'app/Controller',
146
        'app/HitCounter.php',
147
        'app/Module/ClippingsCart/ClippingsCartController.php',
148
        'app/Module/FamiliesSidebarModule.php',
149
        'app/Module/FamilyTreeFavorites',
150
        'app/Module/GoogleMaps',
151
        'app/Module/IndividualSidebarModule.php',
152
        'app/Module/PageMenuModule.php',
153
        'app/Query',
154
        'app/SpecialChars',
155
        'assets/js-1.7.7',
156
        'assets/js-1.7.9',
157
        'autocomplete.php',
158
        'block_edit.php',
159
        'branches.php',
160
        'calendar.php',
161
        'compact.php',
162
        'data/html_purifier_cache',
163
        'descendancy.php',
164
        'editnews.php',
165
        'edituser.php',
166
        'edit_changes.php',
167
        'edit_interface.php',
168
        'expand_view.php',
169
        'familybook.php',
170
        'famlist.php',
171
        'fanchart.php',
172
        'find.php',
173
        'help_text.php',
174
        'hourglass.php',
175
        'hourglass_ajax.php',
176
        'import.php',
177
        'includes',
178
        'index_edit.php',
179
        'indilist.php',
180
        'inverselink.php',
181
        'language',
182
        'lifespan.php',
183
        'login.php',
184
        'logout.php',
185
        'mediafirewall.php',
186
        'medialist.php',
187
        'message.php',
188
        'module.php',
189
        'modules_v3',
190
        'notelist.php',
191
        'packages',
192
        'pedigree.php',
193
        'relationship.php',
194
        'repolist.php',
195
        'reportengine.php',
196
        'search.php',
197
        'search_advanced.php',
198
        'site-offline.php',
199
        'site-unavailable.php',
200
        'sourcelist.php',
201
        'statistics.php',
202
        'statisticsplot.php',
203
        'themes',
204
        'timeline.php',
205
        // Removed in 2.0.3
206
        'public/css/vendor.css',
207
        // Removed in 2.0.4
208
        'public/favicon-120.png',
209
        'public/favicon-144.png',
210
        'public/favicon-57.png',
211
        'public/favicon-76.png',
212
        'public/favicon-96.png',
213
        // Removed in 2.0.7
214
        'public/ckeditor-4.11.2-custom',
215
        // Removed in 2.0.12
216
        'public/ckeditor-4.14.1-custom',
217
        // Removed in 2.1.0
218
        'modules_v4/example-footer.disable',
219
        'modules_v4/example-middleware.disable',
220
        'modules_v4/example-report.disable',
221
        'modules_v4/example-report.disable',
222
        'modules_v4/example-server-configuration.disable',
223
        'modules_v4/example-theme.disable',
224
        'modules_v4/example.disable',
225
        'public/favicon-196.png',
226
        'public/site.webmanifest',
227
    ];
228
229
    /**
230
     * Delete files and folders that belonged to an earlier version of webtrees.
231
     * Return a list of those that we could not delete.
232
     *
233
     * @param FilesystemOperator $filesystem
234
     *
235
     * @return array<string>
236
     */
237
    public function deleteOldWebtreesFiles(FilesystemOperator $filesystem): array
238
    {
239
        $paths_to_delete = [];
240
241
        foreach (self::OLD_PATHS as $path) {
242
            if (!$this->deleteFileOrFolder($filesystem, $path)) {
243
                $paths_to_delete[] = $path;
244
            }
245
        }
246
247
        return $paths_to_delete;
248
    }
249
250
    /**
251
     * Delete old cache files.
252
     *
253
     * @param FilesystemOperator $filesystem
254
     * @param string             $path
255
     * @param int                $max_age Seconds
256
     *
257
     * @return void
258
     */
259
    public function deleteOldFiles(FilesystemOperator $filesystem, string $path, int $max_age): void
260
    {
261
        $threshold = time() - $max_age;
262
263
        $list = $filesystem->listContents($path, FilesystemReader::LIST_DEEP);
264
265
        foreach ($list as $metadata) {
266
            // The timestamp can be absent or false.
267
            $timestamp = $metadata['timestamp'] ?? false;
268
269
            if ($timestamp !== false && $timestamp < $threshold) {
270
                $this->deleteFileOrFolder($filesystem, $metadata['path']);
271
            }
272
        }
273
    }
274
275
    /**
276
     * @param int $max_age_in_seconds
277
     *
278
     * @return void
279
     */
280
    public function deleteOldLogs(int $max_age_in_seconds): void
281
    {
282
        DB::table('log')
283
            ->whereIn('log_type', ['error', 'media'])
284
            ->where('log_time', '<', date('Y-m-d H:i:s', time() - $max_age_in_seconds))
285
            ->delete();
286
    }
287
288
    /**
289
     * @param int $max_age_in_seconds
290
     *
291
     * @return void
292
     */
293
    public function deleteOldSessions(int $max_age_in_seconds): void
294
    {
295
        DB::table('session')
296
            ->where('session_time', '<', date('Y-m-d H:i:s', time() - $max_age_in_seconds))
297
            ->delete();
298
    }
299
300
    /**
301
     * Delete a file or folder, if we can.
302
     *
303
     * @param FilesystemOperator $filesystem
304
     * @param string             $path
305
     *
306
     * @return bool
307
     */
308
    private function deleteFileOrFolder(FilesystemOperator $filesystem, string $path): bool
309
    {
310
        try {
311
            $filesystem->delete($path);
312
        } catch (FilesystemException | UnableToDeleteFile) {
313
            try {
314
                $filesystem->deleteDirectory($path);
315
            } catch (FilesystemException | UnableToDeleteDirectory) {
316
                return false;
317
            }
318
        }
319
320
        return true;
321
    }
322
}
323