Notifications by E-Mail implemented

pull/73/head
Tobias Hößl 2012-07-27 18:35:14 +00:00
parent 5dcbe8eaa5
commit 5b83872773
13 changed files with 245 additions and 20 deletions

View File

@ -54,6 +54,8 @@
the instance of an event, but not the end. the instance of an event, but not the end.
* Fixed: All-day recurring events wouldn't match if an occurence ended * Fixed: All-day recurring events wouldn't match if an occurence ended
exactly on the start of a time-range. exactly on the start of a time-range.
* Fixed: HTTP basic auth did not correctly deal with passwords containing
colons on some servers.
1.6.3-stable (2012-06-12) 1.6.3-stable (2012-06-12)
* Added: It's now possible to specify in Sabre_DAV_Client which type of * Added: It's now possible to specify in Sabre_DAV_Client which type of

View File

@ -223,7 +223,7 @@ function getDenormalizedData($calendarData) {
} }
} else { } else {
$it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID); $it = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->UID);
$maxDate = new DateTime(self::MAX_DATE); $maxDate = new DateTime(Sabre_CalDAV_Backend_PDO::MAX_DATE);
if ($it->isInfinite()) { if ($it->isInfinite()) {
$lastOccurence = $maxDate->getTimeStamp(); $lastOccurence = $maxDate->getTimeStamp();
} else { } else {

View File

@ -6,5 +6,8 @@ CREATE TABLE locks (
token VARCHAR(100), token VARCHAR(100),
scope TINYINT, scope TINYINT,
depth TINYINT, depth TINYINT,
uri text uri VARCHAR(1000),
INDEX(token),
INDEX(uri)
); );

View File

@ -46,7 +46,7 @@ class Sabre_HTTP_BasicAuth extends Sabre_HTTP_AbstractAuth {
if (strpos(strtolower($auth),'basic')!==0) return false; if (strpos(strtolower($auth),'basic')!==0) return false;
return explode(':', base64_decode(substr($auth, 6))); return explode(':', base64_decode(substr($auth, 6)),2);
} }

View File

@ -14,7 +14,7 @@ class Sabre_HTTP_Version {
/** /**
* Full version number * Full version number
*/ */
const VERSION = '1.6.2'; const VERSION = '1.6.4';
/** /**
* Stability : alpha, beta, stable * Stability : alpha, beta, stable

View File

@ -264,6 +264,27 @@ class Sabre_VObject_Component extends Sabre_VObject_Element {
} }
/**
* Validates the node for correctness.
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @return array
*/
public function validate() {
$result = array();
foreach($this->children as $child) {
$result = array_merge($result, $child->validate());
}
return $result;
}
/* Magic property accessors {{{ */ /* Magic property accessors {{{ */
/** /**

View File

@ -129,5 +129,110 @@ class Sabre_VObject_Component_VCalendar extends Sabre_VObject_Component {
} }
/**
* Validates the node for correctness.
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @return array
*/
public function validate() {
$warnings = array();
$version = $this->select('VERSION');
if (count($version)!==1) {
$warnings[] = array(
'level' => 1,
'message' => 'The VERSION property must appear in the VCALENDAR component exactly 1 time',
'node' => $this,
);
} else {
if ((string)$this->VERSION !== '2.0') {
$warnings[] = array(
'level' => 1,
'message' => 'Only iCalendar version 2.0 as defined in rfc5545 is supported.',
'node' => $this,
);
}
}
$version = $this->select('PRODID');
if (count($version)!==1) {
$warnings[] = array(
'level' => 2,
'message' => 'The PRODID property must appear in the VCALENDAR component exactly 1 time',
'node' => $this,
);
}
if (count($this->CALSCALE) > 1) {
$warnings[] = array(
'level' => 2,
'message' => 'The CALSCALE property must not be specified more than once.',
'node' => $this,
);
}
if (count($this->METHOD) > 1) {
$warnings[] = array(
'level' => 2,
'message' => 'The METHOD property must not be specified more than once.',
'node' => $this,
);
}
$allowedComponents = array(
'VEVENT',
'VTODO',
'VJOURNAL',
'VFREEBUSY',
'VTIMEZONE',
);
$allowedProperties = array(
'PRODID',
'VERSION',
'CALSCALE',
'METHOD',
);
$componentsFound = 0;
foreach($this->children as $child) {
if($child instanceof Sabre_VObject_Component) {
$componentsFound++;
if (!in_array($child->name, $allowedComponents)) {
$warnings[] = array(
'level' => 1,
'message' => 'The ' . $child->name . " component is not allowed in the VCALENDAR component",
'node' => $this,
);
}
}
if ($child instanceof Sabre_VObject_Property) {
if (!in_array($child->name, $allowedProperties)) {
$warnings[] = array(
'level' => 2,
'message' => 'The ' . $child->name . " property is not allowed in the VCALENDAR component",
'node' => $this,
);
}
}
}
if ($componentsFound===0) {
$warnings[] = array(
'level' => 1,
'message' => 'An iCalendar object must have at least 1 component.',
'node' => $this,
);
}
return array_merge(
$warnings,
parent::validate()
);
}
} }

