mirror of
https://gitlab.com/davical-project/davical.git
synced 2026-01-27 00:33:34 +00:00
212 lines
7.2 KiB
PHP
Executable File
212 lines
7.2 KiB
PHP
Executable File
#!/usr/bin/env php
|
|
<?php
|
|
/**
|
|
* Script to move old events into an archive collection
|
|
*
|
|
* @package davical
|
|
* @subpackage alarms
|
|
* @author Andrew McMillan <andrew@morphoss.com>
|
|
* @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 <<<USAGE
|
|
Usage:
|
|
archive-old-events.php [-s server.domain.tld] -p principal [other options]
|
|
|
|
-a <archive_suffix> Appendeded (after a '-') to the name of the original calendar to give
|
|
the archive calendar name. Default 'archive'.
|
|
-o <duration> Archive events completed this much prior to the current
|
|
date. Default 'P-190D'
|
|
-p <principal> The name of the principal to do the archiving for (required).
|
|
-c <collection> The name of the collection to do the archiving for (required).
|
|
-s <server> 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 = <<<EOSQL
|
|
INSERT INTO collection (user_no, parent_container, dav_name, dav_etag, dav_displayname, is_calendar,
|
|
created, modified, public_events_only, publicly_readable, is_addressbook,
|
|
resourcetypes, schedule_transp, timezone, description)
|
|
SELECT user_no, parent_container, :archive_dav_name, random(),
|
|
'Archive of ' || dav_displayname, true, current_timestamp, current_timestamp, false, false, false,
|
|
resourcetypes, schedule_transp, timezone,
|
|
'Archive of ' || CASE WHEN description IS NULL OR description = '' THEN dav_name ELSE description END
|
|
FROM collection
|
|
WHERE dav_name = :collection_dav_name
|
|
AND NOT EXISTS(SELECT 1 FROM collection c2 WHERE c2.dav_name = :archive_dav_name)
|
|
EOSQL;
|
|
|
|
$collection_dav_name = sprintf( '/%s/%s/', $args->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 = <<<EOSQL
|
|
UPDATE caldav_data
|
|
SET dav_name = replace( caldav_data.dav_name, :collection_dav_name, :archive_dav_name),
|
|
collection_id = :archive_collection_id
|
|
FROM calendar_item
|
|
WHERE caldav_data.collection_id = :source_collection_id
|
|
AND caldav_data.caldav_type = 'VEVENT'
|
|
AND caldav_data.dav_id = calendar_item.dav_id
|
|
AND (
|
|
(rrule IS NULL AND dtend < :archive_before_date)
|
|
OR (last_instance_end is not null AND last_instance_end < :archive_before_date)
|
|
)
|
|
EOSQL;
|
|
|
|
if ( $args->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 );
|
|
}
|