diff --git a/apps/calendar/ajax/events.php b/apps/calendar/ajax/events.php
index 098e539a2de0322c72ce18119646dbe409cdc8e8..a912889f292729418e58e63cd04c4c3d8d01e77a 100755
--- a/apps/calendar/ajax/events.php
+++ b/apps/calendar/ajax/events.php
@@ -30,16 +30,26 @@ OC_JSON::checkAppEnabled('calendar');
 $start = DateTime::createFromFormat('U', $_GET['start']);
 $end = DateTime::createFromFormat('U', $_GET['end']);
 
-$calendar = OC_Calendar_App::getCalendar($_GET['calendar_id']);
-OC_Response::enableCaching(0);
-OC_Response::setETagHeader($calendar['ctag']);
+$calendar_id = $_GET['calendar_id'];
+if (is_numeric($calendar_id)) {
+	$calendar = OC_Calendar_App::getCalendar($calendar_id);
+	OC_Response::enableCaching(0);
+	OC_Response::setETagHeader($calendar['ctag']);
+	$events = OC_Calendar_Object::allInPeriod($calendar_id, $start, $end);
+} else {
+	$events = array();
+	OC_Hook::emit('OC_Calendar', 'getEvents', array('calendar_id' => $calendar_id, 'events' => &$events));
+}
 
-$events = OC_Calendar_Object::allInPeriod($_GET['calendar_id'], $start, $end);
 $user_timezone = OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'timezone', date_default_timezone_get());
 $return = array();
 foreach($events as $event){
-	$object = OC_VObject::parse($event['calendardata']);
-	$vevent = $object->VEVENT;
+	if (isset($event['calendardata'])) {
+		$object = OC_VObject::parse($event['calendardata']);
+		$vevent = $object->VEVENT;
+	} else {
+		$vevent = $event['vevent'];
+	}
 
 	$return_event = create_return_event($event, $vevent);
 
diff --git a/apps/calendar/index.php b/apps/calendar/index.php
index 12b51f564b4bb8e0dba40d4a6d121aa090128178..c00a4098f7ad7f4a3f919dac828894b13cbd5981 100644
--- a/apps/calendar/index.php
+++ b/apps/calendar/index.php
@@ -9,16 +9,20 @@
 require_once ('../../lib/base.php');
 OC_Util::checkLoggedIn();
 OC_Util::checkAppEnabled('calendar');
+
 // Create default calendar ...
 $calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1);
 if( count($calendars) == 0){
 	OC_Calendar_Calendar::addCalendar(OC_User::getUser(),'Default calendar');
 	$calendars = OC_Calendar_Calendar::allCalendars(OC_User::getUser(), 1);
 }
+
 $eventSources = array();
 foreach($calendars as $calendar){
 	$eventSources[] = OC_Calendar_Calendar::getEventSourceInfo($calendar);
 }
+OC_Hook::emit('OC_Calendar', 'getSources', array('sources' => &$eventSources));
+
 //Fix currentview for fullcalendar
 if(OC_Preferences::getValue(OC_USER::getUser(), 'calendar', 'currentview', 'month') == "oneweekview"){
 	OC_Preferences::setValue(OC_USER::getUser(), "calendar", "currentview", "agendaWeek");
diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php
index 9e424aa89f8c4fc9edd352f9b55b72b7f201adf2..c3b4916a3fded9b3d3ee029387a23ccc3c05be7f 100644
--- a/apps/contacts/appinfo/app.php
+++ b/apps/contacts/appinfo/app.php
@@ -5,6 +5,8 @@ OC::$CLASSPATH['OC_Contacts_VCard'] = 'apps/contacts/lib/vcard.php';
 OC::$CLASSPATH['OC_Contacts_Hooks'] = 'apps/contacts/lib/hooks.php';
 OC::$CLASSPATH['OC_Connector_Sabre_CardDAV'] = 'apps/contacts/lib/connector_sabre.php';
 OC_HOOK::connect('OC_User', 'post_deleteUser', 'OC_Contacts_Hooks', 'deleteUser');
+OC_HOOK::connect('OC_Calendar', 'getEvents', 'OC_Contacts_Hooks', 'getBirthdayEvents');
+OC_HOOK::connect('OC_Calendar', 'getSources', 'OC_Contacts_Hooks', 'getCalenderSources');
 
 OC_App::register( array(
   'order' => 10,
diff --git a/apps/contacts/lib/hooks.php b/apps/contacts/lib/hooks.php
index 155cf40f914c5b65796c363ede154010dc6d985b..10ae82bbf4f0bf3df2d8d713c399bdf808975dae 100644
--- a/apps/contacts/lib/hooks.php
+++ b/apps/contacts/lib/hooks.php
@@ -38,4 +38,49 @@ class OC_Contacts_Hooks{
 
 		return true;
 	}
+
+	static public function getCalenderSources($parameters) {
+		$base_url = OC_Helper::linkTo('calendar', 'ajax/events.php').'?calendar_id=';
+		foreach(OC_Contacts_Addressbook::all(OC_User::getUser()) as $addressbook) {
+			$parameters['sources'][] =
+				array(
+					'url' => $base_url.'birthday_'. $addressbook['id'],
+					'backgroundColor' => '#cccccc',
+					'borderColor' => '#888',
+					'textColor' => 'black',
+					'cache' => true,
+					'editable' => false,
+				);
+		}
+	}
+
+	static public function getBirthdayEvents($parameters) {
+		$name = $parameters['calendar_id'];
+		if (strpos('birthday_', $name) != 0) {
+			return;
+		}
+		$info = explode('_', $name);
+		$aid = $info[1];
+		OC_Contacts_App::getAddressbook($aid);
+		foreach(OC_Contacts_VCard::all($aid) as $card){
+			$vcard = OC_VObject::parse($card['carddata']);
+			$birthday = $vcard->BDAY;
+			if ($birthday) {
+				$date = new DateTime($birthday);
+				$vevent = new OC_VObject('VEVENT');
+				$vevent->setDateTime('LAST-MODIFIED', new DateTime($vcard->REV));
+				$vevent->setDateTime('DTSTART', $date, Sabre_VObject_Element_DateTime::DATE);
+				$vevent->setString('DURATION', 'P1D');
+				// DESCRIPTION?
+				$vevent->setString('RRULE', 'FREQ=YEARLY');
+				$title = str_replace('{name}', $vcard->getAsString('FN'), OC_Contacts_App::$l10n->t('{name}\'s Birthday'));
+				$parameters['events'][] = array(
+					'id' => 0,//$card['id'],
+					'vevent' => $vevent,
+					'repeating' => true,
+					'summary' => $title,
+					);
+			}
+		}
+	}
 }