View File

@ -32,6 +32,23 @@ abstract class Sabre_VObject_Node implements IteratorAggregate, ArrayAccess, Cou
*/ */
public $parent = null; public $parent = null;
/**
* Validates the node for correctness.
* An array is returned with warnings.
*
* Every item in the array has the following properties:
* * level - (number between 1 and 3 with severity information)
* * message - (human readable message)
* * node - (reference to the offending node)
*
* @return array
*/
public function validate() {
return array();
}
/* {{{ IteratorAggregator interface */ /* {{{ IteratorAggregator interface */
/** /**

View File

@ -60,6 +60,25 @@ class Sabre_HTTP_BasicAuthTest extends PHPUnit_Framework_TestCase {
} }
function testGetUserPassWithColon() {
$server = array(
'HTTP_AUTHORIZATION' => 'Basic ' . base64_encode('admin:1234:5678'),
);
$request = new Sabre_HTTP_Request($server);
$this->basicAuth->setHTTPRequest($request);
$userPass = $this->basicAuth->getUserPass();
$this->assertEquals(
array('admin','1234:5678'),
$userPass,
'We did not get the username and password we expected'
);
}
function testGetUserPassApacheEdgeCase() { function testGetUserPassApacheEdgeCase() {
$server = array( $server = array(

View File

@ -73,6 +73,8 @@ function renderCalDavEntry_data(&$calendar, &$calendarobject)
throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component'); throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
} }
$timezoneOffset = date("P"); // @TODO Get the actual timezone from the event
if ($componentType !== 'VEVENT') return; if ($componentType !== 'VEVENT') return;
@ -106,15 +108,15 @@ function renderCalDavEntry_data(&$calendar, &$calendarobject)
$start = $it->getDtStart()->getTimestamp(); $start = $it->getDtStart()->getTimestamp();
q("INSERT INTO %s%sjqcalendar (`calendar_id`, `calendarobject_id`, `Summary`, `StartTime`, `EndTime`, `IsEditable`, `IsAllDayEvent`, `IsRecurring`, `Color`) VALUES q("INSERT INTO %s%sjqcalendar (`calendar_id`, `calendarobject_id`, `Summary`, `StartTime`, `EndTime`, `IsEditable`, `IsAllDayEvent`, `IsRecurring`, `Color`) VALUES
(%d, %d, '%s', '%s', '%s', %d, %d, %d, '%s')", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, (%d, %d, '%s', CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), %d, %d, %d, '%s')",
IntVal($calendar["id"]), IntVal($calendarobject["id"]), dbesc($event["summary"]), date("Y-m-d H:i:s", $start), date("Y-m-d H:i:s", $last_end), CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), dbesc($event["summary"]), date("Y-m-d H:i:s", $start),
1, $allday, $recurring, dbesc(substr($event["color"], 1)) date("Y-m-d H:i:s", $last_end), 1, $allday, $recurring, dbesc(substr($event["color"], 1))
); );
foreach ($alarms as $alarm) { foreach ($alarms as $alarm) {
$alarm = renderCalDavEntry_calcalarm($alarm, $component); $alarm = renderCalDavEntry_calcalarm($alarm, $component);
$notified = ($alarm->getTimestamp() < time() ? 1 : 0); $notified = ($alarm->getTimestamp() < time() ? 1 : 0);
q("INSERT INTO %s%snotifications (`calendar_id`, `calendarobject_id`, `alert_date`, `notified`) VALUES (%d, %d, '%s', %d)", q("INSERT INTO %s%snotifications (`calendar_id`, `calendarobject_id`, `alert_date`, `notified`) VALUES (%d, %d, CONVERT_TZ('%s', '$timezoneOffset', @@session.time_zone), %d)",
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), $alarm->format("Y-m-d H:i:s"), $notified CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendar["id"]), IntVal($calendarobject["id"]), $alarm->format("Y-m-d H:i:s"), $notified
); );
} }

View File

@ -113,11 +113,12 @@ class Sabre_CalDAV_Backend_Private extends Sabre_CalDAV_Backend_Common
$calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId); $calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
$von = wdcal_php2MySqlTime($sd); $von = wdcal_php2MySqlTime($sd);
$bis = wdcal_php2MySqlTime($ed); $bis = wdcal_php2MySqlTime($ed);
$timezoneOffset = date("P");
// @TODO Events, die früher angefangen haben, aber noch andauern // @TODO Events, die früher angefangen haben, aber noch andauern
$evs = q("SELECT * FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `starttime` between '%s' and '%s'", $evs = q("SELECT *, CONVERT_TZ(`StartTime`, @@session.time_zone, '$timezoneOffset') StartTime, CONVERT_TZ(`EndTime`, @@session.time_zone, '$timezoneOffset') EndTime
CALDAV_SQL_DB, CALDAV_SQL_PREFIX, FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `StartTime` between '%s' and '%s'",
IntVal($calendarId), dbesc($von), dbesc($bis)); CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($von), dbesc($bis));
$events = array(); $events = array();
foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $calendar, $base_path . $row["calendar_id"] . "/"); foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $calendar, $base_path . $row["calendar_id"] . "/");

View File

@ -70,7 +70,7 @@ function wdcal_getEditPage_str(&$localization, $baseurl, $calendar_id, $uri)
if ($triggerDuration->s > 0) { if ($triggerDuration->s > 0) {
$unit = "second"; $unit = "second";
$value = $triggerDuration->s + $triggerDuration->i * 60 + $triggerDuration->h * 3600 + $triggerDuration->d * 3600 * 24; // @TODO support more than days? $value = $triggerDuration->s + $triggerDuration->i * 60 + $triggerDuration->h * 3600 + $triggerDuration->d * 3600 * 24; // @TODO support more than days?
} elseif ($triggerDuration->m) { } elseif ($triggerDuration->i) {
$unit = "minute"; $unit = "minute";
$value = $triggerDuration->i + $triggerDuration->h * 60 + $triggerDuration->d * 60 * 24; $value = $triggerDuration->i + $triggerDuration->h * 60 + $triggerDuration->d * 60 * 24;
} elseif ($triggerDuration->h) { } elseif ($triggerDuration->h) {
@ -143,7 +143,7 @@ function wdcal_getEditPage_str(&$localization, $baseurl, $calendar_id, $uri)
$out .= "<h2>" . t("Event data") . "</h2>"; $out .= "<h2>" . t("Event data") . "</h2>";
$out .= "<label for='calendar'>" . t("Calendar") . ":</label><select id='calendar' name='calendar' size='1'>"; $out .= "<label for='calendar' class='block'>" . t("Calendar") . ":</label><select id='calendar' name='calendar' size='1'>";
$found = false; $found = false;
$cal_col = "aaaaaa"; $cal_col = "aaaaaa";
foreach ($calendars as $cal) { foreach ($calendars as $cal) {
@ -462,14 +462,15 @@ function wdcal_getEditPage_str(&$localization, $baseurl, $calendar_id, $uri)
$out .= "<div class='noti_holder' "; $out .= "<div class='noti_holder' ";
if (!is_numeric($index) && $index == "new") $out .= "style='display: none;' id='noti_new_row'"; if (!is_numeric($index) && $index == "new") $out .= "style='display: none;' id='noti_new_row'";
$out .= "><label class='plain'>" . t("Notify by"); $out .= "><label class='block' for='noti_type_" . $index . "'>" . t("Notify by") . ":</label>";
$out .= "<select name='noti_type[$index]' size='1'>"; $out .= "<select name='noti_type[$index]' size='1' id='noti_type_" . $index . "'>";
$out .= "<option value=''>- " . t("Remove") . " -</option>\n"; $out .= "<option value=''>- " . t("Remove") . " -</option>\n";
$out .= "<option value='email' "; if (!$unparsable && $noti["action"] == "email") $out .= "selected"; $out .= ">" . t("E-Mail") . "</option>\n"; $out .= "<option value='email' "; if (!$unparsable && $noti["action"] == "email") $out .= "selected"; $out .= ">" . t("E-Mail") . "</option>\n";
$out .= "<option value='display' "; if (!$unparsable && $noti["action"] == "display") $out .= "selected"; $out .= ">" . t("On Friendica / Display") . "</option>\n"; $out .= "<option value='display' "; if (!$unparsable && $noti["action"] == "display") $out .= "selected"; $out .= ">" . t("On Friendica / Display") . "</option>\n";
//$out .= "<option value='other' "; if ($unparsable) $out .= "selected"; $out .= ">- " . t("other (leave it untouched)") . " -</option>\n"; // @TODO //$out .= "<option value='other' "; if ($unparsable) $out .= "selected"; $out .= ">- " . t("other (leave it untouched)") . " -</option>\n"; // @TODO
$out .= "</select></label>"; $out .= "</select><br>";
$out .= "<label class='block'>" . t("Time") . ":</label>";
$out .= "<input name='noti_value[$index]' size='5' style='width: 5em;' value='" . $noti["trigger_value"] . "'>"; $out .= "<input name='noti_value[$index]' size='5' style='width: 5em;' value='" . $noti["trigger_value"] . "'>";
$out .= "<select name='noti_unit[$index]' size='1'>"; $out .= "<select name='noti_unit[$index]' size='1'>";

View File

@ -7,6 +7,7 @@ function dav_install()
register_hook('event_created', 'addon/dav/dav.php', 'dav_event_created_hook'); register_hook('event_created', 'addon/dav/dav.php', 'dav_event_created_hook');
register_hook('event_updated', 'addon/dav/dav.php', 'dav_event_updated_hook'); register_hook('event_updated', 'addon/dav/dav.php', 'dav_event_updated_hook');
register_hook('profile_tabs', 'addon/dav/dav.php', 'dav_profile_tabs_hook'); register_hook('profile_tabs', 'addon/dav/dav.php', 'dav_profile_tabs_hook');
register_hook('cron', 'addon/dav/dav.php', 'dav_cron');
} }
@ -15,6 +16,7 @@ function dav_uninstall()
unregister_hook('event_created', 'addon/dav/dav.php', 'dav_event_created_hook'); unregister_hook('event_created', 'addon/dav/dav.php', 'dav_event_created_hook');
unregister_hook('event_updated', 'addon/dav/dav.php', 'dav_event_updated_hook'); unregister_hook('event_updated', 'addon/dav/dav.php', 'dav_event_updated_hook');
unregister_hook('profile_tabs', 'addon/dav/dav.php', 'dav_profile_tabs_hook'); unregister_hook('profile_tabs', 'addon/dav/dav.php', 'dav_profile_tabs_hook');
unregister_hook('cron', 'addon/dav/dav.php', 'dav_cron');
} }
@ -97,7 +99,7 @@ function dav_init(&$a)
} }
$server = dav_create_server(); $server = dav_create_server();
$browser = new Sabre_DAV_Browser_Plugin(); $browser = new Sabre_DAV_Browser_Plugin();
$server->addPlugin($browser); $server->addPlugin($browser);
$server->exec(); $server->exec();
@ -159,8 +161,8 @@ function dav_content()
} }
} else { } else {
$server = dav_create_server(true, true, false); $server = dav_create_server(true, true, false);
$cals = dav_get_current_user_calendars($server, DAV_ACL_READ); $cals = dav_get_current_user_calendars($server, DAV_ACL_READ);
$x = wdcal_printCalendar($cals, array(), $a->get_baseurl() . "/dav/wdcal/feed/", "week", 0, 200); $x = wdcal_printCalendar($cals, array(), $a->get_baseurl() . "/dav/wdcal/feed/", "week", 0, 200);
} }
} }
return $x; return $x;
@ -205,6 +207,58 @@ function dav_profile_tabs_hook(&$a, &$b)
); );
} }
/**
* @param App $a
* @param object $b
*/
function dav_cron(&$a, &$b)
{
dav_include_files();
$r = q("SELECT * FROM %s%snotifications WHERE `notified` = 0 AND `alert_date` <= NOW()", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
foreach ($r as $not) {
q("UPDATE %s%snotifications SET `notified` = 1 WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $not["id"]);
$event = q("SELECT * FROM %s%sjqcalendar WHERE `calendarobject_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $not["calendarobject_id"]);
$calendar = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB,CALDAV_SQL_PREFIX, $not["calendar_id"]);
$users = array();
if (count($calendar) != 1 || count($event) == 0) continue;
switch ($calendar[0]["namespace"]) {
case CALDAV_NAMESPACE_PRIVATE:
$user = q("SELECT * FROM user WHERE `uid` = %d AND `blocked` = 0", $calendar[0]["namespace_id"]);
if (count($user) != 1) continue;
$users[] = $user[0];
break;
}
switch ($not["action"]) {
case "email": case "display": // @TODO implement "Display"
foreach ($users as $user) {
$find = array( "%to%" , "%event%", "%url%");
$repl = array($user["username"], $event[0]["Summary"], $a->get_baseurl() . "/dav/wdcal/" . $calendar[0]["id"] . "/" . $not["calendarobject_id"] . "/");
$text_text = str_replace($find, $repl, "Hi %to%!\n\nThe event \"%event%\" is about to begin:\n%url%");
$text_html = str_replace($find, $repl, "Hi %to%!<br>\n<br>\nThe event \"%event%\" is about to begin:<br>\n<a href='" . "%url%" . "'>%url%</a>");
$params = array(
'fromName' => FRIENDICA_PLATFORM,
'fromEmail' => t('noreply') . '@' . $a->get_hostname(),
'replyTo' => t('noreply') . '@' . $a->get_hostname(),
'toEmail' => $user["email"],
'messageSubject' => t("Notification: " . $event[0]["Summary"]),
'htmlVersion' => $text_html,
'textVersion' => $text_text,
'additionalMailHeader' => "",
);
require_once('include/enotify.php');
enotify::send($params);
}
break;
}
}
}
/** /**
* @param App $a * @param App $a
* @param null|object $o * @param null|object $o