#!/usr/bin/env php * @copyright Morphoss Ltd * @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later */ $script_file = __FILE__; chdir(str_replace('/scripts/archive-old-events.php','/htdocs',$script_file)); $_SERVER['SERVER_NAME'] = 'localhost'; /** * Call with something like e.g.: * * scripts/archive-old-events.php -a archive -p karora -c calendar -o P-93D * */ $args = (object) array(); $args->debug = false; $args->principal = false; $args->collection = false; $args->older = 'P-190D'; $args->delete = false; $args->archive_suffix = 'archive'; $debugging = null; function parse_arguments() { global $args; $opts = getopt( 'a:c:o:p:s:dh' ); foreach( $opts AS $k => $v ) { switch( $k ) { case 'a': $args->archive_suffix = $v; break; case 'c': $args->collection = $v; break; case 'o': $args->older = $v; break; case 'p': $args->principal = $v; break; case 's': $_SERVER['SERVER_NAME'] = $v; break; case 'd': $args->debug = true; $debugging = explode(',',$v); break; case 'h': usage(); break; default: $args->{$k} = $v; } } $bad = false; if ( $args->principal === false ) { echo "You must supply a principal.\n"; $bad = true; } if ( $args->collection === false ) { echo "You must supply a collection.\n"; $bad = true; } if ( $bad ) { usage(); } } function usage() { echo << Appendeded (after a '-') to the name of the original calendar to give the archive calendar name. Default 'archive'. -o Archive events completed this much prior to the current date. Default 'P-190D' -p The name of the principal to do the archiving for (required). -c The name of the collection to do the archiving for (required). -s The servername to be used to identify the DAViCal configuration file. -d xxx Enable debugging where 'xxx' is a comma-separated list of debug subsystems USAGE; exit(0); } parse_arguments(); if ( $args->debug && is_array($debugging )) { foreach( $debugging AS $v ) { $c->dbg[$v] = 1; } } require_once("./always.php"); require_once('AwlQuery.php'); require_once('AwlCache.php'); require_once('RRule.php'); require_once('vCalendar.php'); /** * Essentially what we are doing is: * DELETE FROM caldav_data JOIN calendar_item USING(dav_id) WHERE dav_name LIKE '/someprincipal/%' AND ( (rrule IS NULL AND dtend < archive_before_date) OR (last_instance_end < archive_before_date) ) */ $recent = new RepeatRuleDateTime(gmdate('Ymd\THis\Z')); $recent->modify('P-6D'); $archive_before_date = new RepeatRuleDateTime(gmdate('Ymd\THis\Z')); $archive_before_date->modify( $args->older ); if ( $archive_before_date > $recent ) { echo "Cowardly refusing to archive events before " . $archive_before_date->format('Y-m-d H:i:s') . "\n"; exit(1); } if ( $args->debug ) printf( "Archiving event instances finished before '%s'\n", $archive_before_date->UTC() ); // SQL to create an archive collection but only if it doesn't exist already. $archive_collection_sql = <<principal, $args->collection ); $collection_archive = sprintf( '/%s/%s-%s/', $args->principal, $args->collection, $args->archive_suffix ); $sqlargs = array( ':collection_dav_name' => $collection_dav_name, ':archive_dav_name' => $collection_archive ); $qry = new AwlQuery($archive_collection_sql, $sqlargs); $qry->query_time_warning = 5; // Don't warn on queries unless they take more than 5 seconds. if ( $qry->Exec(__CLASS__, __LINE__, __FILE__) ) { $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = ?', $collection_dav_name ); if ( $qry->rows() != 1 ) { printf( "Could not find source collection '%s'\n", $collection_dav_name); exit(1); } $row = $qry->Fetch(); $source_collection_id = $row->collection_id; $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = ?', $collection_archive ); if ( $qry->rows() != 1 ) { printf( "Could not create archive collection '%s'!\n", $collection_archive); exit(2); } $row = $qry->Fetch(); $archive_collection_id = $row->collection_id; $archive_sql = <<debug ) printf( "%s\n", $archive_sql ); $sqlargs[':archive_before_date'] = $archive_before_date->FloatOrUTC(); $sqlargs[':source_collection_id'] = $source_collection_id; $sqlargs[':archive_collection_id'] = $archive_collection_id; $qry->QDo($archive_sql, $sqlargs); /** * At this point we've done all the work, we just have to inform the rest of the world that * everything has changed underneath it. */ // Now ensure the collection tag changes... $sql = 'UPDATE collection SET dav_etag = random(), modified = current_timestamp WHERE collection_id IN (:source_id, :archive_id)'; $sqlargs = array( ':source_id' => $source_collection_id, ':archive_id' => $archive_collection_id ); $qry->QDo($sql, $sqlargs); // Delete the sync tokens... $sql = 'DELETE FROM sync_tokens WHERE collection_id IN (:source_id, :archive_id)'; $qry->QDo($sql, $sqlargs); // Delete the sync_changes... $sql = 'DELETE FROM sync_changes WHERE collection_id IN (:source_id, :archive_id)'; $qry->QDo($sql, $sqlargs); // Uncache anything to do with the collection, or the archive $cache = getCacheInstance(); $cache_ns = 'collection-'.preg_replace( '{/[^/]*$}', '/', $collection_dav_name); $cache->delete( $cache_ns, null ); $cache_ns = 'collection-'.preg_replace( '{/[^/]*$}', '/', $collection_archive); $cache->delete( $cache_ns, null ); }