2017-11-18 07:22:41 +01:00
< ? php
/**
* @ file src / worker / CronJobs . php
*/
2017-11-18 08:33:44 +01:00
namespace Friendica\Worker ;
2017-11-18 07:22:41 +01:00
use Friendica\App ;
2018-07-20 04:15:21 +02:00
use Friendica\BaseObject ;
2017-11-18 07:22:41 +01:00
use Friendica\Core\Cache ;
use Friendica\Core\Config ;
2018-07-20 14:19:26 +02:00
use Friendica\Database\DBA ;
2017-12-07 15:04:24 +01:00
use Friendica\Model\Contact ;
2017-12-07 15:09:28 +01:00
use Friendica\Model\GContact ;
2018-01-04 03:01:41 +01:00
use Friendica\Model\Photo ;
2018-02-18 23:19:20 +01:00
use Friendica\Model\User ;
2017-11-18 07:22:41 +01:00
use Friendica\Network\Probe ;
use Friendica\Protocol\PortableContact ;
2017-12-17 21:24:57 +01:00
require_once 'include/dba.php' ;
require_once 'mod/nodeinfo.php' ;
2017-12-04 04:15:31 +01:00
class CronJobs
{
public static function execute ( $command = '' )
{
2018-07-20 04:15:21 +02:00
$a = BaseObject :: getApp ();
2017-11-18 07:22:41 +01:00
// No parameter set? So return
if ( $command == '' ) {
return ;
}
2017-12-04 04:15:31 +01:00
logger ( " Starting cronjob " . $command , LOGGER_DEBUG );
2017-11-18 07:22:41 +01:00
// Call possible post update functions
2018-01-25 16:08:56 +01:00
// see src/Database/PostUpdate.php for more details
2017-11-18 07:22:41 +01:00
if ( $command == 'post_update' ) {
2018-07-15 20:36:20 +02:00
// Post updates will be reenabled (hopefully in a few days) when most item works are done
// PostUpdate::update();
2017-11-18 07:22:41 +01:00
return ;
}
// update nodeinfo data
if ( $command == 'nodeinfo' ) {
nodeinfo_cron ();
return ;
}
// Expire and remove user entries
if ( $command == 'expire_and_remove_users' ) {
self :: expireAndRemoveUsers ();
return ;
}
if ( $command == 'update_contact_birthdays' ) {
2018-02-03 18:25:58 +01:00
Contact :: updateBirthdays ();
2017-11-18 07:22:41 +01:00
return ;
}
if ( $command == 'update_photo_albums' ) {
self :: updatePhotoAlbums ();
return ;
}
// Clear cache entries
if ( $command == 'clear_cache' ) {
self :: clearCache ( $a );
return ;
}
// Repair missing Diaspora values in contacts
if ( $command == 'repair_diaspora' ) {
self :: repairDiaspora ( $a );
return ;
}
// Repair entries in the database
if ( $command == 'repair_database' ) {
self :: repairDatabase ();
return ;
}
2017-12-04 04:15:31 +01:00
logger ( " Xronjob " . $command . " is unknown. " , LOGGER_DEBUG );
2017-11-18 07:22:41 +01:00
return ;
}
/**
* @ brief Update the cached values for the number of photo albums per user
*/
2017-12-04 04:15:31 +01:00
private static function updatePhotoAlbums ()
{
2017-11-18 07:22:41 +01:00
$r = q ( " SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed` " );
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2017-11-18 07:22:41 +01:00
return ;
}
2018-01-04 04:36:15 +01:00
foreach ( $r as $user ) {
Photo :: clearAlbumCache ( $user [ 'uid' ]);
2017-11-18 07:22:41 +01:00
}
}
/**
* @ brief Expire and remove user entries
*/
2017-12-04 04:15:31 +01:00
private static function expireAndRemoveUsers ()
{
2018-02-18 23:19:20 +01:00
// expire any expired regular accounts. Don't expire forums.
$condition = [ " NOT `account_expired` AND `account_expires_on` > ? AND `account_expires_on` < UTC_TIMESTAMP() AND `page-flags` = 0 " , NULL_DATE ];
2018-07-20 14:19:26 +02:00
DBA :: update ( 'user' , [ 'account_expired' => true ], $condition );
2018-02-18 23:19:20 +01:00
// Remove any freshly expired account
2018-07-20 14:19:26 +02:00
$users = DBA :: select ( 'user' , [ 'uid' ], [ 'account_expired' => true , 'account_removed' => false ]);
while ( $user = DBA :: fetch ( $users )) {
2018-02-18 23:19:20 +01:00
User :: remove ( $user [ 'uid' ]);
}
2017-11-18 07:22:41 +01:00
// delete user records for recently removed accounts
2018-07-20 14:19:26 +02:00
$users = DBA :: select ( 'user' , [ 'uid' ], [ " `account_removed` AND `account_expires_on` < UTC_TIMESTAMP() - INTERVAL 3 DAY " ]);
while ( $user = DBA :: fetch ( $users )) {
DBA :: delete ( 'user' , [ 'uid' => $user [ 'uid' ]]);
2017-11-18 07:22:41 +01:00
}
}
/**
* @ brief Clear cache entries
*
* @ param App $a
*/
2017-12-04 04:15:31 +01:00
private static function clearCache ( App $a )
{
$last = Config :: get ( 'system' , 'cache_last_cleared' );
2017-11-18 07:22:41 +01:00
if ( $last ) {
$next = $last + ( 3600 ); // Once per hour
$clear_cache = ( $next <= time ());
} else {
$clear_cache = true ;
}
if ( ! $clear_cache ) {
return ;
}
// clear old cache
Cache :: clear ();
// clear old item cache files
clear_cache ();
// clear cache for photos
2017-12-04 04:15:31 +01:00
clear_cache ( $a -> get_basepath (), $a -> get_basepath () . " /photo " );
2017-11-18 07:22:41 +01:00
// clear smarty cache
2017-12-04 04:15:31 +01:00
clear_cache ( $a -> get_basepath () . " /view/smarty3/compiled " , $a -> get_basepath () . " /view/smarty3/compiled " );
2017-11-18 07:22:41 +01:00
// clear cache for image proxy
if ( ! Config :: get ( " system " , " proxy_disabled " )) {
2017-12-04 04:15:31 +01:00
clear_cache ( $a -> get_basepath (), $a -> get_basepath () . " /proxy " );
2017-11-18 07:22:41 +01:00
2017-12-04 04:15:31 +01:00
$cachetime = Config :: get ( 'system' , 'proxy_cache_time' );
2017-11-18 07:22:41 +01:00
if ( ! $cachetime ) {
$cachetime = PROXY_DEFAULT_TIME ;
}
2018-01-15 14:05:12 +01:00
$condition = [ '`uid` = 0 AND `resource-id` LIKE "pic:%" AND `created` < NOW() - INTERVAL ? SECOND' , $cachetime ];
2018-07-20 14:19:26 +02:00
DBA :: delete ( 'photo' , $condition );
2017-11-18 07:22:41 +01:00
}
2017-11-22 08:21:19 +01:00
// Delete the cached OEmbed entries that are older than three month
2018-07-20 14:19:26 +02:00
DBA :: delete ( 'oembed' , [ " `created` < NOW() - INTERVAL 3 MONTH " ]);
2017-11-18 07:22:41 +01:00
2017-11-22 08:21:19 +01:00
// Delete the cached "parse_url" entries that are older than three month
2018-07-20 14:19:26 +02:00
DBA :: delete ( 'parsed_url' , [ " `created` < NOW() - INTERVAL 3 MONTH " ]);
2017-11-18 07:22:41 +01:00
// Maximum table size in megabyte
2017-12-04 04:15:31 +01:00
$max_tablesize = intval ( Config :: get ( 'system' , 'optimize_max_tablesize' )) * 1000000 ;
2017-11-18 07:22:41 +01:00
if ( $max_tablesize == 0 ) {
$max_tablesize = 100 * 1000000 ; // Default are 100 MB
}
if ( $max_tablesize > 0 ) {
// Minimum fragmentation level in percent
2017-12-04 04:15:31 +01:00
$fragmentation_level = intval ( Config :: get ( 'system' , 'optimize_fragmentation' )) / 100 ;
2017-11-18 07:22:41 +01:00
if ( $fragmentation_level == 0 ) {
$fragmentation_level = 0.3 ; // Default value is 30%
}
// Optimize some tables that need to be optimized
$r = q ( " SHOW TABLE STATUS " );
foreach ( $r as $table ) {
// Don't optimize tables that are too large
if ( $table [ " Data_length " ] > $max_tablesize ) {
continue ;
}
// Don't optimize empty tables
if ( $table [ " Data_length " ] == 0 ) {
continue ;
}
// Calculate fragmentation
$fragmentation = $table [ " Data_free " ] / ( $table [ " Data_length " ] + $table [ " Index_length " ]);
2017-12-04 04:15:31 +01:00
logger ( " Table " . $table [ " Name " ] . " - Fragmentation level: " . round ( $fragmentation * 100 , 2 ), LOGGER_DEBUG );
2017-11-18 07:22:41 +01:00
// Don't optimize tables that needn't to be optimized
if ( $fragmentation < $fragmentation_level ) {
continue ;
}
// So optimize it
2017-12-04 04:15:31 +01:00
logger ( " Optimize Table " . $table [ " Name " ], LOGGER_DEBUG );
2018-07-21 15:10:13 +02:00
q ( " OPTIMIZE TABLE `%s` " , DBA :: escape ( $table [ " Name " ]));
2017-11-18 07:22:41 +01:00
}
}
2017-12-04 04:15:31 +01:00
Config :: set ( 'system' , 'cache_last_cleared' , time ());
2017-11-18 07:22:41 +01:00
}
/**
* @ brief Repair missing values in Diaspora contacts
*
* @ param App $a
*/
2017-12-04 04:15:31 +01:00
private static function repairDiaspora ( App $a )
{
2017-11-18 07:22:41 +01:00
$starttime = time ();
$r = q ( " SELECT `id`, `url` FROM `contact`
WHERE `network` = '%s' AND ( `batch` = '' OR `notify` = '' OR `poll` = '' OR pubkey = '' )
2018-07-21 15:10:13 +02:00
ORDER BY RAND () LIMIT 50 " , DBA::escape(NETWORK_DIASPORA));
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $r )) {
2017-11-18 07:22:41 +01:00
return ;
}
foreach ( $r AS $contact ) {
// Quit the loop after 3 minutes
if ( time () > ( $starttime + 180 )) {
return ;
}
if ( ! PortableContact :: reachable ( $contact [ " url " ])) {
continue ;
}
$data = Probe :: uri ( $contact [ " url " ]);
if ( $data [ " network " ] != NETWORK_DIASPORA ) {
continue ;
}
2017-12-04 04:15:31 +01:00
logger ( " Repair contact " . $contact [ " id " ] . " " . $contact [ " url " ], LOGGER_DEBUG );
2017-11-18 07:22:41 +01:00
q ( " UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d " ,
2018-07-21 15:10:13 +02:00
DBA :: escape ( $data [ " batch " ]), DBA :: escape ( $data [ " notify " ]), DBA :: escape ( $data [ " poll " ]), DBA :: escape ( $data [ " pubkey " ]),
2017-11-18 07:22:41 +01:00
intval ( $contact [ " id " ]));
}
}
/**
* @ brief Do some repairs in database entries
*
*/
2017-12-04 04:15:31 +01:00
private static function repairDatabase ()
{
2017-11-18 07:22:41 +01:00
// Sometimes there seem to be issues where the "self" contact vanishes.
// We haven't found the origin of the problem by now.
$r = q ( " SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`) " );
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2017-11-18 07:22:41 +01:00
foreach ( $r AS $user ) {
2017-12-04 04:15:31 +01:00
logger ( 'Create missing self contact for user ' . $user [ 'uid' ]);
2017-12-04 04:29:06 +01:00
Contact :: createSelfFromUserId ( $user [ 'uid' ]);
2017-11-18 07:22:41 +01:00
}
}
// Set the parent if it wasn't set. (Shouldn't happen - but does sometimes)
// This call is very "cheap" so we can do it at any time without a problem
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 " );
// There was an issue where the nick vanishes from the contact table
q ( " UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`='' " );
// Update the global contacts for local users
$r = q ( " SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired` " );
2018-07-21 14:46:04 +02:00
if ( DBA :: isResult ( $r )) {
2017-11-18 07:22:41 +01:00
foreach ( $r AS $user ) {
2017-12-07 15:09:28 +01:00
GContact :: updateForUser ( $user [ " uid " ]);
2017-11-18 07:22:41 +01:00
}
}
/// @todo
/// - remove thread entries without item
/// - remove sign entries without item
/// - remove children when parent got lost
/// - set contact-id in item when not present
}
}