4 * Script to refresh the pending alarm times for the next alarm instance.
8 * @author Andrew McMillan <andrew@morphoss.com>
9 * @copyright Morphoss Ltd
10 * @license http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
12 $script_file = __FILE__
;
14 chdir(str_replace('/scripts/refresh-alarms.php','/htdocs',$script_file));
15 $_SERVER['SERVER_NAME'] = 'localhost';
18 * Call with something like e.g.:
20 * scripts/refresh_alarms.php -p P1800D -f P1D
24 $args = (object) array();
26 $args->set_last
= false;
28 $args->future
= 'P400D';
29 $args->near_past
= 'P1D';
33 function parse_arguments() {
36 $opts = getopt( 'f:p:s:d:lh' );
37 foreach( $opts AS $k => $v ) {
39 case 'f': $args->future
= $v; break;
40 case 'p': $args->near_past
= $v; break;
41 case 's': $_SERVER['SERVER_NAME'] = $v; break;
42 case 'd': $args->debug
= true; $debugging = explode(',',$v); break;
43 case 'l': $args->set_last
= true; break;
44 case 'h': usage(); break;
45 default: $args->{$k} = $v;
54 refresh-alarms.php [-s server.domain.tld] [other options]
56 -s <server> The servername to be used to identify the DAViCal configuration file.
57 -p <duration> Near past period to review for finding recently last instances: default 1 days ('P1D')
58 -f <duration> Future period to consider for finding future alarms: default ~5 years ('P2000D')
60 -l Try to set the 'last' alarm date in historical alarms
62 -d xxx Enable debugging where 'xxx' is a comma-separated list of debug subsystems
70 if ( $args->debug
&& is_array($debugging )) {
71 foreach( $debugging AS $v ) {
75 $args->near_past
= '-' . $args->near_past
;
77 require_once("./always.php");
78 require_once('AwlQuery.php');
79 require_once('RRule-v2.php');
80 require_once('vCalendar.php');
84 * Essentially what we are doing is:
87 SET next_trigger = (SELECT rrule_event_instances_range(
88 dtstart + icalendar_interval_to_SQL(replace(trigger,'TRIGGER:','')),
90 current_timestamp, current_timestamp + '2 days'::interval,
94 WHERE calendar_alarm.dav_id = calendar_item.dav_id
95 AND next_trigger is null
99 $expand_range_start = new RepeatRuleDateTime(gmdate('Ymd\THis\Z'));
100 $expand_range_end = new RepeatRuleDateTime(gmdate('Ymd\THis\Z'));
101 $expand_range_end->modify( $args->future
);
105 $earliest = clone($expand_range_start);
106 $earliest->modify( $args->near_past
);
108 if ( $args->debug
) printf( "Looking for event instances between '%s' and '%s'\n", $earliest->UTC(), $expand_range_end->UTC() );
110 $sql = 'SELECT * FROM calendar_alarm JOIN calendar_item USING (dav_id) JOIN caldav_data USING (dav_id) WHERE rrule IS NOT NULL AND next_trigger IS NULL';
111 if ( $args->debug
) printf( "%s\n", $sql );
112 $qry = new AwlQuery( $sql );
113 if ( $qry->Exec() && $qry->rows() ) {
114 while( $alarm = $qry->Fetch() ) {
115 if ( $args->debug
) printf( "refresh: Processing alarm for '%s' based on '%s','%s', '%s'\n",
116 $alarm->dav_name
, $alarm->dtstart
, $alarm->rrule
, $alarm->trigger
);
117 $ic = new vComponent( $alarm->caldav_data
);
118 $expanded = expand_event_instances( $ic, $earliest, $expand_range_end );
119 $expanded->MaskComponents( array( 'VEVENT'=>1, 'VTODO'=>1, 'VJOURNAL'=>1 ) );
120 $instances = $expanded->GetComponents();
122 $trigger = new vProperty( $alarm->trigger
);
123 $related = $trigger->GetParameterValue('RELATED');
125 $first = new RepeatRuleDateTime($alarm->dtstart
);
126 $first->modify( $trigger->Value() );
129 foreach( $instances AS $k => $component ) {
130 $when = new RepeatRuleDateTime( $component->GetPValue('DTSTART') ); // a UTC value
131 if ( $args->debug
) printf( "refresh: Looking at event instance on '%s'\n", $when->UTC() );
132 if ( $related == 'END' ) {
133 $when->modify( $component->GetPValue('DURATION') );
135 $when->modify( $trigger->Value() );
136 if ( $when > $expand_range_start && $when < $expand_range_end && (!isset($next) ||
$when < $next) ) {
137 $next = clone($when);
139 if ( $args->set_last
&& (!isset($last) ||
$when > $last) ) {
140 $last = clone($when);
143 $trigger_type = $trigger->GetParameterValue('VALUE');
144 if ( $trigger_type == 'DATE' ||
$trigger_type == 'DATE-TIME' ||
preg_match('{^\d{8}T\d{6}Z?$}', $trigger->Value()) ) {
145 $first = new RepeatRuleDateTime($trigger);
146 if ( $first > $expand_range_start && (empty($next) ||
$first < $next ) )
148 else if ( empty($next) ) {
149 if ( $args->set_last
&& (empty($last) ||
$first > $last) )
153 if ( $args->set_last
&& !isset($last) && (!isset($next) ||
$next < $expand_range_Start) ) {
154 $vc = new vCalendar( $alarm->caldav_data
);
155 $range = getVCalendarRange($vc);
156 if ( isset($range->until
) && $range->until
< $earliest ) $last = $range->until
;
159 if ( isset($next) && $next < $expand_range_end ) {
160 if ( $args->debug
) printf( "refresh: Found next alarm instance on '%s'\n", $next->UTC() );
161 $sql = 'UPDATE calendar_alarm SET next_trigger = :next WHERE dav_id = :id AND component = :component';
162 $update = new AwlQuery( $sql, array( ':next' => $next->UTC(), ':id' => $alarm->dav_id
, ':component' => $alarm->component
) );
163 $update->Exec('refresh-alarms', __LINE__
, __FILE__
);
165 else if ( $args->set_last
&& isset($last) && $last < $earliest ) {
166 if ( $args->debug
) printf( "refresh: Found past final alarm instance on '%s'\n", $last->UTC() );
167 $sql = 'UPDATE calendar_alarm SET next_trigger = :last WHERE dav_id = :id AND component = :component';
168 $update = new AwlQuery( $sql, array( ':last' => $last->UTC(), ':id' => $alarm->dav_id
, ':component' => $alarm->component
) );
169 $update->Exec('refresh-alarms', __LINE__
, __FILE__
);
171 else if ( $args->debug
&& isset($next) && $next < $expand_range_end ) {
172 printf( "refresh: Found next alarm instance on '%s' after '%s'\n", $next->UTC(), $expand_range_end->UTC() );