]> git.mxchange.org Git - friendica.git/blob - src/Worker/CronJobs.php
926a1aff5af85bcb60eb0423f4cc27b32dc3b70a
[friendica.git] / src / Worker / CronJobs.php
1 <?php
2 /**
3  * @file src/worker/CronJobs.php
4  */
5 namespace Friendica\Worker;
6
7 use Friendica\App;
8 use Friendica\BaseObject;
9 use Friendica\Core\Cache;
10 use Friendica\Core\Config;
11 use Friendica\Database\DBA;
12 use Friendica\Database\PostUpdate;
13 use Friendica\Model\Contact;
14 use Friendica\Model\GContact;
15 use Friendica\Model\Photo;
16 use Friendica\Model\User;
17 use Friendica\Network\Probe;
18 use Friendica\Protocol\PortableContact;
19
20 require_once 'include/dba.php';
21 require_once 'mod/nodeinfo.php';
22
23 class CronJobs
24 {
25         public static function execute($command = '')
26         {
27                 $a = BaseObject::getApp();
28
29                 // No parameter set? So return
30                 if ($command == '') {
31                         return;
32                 }
33
34                 logger("Starting cronjob " . $command, LOGGER_DEBUG);
35
36                 // Call possible post update functions
37                 // see src/Database/PostUpdate.php for more details
38                 if ($command == 'post_update') {
39 // Post updates will be reenabled (hopefully in a few days) when most item works are done
40 //                      PostUpdate::update();
41                         return;
42                 }
43
44                 // update nodeinfo data
45                 if ($command == 'nodeinfo') {
46                         nodeinfo_cron();
47                         return;
48                 }
49
50                 // Expire and remove user entries
51                 if ($command == 'expire_and_remove_users') {
52                         self::expireAndRemoveUsers();
53                         return;
54                 }
55
56                 if ($command == 'update_contact_birthdays') {
57                         Contact::updateBirthdays();
58                         return;
59                 }
60
61                 if ($command == 'update_photo_albums') {
62                         self::updatePhotoAlbums();
63                         return;
64                 }
65
66                 // Clear cache entries
67                 if ($command == 'clear_cache') {
68                         self::clearCache($a);
69                         return;
70                 }
71
72                 // Repair missing Diaspora values in contacts
73                 if ($command == 'repair_diaspora') {
74                         self::repairDiaspora($a);
75                         return;
76                 }
77
78                 // Repair entries in the database
79                 if ($command == 'repair_database') {
80                         self::repairDatabase();
81                         return;
82                 }
83
84                 logger("Xronjob " . $command . " is unknown.", LOGGER_DEBUG);
85
86                 return;
87         }
88
89         /**
90          * @brief Update the cached values for the number of photo albums per user
91          */
92         private static function updatePhotoAlbums()
93         {
94                 $r = q("SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`");
95                 if (!DBA::isResult($r)) {
96                         return;
97                 }
98
99                 foreach ($r as $user) {
100                         Photo::clearAlbumCache($user['uid']);
101                 }
102         }
103
104         /**
105          * @brief Expire and remove user entries
106          */
107         private static function expireAndRemoveUsers()
108         {
109                 // expire any expired regular accounts. Don't expire forums.
110                 $condition = ["NOT `account_expired` AND `account_expires_on` > ? AND `account_expires_on` < UTC_TIMESTAMP() AND `page-flags` = 0", NULL_DATE];
111                 DBA::update('user', ['account_expired' => true], $condition);
112
113                 // Remove any freshly expired account
114                 $users = DBA::select('user', ['uid'], ['account_expired' => true, 'account_removed' => false]);
115                 while ($user = DBA::fetch($users)) {
116                         User::remove($user['uid']);
117                 }
118
119                 // delete user records for recently removed accounts
120                 $users = DBA::select('user', ['uid'], ["`account_removed` AND `account_expires_on` < UTC_TIMESTAMP() - INTERVAL 3 DAY"]);
121                 while ($user = DBA::fetch($users)) {
122                         DBA::delete('user', ['uid' => $user['uid']]);
123                 }
124         }
125
126         /**
127          * @brief Clear cache entries
128          *
129          * @param App $a
130          */
131         private static function clearCache(App $a)
132         {
133                 $last = Config::get('system', 'cache_last_cleared');
134
135                 if ($last) {
136                         $next = $last + (3600); // Once per hour
137                         $clear_cache = ($next <= time());
138                 } else {
139                         $clear_cache = true;
140                 }
141
142                 if (!$clear_cache) {
143                         return;
144                 }
145
146                 // clear old cache
147                 Cache::clear();
148
149                 // clear old item cache files
150                 clear_cache();
151
152                 // clear cache for photos
153                 clear_cache($a->get_basepath(), $a->get_basepath() . "/photo");
154
155                 // clear smarty cache
156                 clear_cache($a->get_basepath() . "/view/smarty3/compiled", $a->get_basepath() . "/view/smarty3/compiled");
157
158                 // clear cache for image proxy
159                 if (!Config::get("system", "proxy_disabled")) {
160                         clear_cache($a->get_basepath(), $a->get_basepath() . "/proxy");
161
162                         $cachetime = Config::get('system', 'proxy_cache_time');
163                         if (!$cachetime) {
164                                 $cachetime = PROXY_DEFAULT_TIME;
165                         }
166                         $condition = ['`uid` = 0 AND `resource-id` LIKE "pic:%" AND `created` < NOW() - INTERVAL ? SECOND', $cachetime];
167                         DBA::delete('photo', $condition);
168                 }
169
170                 // Delete the cached OEmbed entries that are older than three month
171                 DBA::delete('oembed', ["`created` < NOW() - INTERVAL 3 MONTH"]);
172
173                 // Delete the cached "parse_url" entries that are older than three month
174                 DBA::delete('parsed_url', ["`created` < NOW() - INTERVAL 3 MONTH"]);
175
176                 // Maximum table size in megabyte
177                 $max_tablesize = intval(Config::get('system', 'optimize_max_tablesize')) * 1000000;
178                 if ($max_tablesize == 0) {
179                         $max_tablesize = 100 * 1000000; // Default are 100 MB
180                 }
181                 if ($max_tablesize > 0) {
182                         // Minimum fragmentation level in percent
183                         $fragmentation_level = intval(Config::get('system', 'optimize_fragmentation')) / 100;
184                         if ($fragmentation_level == 0) {
185                                 $fragmentation_level = 0.3; // Default value is 30%
186                         }
187
188                         // Optimize some tables that need to be optimized
189                         $r = q("SHOW TABLE STATUS");
190                         foreach ($r as $table) {
191
192                                 // Don't optimize tables that are too large
193                                 if ($table["Data_length"] > $max_tablesize) {
194                                         continue;
195                                 }
196
197                                 // Don't optimize empty tables
198                                 if ($table["Data_length"] == 0) {
199                                         continue;
200                                 }
201
202                                 // Calculate fragmentation
203                                 $fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]);
204
205                                 logger("Table " . $table["Name"] . " - Fragmentation level: " . round($fragmentation * 100, 2), LOGGER_DEBUG);
206
207                                 // Don't optimize tables that needn't to be optimized
208                                 if ($fragmentation < $fragmentation_level) {
209                                         continue;
210                                 }
211
212                                 // So optimize it
213                                 logger("Optimize Table " . $table["Name"], LOGGER_DEBUG);
214                                 q("OPTIMIZE TABLE `%s`", dbesc($table["Name"]));
215                         }
216                 }
217
218                 Config::set('system', 'cache_last_cleared', time());
219         }
220
221         /**
222          * @brief Repair missing values in Diaspora contacts
223          *
224          * @param App $a
225          */
226         private static function repairDiaspora(App $a)
227         {
228                 $starttime = time();
229
230                 $r = q("SELECT `id`, `url` FROM `contact`
231                         WHERE `network` = '%s' AND (`batch` = '' OR `notify` = '' OR `poll` = '' OR pubkey = '')
232                                 ORDER BY RAND() LIMIT 50", dbesc(NETWORK_DIASPORA));
233                 if (!DBA::isResult($r)) {
234                         return;
235                 }
236
237                 foreach ($r AS $contact) {
238                         // Quit the loop after 3 minutes
239                         if (time() > ($starttime + 180)) {
240                                 return;
241                         }
242
243                         if (!PortableContact::reachable($contact["url"])) {
244                                 continue;
245                         }
246
247                         $data = Probe::uri($contact["url"]);
248                         if ($data["network"] != NETWORK_DIASPORA) {
249                                 continue;
250                         }
251
252                         logger("Repair contact " . $contact["id"] . " " . $contact["url"], LOGGER_DEBUG);
253                         q("UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d",
254                                 dbesc($data["batch"]), dbesc($data["notify"]), dbesc($data["poll"]), dbesc($data["pubkey"]),
255                                 intval($contact["id"]));
256                 }
257         }
258
259         /**
260          * @brief Do some repairs in database entries
261          *
262          */
263         private static function repairDatabase()
264         {
265                 // Sometimes there seem to be issues where the "self" contact vanishes.
266                 // We haven't found the origin of the problem by now.
267                 $r = q("SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`)");
268                 if (DBA::isResult($r)) {
269                         foreach ($r AS $user) {
270                                 logger('Create missing self contact for user ' . $user['uid']);
271                                 Contact::createSelfFromUserId($user['uid']);
272                         }
273                 }
274
275                 // Set the parent if it wasn't set. (Shouldn't happen - but does sometimes)
276                 // This call is very "cheap" so we can do it at any time without a problem
277                 q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0");
278
279                 // There was an issue where the nick vanishes from the contact table
280                 q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
281
282                 // Update the global contacts for local users
283                 $r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`");
284                 if (DBA::isResult($r)) {
285                         foreach ($r AS $user) {
286                                 GContact::updateForUser($user["uid"]);
287                         }
288                 }
289
290                 /// @todo
291                 /// - remove thread entries without item
292                 /// - remove sign entries without item
293                 /// - remove children when parent got lost
294                 /// - set contact-id in item when not present
295         }
296 }