diff --git a/_config.php b/_config.php new file mode 100755 index 0000000..352775c --- /dev/null +++ b/_config.php @@ -0,0 +1,19 @@ + '$StartMonth%{sMonShort}. %{sDayNumShort}, %{sYearFull}' +'SameMonthSameYear' => '%{sMonShort}. %{sDayNumShort} - %{eDayNumShort}, %{eYearFull}' +'DiffMonthSameYear' => '%{sMonShort}. %{sDayNumShort} - %{eMonShort}. %{eDayNumShort}, %{eYearFull}' +'DiffMonthDiffYear' => '%{sMonShort}. %{sDayNumShort}, %{sYearFull} - %{eMonShort} %{eDayNumShort}, %{eYearFull}' + +'OneDayHeader' => '%{sMonFull} %{sDayNumShort}%{sDaySuffix}, %{sYearFull}' +'MonthHeader' => '%{sMonFull}, %{sYearFull}' +'YearHeader' => '%{sYearFull}' + +*/ +); diff --git a/code/Calendar.php b/code/Calendar.php new file mode 100755 index 0000000..6f36c6d --- /dev/null +++ b/code/Calendar.php @@ -0,0 +1,934 @@ + 'Varchar(50)', + 'OtherDatesCount' => 'Int', + 'RSSTitle' => 'Varchar(255)', + 'DefaultFutureMonths' => 'Int', + 'EventsPerPage' => 'Int', + 'DefaultView' => "Enum('today,week,,month,weekend,upcoming','upcoming')" + ); + + + + static $has_many = array ( + 'Announcements' => 'CalendarAnnouncement', + 'CalendarEvents' => 'CalendarEvent', + 'Feeds' => 'ICSFeed' + ); + + + + static $many_many = array ( + 'NestedCalendars' => 'Calendar' + ); + + + + static $belongs_many_many = array ( + 'ParentCalendars' => 'Calendar' + ); + + + + static $allowed_children = array ( + 'CalendarEvent' + ); + + + + static $defaults = array ( + 'DefaultEventDisplay' => '10', + 'DefaultDateHeader' => 'Upcoming Events', + 'OtherDatesCount' => '3', + 'DefaultFutureMonths' => '6', + 'EventsPerPage' => '10', + 'DefaultView' => 'upcoming' + ); + + + + static $reccurring_event_index = 0; + + + static $icon = "event_calendar/images/calendar"; + + + static $event_class = "CalendarEvent"; + + + static $announcement_class = "CalendarAnnouncement"; + + + static $timezone = "0:00 GMT"; + + + static $language = "EN"; + + + protected $eventClass_cache, + $announcementClass_cache, + $datetimeClass_cache, + $dateToEventRelation_cache, + $announcementToCalendarRelation_cache; + + + + public function getCMSFields() + { + + Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); + Requirements::javascript('event_calendar/javascript/calendar_cms.js'); + + $f = parent::getCMSFields(); + $configuration = _t('Calendar.CONFIGURATION','Configuration'); + $f->addFieldsToTab("Root.$configuration", array( + new DropdownField('DefaultView',_t('Calendar.DEFAULTVIEW','Default view'), array ( + 'upcoming' => _t('Calendar.UPCOMINGVIEW',"Show a list of upcoming events."), + 'month' => _t('Calendar.MONTHVIEW',"Show this month's events."), + 'week' => _t('Calendar.WEEKVIEW',"Show this week's events. If none, fall back on this month's"), + 'today' => _t('Calendar.TODAYVIEW',"Show today's events. If none, fall back on this week's events"), + 'weekend' => _t('Calendar.WEEKENDVIEW',"Show this weekend's events.") + + )), + new NumericField('DefaultFutureMonths', _t('Calendar.DEFAULTFUTUREMONTHS','Number maximum number of future months to show in default view')), + new NumericField('EventsPerPage', _t('Calendar.EVENTSPERPAGE','Events per page')), + new TextField('DefaultDateHeader', _t('Calendar.DEFAULTDATEHEADER','Default date header (displays when no date range has been selected)')), + new NumericField('OtherDatesCount', _t('Calendar.NUMBERFUTUREDATES','Number of future dates to show for repeating events')) + )); + + $announcements = _t('Calendar.Announcements','Announcements'); + $f->addFieldToTab("Root.$announcements", GridField::create( + "Announcements", + _t('Calendar.ANNOUNCEMENTDESCRIPTION','Announcements are simple entries you can add to your calendar that do not have detail pages, e.g. "Office closed"'), + $this->Announcements(), + GridFieldConfig_RecordEditor::create() + )); + + $feeds = _t('Calendar.FEEDS','Feeds'); + $f->addFieldToTab("Root.$feeds", GridField::create( + "Feeds", + _t('Calendar.ICSFEEDDESCRIPTION','Add ICS feeds to your calendar to include events from external sources, e.g. a Google Calendar'), + $this->Feeds(), + GridFieldConfig_RecordEditor::create() + )); + + $otherCals = DataList::create("Calendar")->exclude("Calendar\".\"ID",$this->ID); + if($otherCals->exists()) { + $f->addFieldToTab("Root.$feeds", new CheckboxSetField( + 'NestedCalendars', + _t('Calendar.NESTEDCALENDARS','Include events from these calendars'), + $otherCals->map() + )); + } + + $f->addFieldToTab("Root.Main", new TextField('RSSTitle', _t('Calendar.RSSTITLE','Title of RSS Feed')),'Content'); + $this->extend('updateCMSFields',$f); + return $f; + } + + + public function getEventClass() { + if($this->eventClass_cache) return $this->eventClass_cache; + $this->eventClass_cache = $this->stat('event_class'); + return $this->eventClass_cache; + } + + + + public function getAnnouncementClass() { + if($this->announcementClass_cache) return $this->announcementClass_cache; + $this->announcementClass_cache = $this->stat('announcement_class'); + return $this->announcementClass_cache; + } + + + + public function getDateTimeClass() { + if($this->datetimeClass_cache) return $this->datetimeClass_cache; + $this->datetimeClass_cache = singleton($this->getEventClass())->stat('datetime_class'); + return $this->datetimeClass_cache; + } + + + + public function getDateToEventRelation() { + if($this->dateToEventRelation_cache) return $this->dateToEventRelation_cache; + $this->dateToEventRelation_cache = singleton($this->getDateTimeClass())->getReverseAssociation($this->getEventClass())."ID"; + return $this->dateToEventRelation_cache; + } + + + + public function getEventList($start, $end, $filter = null, $limit = null) { + $eventList = new ArrayList(); + if($events = $this->getStandardEvents($start, $end, $filter)) { + $eventList->merge($events); + } + + + foreach($this->getAllCalendars() as $calendar) { + $announcements = DataList::create($this->getAnnouncementClass()) + ->filter('CalendarID', $calendar->ID) + ->where(" + (StartDate <= '$start' AND EndDate >= '$end') OR + (StartDate BETWEEN '$start' AND '$end') OR + (EndDate BETWEEN '$start' AND '$end') + "); + if($filter) { + var_dump($filter); + $announcements->filter($filter); + } + + if($announcements) { + foreach($announcements as $announcement) { + $eventList->push($announcement); + } + + } + } + + + if($recurring = $this->getRecurringEvents($filter)) { + $eventList = $this->addRecurringEvents($start, $end, $recurring, $eventList); + } + + // if($this->Feeds()) { + // $event_list = $this->importFromFeeds($event_list); + // } + $eventList->sort("StartDate","ASC"); + return $eventList; + } + + + + protected function getEventIds() { + $ids = array(); + foreach($this->getAllCalendars() as $calendar) { + if($children = $calendar->AllChildren()) { + $ids = array_merge($ids, $children->column('ID')); + } + } + if(!empty($ids)) return $ids; + return false; + } + + + + protected function getStandardEvents($start, $end, $filter = null) { + if(!$ids = $this->getEventIds()) return false; + $datetime_class = $this->getDateTimeClass(); + $relation = $this->getDateToEventRelation(); + $event_class = $this->getEventClass(); + $list = DataList::create($datetime_class); + $list->where("Recursion != 1"); + $list->where(" + (StartDate <= '$start' AND EndDate >= '$end') OR + (StartDate BETWEEN '$start' AND '$end') OR + (EndDate BETWEEN '$start' AND '$end') + "); + $list->filter(array($relation => $ids)); + $list->innerJoin($event_class, $relation = "\"{$event_class}\".ID"); + return $list; + } + + + + + protected function getRecurringEvents($filter = null) { + $event_class = $this->getEventClass(); + $datetime_class = $this->getDateTimeClass(); + $SNG = singleton($datetime_class); + if($relation = $this->getDateToEventRelation()) { + $events = DataList::create($event_class) + ->filter("Recursion", "1") + ->filter("ParentID", $this->ID) + ->innerJoin($datetime_class, "\"{$datetime_class}\".{$relation} = \"SiteTree\".ID"); + if($filter) { + $events->filter($filter); + } + return $events; + } + return false; + } + + + + + public function getNextRecurringEvents($event_obj, $datetime_obj, $limit = null) { + $counter = sfDate::getInstance($datetime_obj->StartDate); + if($event = $datetime_obj->Event()->DateTimes()->First()) { + $end_date = strtotime($event->EndDate); + } + else { + $end_date = false; + } + $counter->tomorrow(); + $dates = new ArrayList(); + while($dates->Count() != $this->OtherDatesCount) { + // check the end date + if($end_date) { + if($end_date > 0 && $end_date <= $counter->get()) + break; + } + if($event_obj->getRecursionReader()->recursionHappensOn($counter->get())) { + $dates->push($this->newRecursionDateTime($datetime_obj,$counter->date())); + } + $counter->tomorrow(); + } + return $dates; + } + + + + + protected function addRecurringEvents($start_date, $end_date, $recurring_events,$all_events) { + $date_counter = sfDate::getInstance($start_date); + $end = sfDate::getInstance($end_date); + foreach($recurring_events as $recurring_event) { + $reader = $recurring_event->getRecursionReader(); + $relation = $recurring_event->getReverseAssociation($this->getDateTimeClass()); + if(!$relation) continue; + + if($recurring_event_datetime = $recurring_event->$relation()->first()) { + while($date_counter->get() <= $end->get()){ + + // check the end date + if($recurring_event_datetime->EndDate) { + $end_stamp = strtotime($recurring_event_datetime->EndDate); + if($end_stamp > 0 && $end_stamp <= $date_counter->get()) { + break; + } + } + if($reader->recursionHappensOn($date_counter->get())) { + $e = $this->newRecursionDateTime($recurring_event_datetime, $date_counter->date()); + $all_events->push($e); + } + $date_counter->tomorrow(); + } + $date_counter->reset(); + } + } + return $all_events; + } + + + public function newRecursionDateTime($recurring_event_datetime, $start_date) { + $c = $this->getDateTimeClass(); + $relation = $this->getDateToEventRelation(); + $e = new $c(); + foreach($recurring_event_datetime->db() as $field => $type) { + $e->$field = $recurring_event_datetime->$field; + } + $e->StartDate = $start_date; + $e->EndDate = $start_date; + $e->$relation = $recurring_event_datetime->$relation; + $e->ID = "recurring" . self::$reccurring_event_index; + self::$reccurring_event_index++; + return $e; + } + + + public function getAllCalendars() { + $calendars = new ArrayList(); + $calendars->push($this); + $calendars->merge($this->NestedCalendars()); + return $calendars; + } + + + + + public function UpcomingEvents($limit = 5, $filter = null) { + return $this->getEventList( + sfDate::getInstance()->dump(), + sfDate::getInstance()->addMonth($this->DefaultFutureMonths)->dump(), + $filter, + $limit + ); + } + + + + public function RecentEvents($limit = null, $filter = null) { + $start_date = sfDate::getInstance(); + $end_date = sfDate::getInstance(); + $l = ($limit === null) ? "9999" : $limit; + $events = $this->getEventList( + $start_date->subtractMonth($this->DefaultFutureMonths)->dump(), + $end_date->yesterday()->dump(), + $filter, + $l + ); + $events->sort('StartDate','DESC'); + return $events->getRange(0,$limit); + } + + + + public function CalendarWidget() { + $calendar = new CalendarWidget($this); + $controller = Controller::curr(); + if($controller->class == "Calendar_Controller" || is_subclass_of($controller, "Calendar_Controller")) { + if($controller->getView() != "default") { + $calendar->setOption('start', $controller->getStartDate()->format('Y-m-d')); + $calendar->setOption('end', $controller->getEndDate()->format('Y-m-d')); + } + } + return $calendar; + } + + + +} + + + +class Calendar_Controller extends Page_Controller { + + + static $allowed_actions = array ( + 'show', + 'month', + 'year', + 'rss', + 'today', + 'week', + 'weekend', + 'ical', + 'ics', + 'monthjson', + 'MonthJumpForm' + ); + + + + protected $view; + + + protected $startDate; + + + protected $endDate; + + + + public function init() { + parent::init(); + RSSFeed::linkToFeed($this->Link() . "rss", $this->RSSTitle ? $this->RSSTitle : $this->Title); + Requirements::css('event_calendar/css/calendar.css'); + Requirements::javascript(THIRDPARTY_DIR.'/jquery/jquery.js'); + Requirements::javascript('event_calendar/javascript/calendar.js'); + } + + + + public function getStartDate() { + return $this->startDate; + } + + + + public function getEndDate() { + return $this->endDate; + } + + + + public function getView() { + return $this->view; + } + + + + public function setDefaultView() { + $this->view = "default"; + $this->startDate = sfDate::getInstance(); + $this->endDate = sfDate::getInstance()->addMonth(6); + } + + + + public function setTodayView() { + $this->view = "day"; + $this->startDate = sfDate::getInstance(); + $this->endDate = sfDate::getInstance(); + } + + + + public function setWeekView() { + $this->view = "week"; + $this->startDate = sfDate::getInstance()->firstDayOfWeek(); + $this->endDate = sfDate::getInstance()->finalDayOfWeek(); + if(CalendarUtil::get_first_day_of_week() == sfTime::MONDAY) { + $this->startDate->tomorrow(); + $this->endDate->tomorrow(); + } + } + + + + public function setWeekendView() { + $this->view = "weekend"; + $start = sfDate::getInstance(); + if($start->format('w') == sfTime::SATURDAY) { + $start->yesterday(); + } + elseif($start->format('w') != sfTime::FRIDAY) { + $start->nextDay(sfTime::FRIDAY); + } + $this->startDate = $start; + $this->endDate = sfDate::getInstance($start)->nextDay(sfTime::SUNDAY); + } + + + + public function setMonthView() { + $this->view = "month"; + $this->startDate = sfDate::getInstance()->firstDayOfMonth(); + $this->endDate = sfDate::getInstance()->finalDayOfMonth(); + } + + + + public function getOffset() { + if(!isset($_REQUEST['start'])) { + $_REQUEST['start'] = 0; + } + return $_REQUEST['start']; + } + + + + protected function getRangeLink(sfDate $start, sfDate $end) { + return Controller::join_links($this->Link(), "show", $start->format('Ymd'), $end->format('Ymd')); + } + + + + public function respond() { + if(Director::is_ajax()) { + return $this->renderWith('EventList'); + } + return array(); + } + + + public function index(SS_HTTPRequest $r) { + switch($this->DefaultView) { + case "month": + return Director::redirect($this->Link('show/month')); + break; + + case "week": + $this->setWeekView(); + // prevent pagination on these default views + $this->EventsPerPage = 999; + $e = $this->Events(); + if($e->count() > 0) { + return array('Events' => $e); + } + else { + $this->setMonthView(); + return array(); + } + break; + + case "today": + // prevent pagination on these default views + $this->EventsPerPage = 999; + $this->setTodayView(); + $e = $this->Events(); + if($e->count() > 0) { + return array('Events' => $e); + } + else { + $this->setWeekView(); + return array(); + } + break; + + default: + $this->setDefaultView(); + $list = $this->Events(); + return $this->respond(); + break; + + + } + } + + + + public function today(SS_HTTPRequest $r) { + $this->setTodayView(); + return $this->respond(); + } + + + + public function week(SS_HTTPRequest $r) { + $this->setWeekView(); + return $this->respond(); + } + + + + public function weekend(SS_HTTPRequest $r) { + $this->setWeekendView(); + return $this->respond(); + } + + + + + public function month(SS_HTTPReqest $r) { + $this->setMonthView(); + return $this->respond(); + } + + + + public function show(SS_HTTPRequest $r) { + $this->parseURL($r); + return $this->respond(); + } + + + + public function rss() { + $this->setDefaultView(); + $events = $this->Events(); + foreach($events as $event) { + $event->Title = strip_tags($event->DateRange()) . " : " . $event->getTitle(); + $event->Description = $event->getContent(); + } + $rss_title = $this->RSSTitle ? $this->RSSTitle : sprintf(_t("Calendar.UPCOMINGEVENTSFOR","Upcoming Events for %s"),$this->Title); + $rss = new RSSFeed($events, $this->Link(), $rss_title, "", "Title", "Description"); + + if(is_int($rss->lastModified)) { + HTTP::register_modification_timestamp($rss->lastModified); + header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $rss->lastModified) . ' GMT'); + } + if(!empty($rss->etag)) { + HTTP::register_etag($rss->etag); + } + $xml = str_replace(' ', ' ', $rss->renderWith('RSSFeed')); + $xml = preg_replace('//', '', $xml); + $xml = trim($xml); + HTTP::add_cache_headers(); + header("Content-type: text/xml"); + echo $xml; + } + + + + public function monthjson(SS_HTTPRequest $r) { + if(!$r->param('ID')) return false; + $this->startDate = sfDate::getInstance(CalendarUtil::get_date_from_string($r->param('ID'))); + $this->endDate = sfDate::getInstance($this->startDate)->finalDayOfMonth(); + + $json = array (); + $counter = clone $this->startDate; + while($counter->get() <= $this->endDate->get()) { + $d = $counter->format('Y-m-d'); + $json[$d] = array ( + 'events' => array () + ); + $counter->tomorrow(); + } + $list = $this->Events(); + foreach($list as $e) { + foreach($e->getAllDatesInRange() as $date) { + if(isset($json[$date])) { + $json[$date]['events'][] = $e->getTitle(); + } + } + } + return Convert::array2json($json); + } + + + + /** + * Send ical file of multiple upcoming events using ICSWriter + * + * @todo Support recurring events. + * @see ICSWriter + * @author Alex Hayes + */ + public function ical() { + $writer = new ICSWriter($this->data(), Director::absoluteURL('/')); + $writer->sendDownload(); + } + + + + public function ics(SS_HTTPRequest $r) { + $feed = false; + $announcement = false; + $id = $r->param('ID'); + $oid = $r->param('OtherID'); + + if(stristr($id, "feed") !== false) { + $id = str_replace("feed","",$id); + $feed = true; + } + else if(stristr($id, "announcement-") !== false) { + $id = str_replace("announcement-","",$id); + $announcement = true; + } + else { + $announcement = false; + } + if(is_numeric($id) && $oid) { + if(!$feed) { + $event = DataObject::get_by_id($announcement ? $this->data()->getDateTimeClass() : $this->data()->getEventClass(), $id); + $FILENAME = $announcement ? preg_replace("/[^a-zA-Z0-9s]/", "", $event->Title) : $event->URLSegment; + } + else { + $FILENAME = preg_replace("/[^a-zA-Z0-9s]/", "", urldecode($_REQUEST['title'])); + } + + $FILENAME .= ".ics"; + $HOST = $_SERVER['HTTP_HOST']; + $TIMEZONE = Calendar::$timezone; + $LANGUAGE = Calendar::$language; + $CALSCALE = "GREGORIAN"; + $parts = explode('-',$oid); + $START_TIMESTAMP = $parts[0]; + $END_TIMESTAMP = $parts[1]; + if(!$feed) { + $URL = $announcement ? $event->Calendar()->AbsoluteLink() : $event->AbsoluteLink(); + } + else { + $URL = ""; + } + $TITLE = $feed ? $_REQUEST['title'] : $event->Title; + header("Cache-Control: private"); + header("Content-Description: File Transfer"); + header("Content-Type: text/calendar"); + header("Content-Transfer-Encoding: binary"); + if(stristr($_SERVER['HTTP_USER_AGENT'], "MSIE")) { + header("Content-disposition: filename=".$FILENAME."; attachment;"); + } + else { + header("Content-disposition: attachment; filename=".$FILENAME); + } + $result = trim(strip_tags($this->customise(array( + 'HOST' => $HOST, + 'LANGUAGE' => $LANGUAGE, + 'TIMEZONE' => $TIMEZONE, + 'CALSCALE' => $CALSCALE, + 'START_TIMESTAMP' => $START_TIMESTAMP, + 'END_TIMESTAMP' => $END_TIMESTAMP, + 'URL' => $URL, + 'TITLE' => $TITLE + ))->renderWith(array('ics')))); + return $result; + } + else { + Director::redirectBack(); + } + } + + + + + + public function parseURL(SS_HTTPRequest $r) { + if(!$r->param('ID')) return; + $this->startDate = sfDate::getInstance(CalendarUtil::get_date_from_string($r->param('ID'))); + if($r->param('OtherID')) { + $this->view = "range"; + $this->endDate = sfDate::getInstance(CalendarUtil::get_date_from_string($r->param('OtherID'))); + } + else { + $d = clone $this->startDate; + switch(strlen(str_replace("-","",$r->param('ID')))) { + case 8: + $this->view = "day"; + $this->endDate = sfDate::getInstance($d->get()+1); + break; + + case 6: + $this->view = "month"; + $this->endDate = sfDate::getInstance($d->finalDayOfMonth()->date()); + break; + + case 4: + $this->view = "year"; + $this->endDate = sfDate::getInstance($d->finalDayOfYear()->date()); + break; + + default: + $this->view = "default"; + $this->endDate = sfDate::getInstance($d->addMonth($this->DefaultFutureMonths)->date()); + break; + } + } + } + + + + public function Events() { + $all = $this->data()->getEventList( + $this->startDate->dump(), + $this->endDate->dump() + ); + $list = $all->limit($this->EventsPerPage, $this->getOffset()); + $next = $this->getOffset()+$this->EventsPerPage; + $this->MoreEvents = ($next < $all->count()); + $this->MoreLink = HTTP::setGetVar("start", $next); + return $list; + } + + + + public function DateHeader() { + switch($this->view) { + case "day": + return CalendarUtil::localize($this->startDate->get(), null, CalendarUtil::ONE_DAY_HEADER); + break; + + case "month": + return CalendarUtil::localize($this->startDate->get(), null, CalendarUtil::MONTH_HEADER); + break; + + case "year": + return CalendarUtil::localize($this->startDate->get(), null, CalendarUtil::YEAR_HEADER); + break; + + + case "range": + case "week": + case "weekend": + list($strStartDate,$strEndDate) = CalendarUtil::get_date_string($this->startDate->date(),$this->endDate->date()); + return $strStartDate.$strEndDate; + break; + + default: + return $this->DefaultDateHeader; + break; + } + } + + + public function CurrentAction($a) { + return $this->getAction() == $a; + } + + + public function PreviousDayLink() { + $s = sfDate::getInstance($this->startDate)->yesterday(); + return $this->getRangeLink($s, $s); + } + + + + public function NextDayLink() { + $s = sfDate::getInstance($this->startDate)->tomorrow(); + return $this->getRangeLink($s, $s); + } + + + + public function PreviousWeekLink() { + $s = sfDate::getInstance($this->startDate)->subtractWeek(); + $e = sfDate::getInstance($this->endDate)->subtractWeek(); + return $this->getRangeLink($s, $e); + } + + + + public function NextWeekLink() { + $s = sfDate::getInstance($this->startDate)->addWeek(); + $e = sfDate::getInstance($this->endDate)->addWeek(); + return $this->getRangeLink($s, $e); + } + + + + public function NextMonthLink() { + $s = sfDate::getInstance($this->startDate)->addMonth(); + $e = sfDate::getInstance($s)->finalDayOfMonth(); + return $this->getRangeLink($s, $e); + } + + + + public function PreviousMonthLink() { + $s = sfDate::getInstance($this->startDate)->subtractMonth(); + $e = sfDate::getInstance($s)->finalDayOfMonth(); + return $this->getRangeLink($s, $e); + } + + + public function NextWeekendLink() { + return $this->NextWeekLink(); + } + + + public function PreviousWeekendLink() { + return $this->PreviousWeekLink(); + } + + + public function IsSegment($segment) { + switch($segment) { + case "today": + return $this->startDate->dump() == $this->endDate->dump(); + case "week": + if(CalendarUtil::get_first_day_of_week() == sfTime::MONDAY) { + return ($this->startDate->format('w') == sfTime::MONDAY) && ($this->startDate->format('w') == sfTime::SUNDAY); + } + return ($this->startDate->format('w') == sfTime::SUNDAY) && ($this->endDate->format('w') == sfTime::SATURDAY); + + case "month": + return ($this->startDate->format('j') == 1) && (sfDate::getInstance($this->startDate)->finalDayOfMonth()->format('j') == $this->endDate->format('j')); + case "weekend": + return ($this->startDate->format('w') == sfTime::FRIDAY) && ($this->endDate->format('w') == sfTime::SUNDAY); + } + } + + + public function MonthJumper() { + return $this->renderWith('MonthJumper'); + } + + + + public function MonthJumpForm() { + $this->parseURL($this->getRequest()); + $dummy = sfDate::getInstance($this->startDate); + $range = range(($dummy->subtractYear(3)->format('Y')), ($dummy->addYear(6)->format('Y'))); + $year_map = array_combine($range, $range); + $f = new Form( + Controller::curr(), + "MonthJumpForm", + new FieldList ( + $m = new DropdownField('Month','', CalendarUtil::get_months_map('%B')), + $y = new DropdownField('Year','', $year_map) + ), + new FieldList ( + new FormAction('doMonthJump', _t('Calendar.JUMP','Go')) + ) + ); + + if($this->startDate) { + $m->setValue($this->startDate->format('m')); + $y->setValue($this->startDate->format('Y')); + } + return $f; + } + + public function doMonthJump($data, $form) { + return Director::redirect($this->Link('show').'/'.$data['Year'].$data['Month']); + } + + +} \ No newline at end of file diff --git a/code/CalendarAnnouncement.php b/code/CalendarAnnouncement.php new file mode 100755 index 0000000..db11bf1 --- /dev/null +++ b/code/CalendarAnnouncement.php @@ -0,0 +1,38 @@ + 'Varchar(255)', + 'Content' => 'Text' + ); + + + static $has_one = array ( + 'Calendar' => 'Calendar' + ); + + + public function getCMSFields() { + $f = parent::getCMSFields(); + $f->insertBefore(new TextField('Title', _t('CalendarAnnouncement.TITLE','Title of announcement')), "StartDate"); + $f->insertBefore(new TextareaField('Content', _t('CalendarAnnouncement.CONTENT','Announcement content')), "StartDate"); + $this->extend('updateCMSFields', $f); + + return $f; + } + + + public function getTitle() { + return $this->getField('Title'); + } + + + public function getContent() { + return $this->getField('Content'); + } + + + +} \ No newline at end of file diff --git a/code/CalendarDateTime.php b/code/CalendarDateTime.php new file mode 100755 index 0000000..33d6c5c --- /dev/null +++ b/code/CalendarDateTime.php @@ -0,0 +1,230 @@ + 'Date', + 'StartTime' => 'Time', + 'EndDate' => 'Date', + 'EndTime' => 'Time', + 'AllDay' => 'Boolean' + ); + + + static $has_one = array ( + 'Event' => 'CalendarEvent' + ); + + + static $date_format_override; + + + static $time_format_override; + + + static $default_sort = "StartDate ASC, StartTime ASC"; + + + static $offset = "0:00"; + + + public function getCMSFields() { + DateField::set_default_config('showcalendar', true); + $f = new FieldList( + new DateField('StartDate',_t('CalendarDateTime.STARTDATE','Start date')), + new DateField('EndDate',_t('CalendarDateTime.ENDDATE','End date')), + new TimeField('StartTime', _t('CalendarDateTime.STARTTIME','Start time')), + new TimeField('EndTime', _t('CalendarDateTime.ENDTIME','End time')), + new CheckboxField('AllDay', _t('CalendarDateTime.ALLDAY','This event lasts all day')) + ); + + $this->extend('updateCMSFields', $f); + + return $f; + } + + + + public function summaryFields() { + return array ( + 'FormattedStartDate' => _t('Calendar.STARTDATE','Start date'), + 'FormattedEndDate' => _t('Calendar.ENDDATE','End date'), + 'FormattedStartTime' => _t('Calendar.STARTTIME','Start time'), + 'FormattedEndTime' => _t('Calendar.ENDTIME','End time'), + 'FormattedAllDay' => _t('Calendar.ALLDAY','All day'), + ); + } + + + public function Link() { + return Controller::join_links($this->Event()->Link(),"?date=".$this->StartDate); + } + + + + public function DateRange() { + list($strStartDate,$strEndDate) = CalendarUtil::get_date_string($this->StartDate,$this->EndDate); + $html = "" . $strStartDate . ""; + $html .= ($strEndDate != "") ? "-" : ""; + $html .= ""; + $html .= ($strEndDate != "") ? $strEndDate : ""; + $html .= ""; + + return $html; + } + + + + public function TimeRange() { + $func = CalendarUtil::get_time_format() == "24" ? "Nice24" : "Nice"; + $ret = $this->obj('StartTime')->$func(); + $ret .= $this->EndTime ? " — " . $this->obj('EndTime')->$func() : ""; + return $ret; + } + + + public function Announcement() { + return $this->ClassName == "CalendarAnnouncement"; + } + + + + public function OtherDates() { + if($this->Announcement()) { + return false; + } + + if($this->Event()->Recursion) { + return $this->Event()->Parent()->getNextRecurringEvents($this->Event(), $this); + } + + return DataList::create($this->class) + ->where("EventID = {$this->EventID}") + ->where("StartDate != '{$this->StartDate}'") + ->limit($this->Event()->Parent()->DefaultEventDisplay); + } + + + + + public function MicroformatStart($offset = true) { + if(!$this->StartDate) + return ""; + + $date = $this->StartDate; + + if($this->AllDay) + $time = "00:00:00"; + else + $time = $this->StartTime ? $this->StartTime : "00:00:00"; + + return CalendarUtil::microformat($date, $time, self::$offset); + } + + + + public function MicroformatEnd($offset = true) { + if($this->AllDay && $this->StartDate) { + $time = "00:00:00"; + $end = sfDate::getInstance($this->StartDate); + $date = $end->tomorrow()->date(); + unset($end); + } + else { + $date = $this->EndDate ? $this->EndDate : $this->StartDate; + $time = $this->EndTime && $this->StartTime ? $this->EndTime : (!$this->EndTime && $this->StartTime ? $this->StartTime : "00:00:00"); + } + + return CalendarUtil::microformat($date, $time, self::$offset); + } + + + + public function ICSLink() { + if($this->Feed) { + return Controller::join_links( + $this->Calendar()->Link(), + "ics", + $this->ID, + $this->MicroformatStart(false) . "-" . $this->MicroformatEnd(false), + "?title=".urlencode($this->Title) + ); + } + else if($this->Announcement()) { + return Controller::join_links( + $this->Calendar()->Link(), + "ics","announcement-".$this->ID, + $this->MicroformatStart(false) . "-" . $this->MicroformatEnd(false) + ); + } + return Controller::join_links( + $this->Event()->Parent()->Link(), + "ics", + $this->Event()->ID, + $this->MicroformatStart(false) . "-" . $this->MicroformatEnd(false) + ); + } + + + + public function getFormattedStartDate() { + if(!$this->StartDate) return "--"; + return CalendarUtil::get_date_format() == "mdy" ? $this->obj('StartDate')->Format('m-d-Y') : $this->obj('StartDate')->Format('d-m-Y'); + } + + + + public function getFormattedEndDate() { + if(!$this->EndDate) return "--"; + return CalendarUtil::get_date_format() == "mdy" ? $this->obj('EndDate')->Format('m-d-Y') : $this->obj('EndDate')->Format('d-m-Y'); + } + + + + public function getFormattedStartTime() { + if(!$this->StartTime) return "--"; + return CalendarUtil::get_time_format() == "12" ? $this->obj('StartTime')->Nice() : $this->obj('StartTime')->Nice24(); + } + + + + public function getFormattedEndTime() { + if(!$this->EndTime) return "--"; + return CalendarUtil::get_time_format() == "12" ? $this->obj('EndTime')->Nice() : $this->obj('EndTime')->Nice24(); + } + + + + public function getFormattedAllDay() { + return $this->AllDay == 1 ? _t('YES','Yes') : _t('NO','No'); + } + + + + public function getTitle() { + return $this->Event()->Title; + } + + + public function getContent() { + return $this->Event()->Content; + } + + + + public function getAllDatesInRange() { + $start = sfDate::getInstance($this->StartDate); + $end = sfDate::getInstance($this->EndDate); + $dates = array (); + while($start->get() <= $end->get()) { + $dates[] = $start->format('Y-m-d'); + $start->tomorrow(); + } + return $dates; + } + + + +} \ No newline at end of file diff --git a/code/CalendarEvent.php b/code/CalendarEvent.php new file mode 100755 index 0000000..e11d6e3 --- /dev/null +++ b/code/CalendarEvent.php @@ -0,0 +1,242 @@ + 'Boolean', + 'CustomRecursionType' => 'Int', + 'DailyInterval' => 'Int', + 'WeeklyInterval' => 'Int', + 'MonthlyInterval' => 'Int', + 'MonthlyRecursionType1' => 'Int', + 'MonthlyRecursionType2' => 'Int', + 'MonthlyIndex' => 'Int', + 'MonthlyDayOfWeek' => 'Int' + ); + + static $has_many = array ( + 'DateTimes' => 'CalendarDateTime', + 'Exceptions' => 'RecurringException' + ); + + + static $many_many = array ( + 'RecurringDaysOfWeek' => 'RecurringDayOfWeek', + 'RecurringDaysOfMonth' => 'RecurringDayOfMonth' + ); + + static $icon = "event_calendar/images/event"; + + + static $datetime_class = "CalendarDateTime"; + + + public function getCMSFields() + { + Requirements::javascript(THIRDPARTY_DIR.'/jquery-livequery/jquery.livequery.js'); + Requirements::javascript('event_calendar/javascript/calendar_cms.js'); + $f = parent::getCMSFields(); + $dt = _t('CalendarEvent.DATESANDTIMES','Dates and Times'); + $recursion = _t('CalendarEvent.RECURSION','Recursion'); + $f->addFieldToTab("Root.$dt", + GridField::create( + "DateTimes", + _t('Calendar.DATETIMEDESCRIPTION','Add dates for this event'), + $this->DateTimes(), + GridFieldConfig_RecordEditor::create() + ) + ); + + $f->addFieldsToTab("Root.$recursion", array( + new CheckboxField('Recursion',_t('CalendarEvent.REPEATEVENT','Repeat this event')), + new OptionsetField( + 'CustomRecursionType', + _t('CalendarEvent.DESCRIBEINTERVAL','Describe the interval at which this event recurs.'), + array ( + '1' => _t('CalendarEvent.DAILY','Daily'), + '2' => _t('CalendarEvent.WEEKLY','Weekly'), + '3' => _t('CalendarEvent.MONTHLY','Monthly') + ) + ) + )); + + $f->addFieldToTab("Root.$recursion", $dailyInterval = new FieldGroup( + new LabelField($name = "every1", $title = _t("CalendarEvent.EVERY","Every ")), + new DropdownField('DailyInterval', '', array_combine(range(1,10), range(1,10))), + new LabelField($name = "days",$title = _t("CalendarEvent.DAYS"," day(s)")) + )); + + $f->addFieldToTab("Root.$recursion", $weeklyInterval = new FieldGroup( + new LabelField($name = "every2", $title = _t("CalendarEvent.EVERY","Every ")), + new DropdownField('WeeklyInterval', '', array_combine(range(1,10), range(1,10))), + new LabelField($name = "weeks", $title = _t("CalendarEvent.WEEKS", " weeks")) + )); + + $f->addFieldToTab("Root.$recursion", new CheckboxSetField( + 'RecurringDaysOfWeek', + _t('CalendarEvent.ONFOLLOWINGDAYS','On the following day(s)...'), + DataList::create("RecurringDayOfWeek")->map("ID", "Title") + )); + + $f->addFieldToTab("Root.$recursion", $monthlyInterval = new FieldGroup( + new LabelField($name="every3", $title = _t("CalendarEvent.EVERY","Every ")), + new DropdownField('MonthlyInterval', '', array_combine(range(1,10), range(1,10))), + new LabelField($name = "months", $title = _t("CalendarEvent.MONTHS"," month(s)")) + )); + + $f->addFieldsToTab("Root.$recursion", array ( + new OptionsetField('MonthlyRecursionType1','', array('1' => _t('CalendarEvent.ONTHESEDATES','On these date(s)...'))), + new CheckboxSetField('RecurringDaysOfMonth', '', DataList::create("RecurringDayOfMonth")->map("ID", "Value")), + new OptionsetField('MonthlyRecursionType2','', array('1' => _t('CalendarEvent.ONTHE','On the...'))) + )); + + $f->addFieldToTab("Root.$recursion", $monthlyIndex = new FieldGroup( + new DropdownField('MonthlyIndex','', array ( + '1' => _t('CalendarEvent.FIRST','First'), + '2' => _t('CalendarEvent.SECOND','Second'), + '3' => _t('CalendarEvent.THIRD','Third'), + '4' => _t('CalendarEvent.FOURTH','Fourth'), + '5' => _t('CalendarEvent.LAST','Last') + )), + new DropdownField('MonthlyDayOfWeek','', DataList::create("RecurringDayOfWeek")->map("Value", "Title")), + new LabelField( $name = "ofthemonth", $title = _t("CalendarEvent.OFTHEMONTH"," of the month.")) + )); + // $f->addFieldToTab("Root.$recursion", + // GridField::create( + // "Exceptions", + // _t('CalendarEvent.ANYEXCEPTIONS','Any exceptions to this pattern? Add the dates below.'), + // $this->Exceptions(), + // GridFieldConfig_RecordEditor::create() + // ) + // )); + $dailyInterval->addExtraClass('dailyinterval'); + $weeklyInterval->addExtraClass('weeklyinterval'); + $monthlyInterval->addExtraClass('monthlyinterval'); + $monthlyIndex->addExtraClass('monthlyindex'); + + $this->extend('updateCMSFields',$f); + + return $f; + + } + + + public function getRecursionReader() { + return new RecursionReader($this); + } + + + + + public function getDateTimeClass() { + return $this->stat('datetime_class'); + } + + + public function CalendarWidget() { + return $this->Parent()->CalendarWidget(); + } + + +} + + + + + +class CalendarEvent_Controller extends Page_Controller { + + public function init() { + parent::init(); + Requirements::css('event_calendar/css/calendar.css'); + } + + + + public function MultipleDates() { + return DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = $this->ID") + ->sort("StartDate ASC") + ->count() > 1; + } + + + + public function DateAndTime() { + return DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = $this->ID") + ->sort("StartDate ASC"); + } + + + + public function UpcomingDates($limit = 3) { + return DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID} AND StartDate >= DATE(NOW())") + ->sort("StartDate ASC") + ->limit($limit); + } + + + + public function OtherDates() { + if(!isset($_REQUEST['date'])) { + $date_obj = DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID}") + ->sort("StartDate ASC") + ->first(); + if(!$date_obj) return false; + else $date = $date_obj->StartDate; + } + elseif(strtotime($_REQUEST['date']) > 0) { + $date = date('Y-m-d', strtotime($_REQUEST['date'])); + $cal = $this->Parent(); + if($this->Recursion == 1) { + $datetime_obj = DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID}") + ->first(); + $datetime_obj->StartDate = $date; + return $cal->getNextRecurringEvents($this, $datetime_obj); + } + else { + return DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID} AND StartDate != '".$date."'") + ->sort("StartDate ASC") + ->limit($cal->OtherDatesCount); + } + } + return false; + } + + + + public function CurrentDate() { + if(!isset($_REQUEST['date'])) { + $obj = DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID}") + ->sort("StartDate ASC") + ->first(); + if($obj) { + $date = $obj->StartDate; + } + } + elseif(strtotime($_REQUEST['date']) > 0) { + $date = date('Y-m-d', strtotime($_REQUEST['date'])); + if($this->Recursion) { + $datetime = DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID}") + ->first(); + if($datetime) { + $datetime->StartDate = $date; + return $datetime; + } + } + $result = DataList::create($this->data()->getDateTimeClass()) + ->where("EventID = {$this->ID} AND StartDate = '".$date."'") + ->first(); + return $result; + } + } + + +} \ No newline at end of file diff --git a/code/CalendarUtil.php b/code/CalendarUtil.php new file mode 100755 index 0000000..0d55612 --- /dev/null +++ b/code/CalendarUtil.php @@ -0,0 +1,241 @@ + 0) { + while($missing > 0) {$str .= "01";$missing-=2;} + } + return substr($str,0,4) . "-" . substr($str,4,2) . "-" . substr($str,6,2); + } + else { + return date('Y-m-d'); + } + } + + + + static function get_date_string($start_date,$end_date) { + $strStartDate = null; + $strEndDate = null; + + $start = strtotime($start_date); + $end = strtotime($end_date); + + $start_year = date("Y", $start); + $start_month = date("m", $start); + + $end_year = date("Y", $end); + $end_month = date("m", $end); + + // Invalid date. Get me out of here! + if($start < 1) return; + + // Only one day long! + else if($start == $end || !$end || $end < 1) { + $key = self::ONE_DAY; + } + + else { + if($start_year == $end_year) { + $key = ($start_month == $end_month) ? self::SAME_MONTH_SAME_YEAR : self::DIFF_MONTH_SAME_YEAR; + } + else { + $key = self::DIFF_MONTH_DIFF_YEAR; + } + } + $date_string = self::localize($start, $end, $key); + $break = strpos($date_string, '$End'); + if($break !== FALSE) { + $strStartDate = substr($date_string, 0, $break); + $strEndDate = substr($date_string, $break+1, strlen($date_string) - strlen($strStartDate)); + return array($strStartDate, $strEndDate); + } + + return array($date_string, ""); + } + + + + public static function microformat($date, $time, $offset = true) { + if(!$date) + return ""; + + $ts = strtotime($date . " " . $time); + + if($ts < 1) + return ""; + + $ret = date('Ymd', $ts) . "T" . date('His',$ts); + return $offset ? $ret . $offset : $ret; + } + + + + public static function get_months_map($key = '%b') { + return array ( + '01' => strftime($key,strtotime('2000-01-01')), + '02' => strftime($key,strtotime('2000-02-01')), + '03' => strftime($key,strtotime('2000-03-01')), + '04' => strftime($key,strtotime('2000-04-01')), + '05' => strftime($key,strtotime('2000-05-01')), + '06' => strftime($key,strtotime('2000-06-01')), + '07' => strftime($key,strtotime('2000-07-01')), + '08' => strftime($key,strtotime('2000-08-01')), + '09' => strftime($key,strtotime('2000-09-01')), + '10' => strftime($key,strtotime('2000-10-01')), + '11' => strftime($key,strtotime('2000-11-01')), + '12' => strftime($key,strtotime('2000-12-01')) + ); + } + + + public static function get_date_format() { + if(CalendarDateTime::$date_format_override) { + return CalendarDateTime::$date_format_override; + } + return _t('CalendarDateTime.DATEFORMAT','mdy'); + } + + + + public static function get_time_format() { + if(CalendarDateTime::$time_format_override) { + return CalendarDateTime::$time_format_override; + } + return _t('CalendarDateTime.TIMEFORMAT','24'); + } + + + + public static function get_first_day_of_week() { + $result = strtolower(_t('CalendarDateTime.FIRSTDAYOFWEEK','monday')); + return ($result == "monday") ? sfTime::MONDAY : sfTime::SUNDAY; + } + + + + public static function date_sort(&$data) { + uasort($data, array("CalendarUtil","date_sort_callback")); + } + + /** + * Callback used by column_sort + */ + public static function date_sort_callback($a, $b) { + if($a->StartDate == $b->StartDate) { + if($a->StartTime == $b->StartTime) + return 0; + else if(strtotime($a->StartTime) > strtotime($b->StartTime)) + return 1; + else + return -1; + } + else if(strtotime($a->StartDate) > strtotime($b->StartDate)) + return 1; + else + return -1; + + } + + + + + +} \ No newline at end of file diff --git a/code/CalendarWidget.php b/code/CalendarWidget.php new file mode 100755 index 0000000..5108361 --- /dev/null +++ b/code/CalendarWidget.php @@ -0,0 +1,66 @@ + +calendar = $calendar; + } + + + + public function setOption($k, $v) { + $this->options[$k] = $v; + } + + + + public function getDataAttributes() { + $attributes = ""; + $this->options['url'] = $this->calendar->Link(); + + foreach($this->options as $opt => $value) { + $attributes .= sprintf('data-%s="%s" ', $opt, Convert::raw2att($value)); + } + return $attributes; + } + + + + public function setSelectionStart($date) { + $this->selectionStart = $date; + } + + + + public function setSelectionEnd($date) { + $this->selectionEnd = $date; + } + + + public function forTemplate() { + Requirements::javascript(THIRDPARTY_DIR."/jquery/jquery.js"); + Requirements::javascript("event_calendar/javascript/calendar_widget.js"); + $locale_file = _t('Calendar.DATEJSFILE','calendar_en.js'); + Requirements::javascript("event_calendar/javascript/i18n/{$locale_file}"); + Requirements::javascript("event_calendar/javascript/calendar_widget_init.js"); + Requirements::css("event_calendar/css/calendar_widget.css"); + return '
getDataAttributes() . '>
'; + } +} \ No newline at end of file diff --git a/code/ICSFeed.php b/code/ICSFeed.php new file mode 100755 index 0000000..c8dc2a4 --- /dev/null +++ b/code/ICSFeed.php @@ -0,0 +1,28 @@ + 'Varchar(100)', + 'URL' => 'Varchar(255)' + ); + + + + static $has_one = array ( + 'Calendar' => 'Calendar' + ); + + + + public function getCMSFields() { + $f = new FieldList ( + new TextField('Title',_t('ICSFeed.TITLEOFFEED','Title of feed')), + new TextField('URL',_t('ICSFeed.URLLINK','URL'),'http://') + ); + + $this->extend('updateCMSFields', $f); + + return $f; + } +} \ No newline at end of file diff --git a/code/ICSWriter.php b/code/ICSWriter.php new file mode 100755 index 0000000..4690fd5 --- /dev/null +++ b/code/ICSWriter.php @@ -0,0 +1,168 @@ +Examples + * + *

Send to client

+ * + * + * $writer = new ICSWriter($this->data(), Director::absoluteURL('/')); + * $writer->sendDownload(); + * + * + *

Get output

+ * + * + * $writer = new ICSWriter($this->data(), Director::absoluteURL('/')); + * $writer->getOutput(); + * + * + * @todo Support recurring events + * @copyright 2011 Dimension27 + * @author Alex Hayes + * @link https://github.com/dimension27/EventCalendar + */ +class ICSWriter +{ + + /** + * @var Calendar + */ + public $calendar; + + public $host; + public $prodid; + public $limit; + + /** + * @var array + */ + protected $lines = array(); + + /** + * Construct an ICSWriter instance. + * + * @param Calendar $calendar The calendar to render. + * @param string $host The calendar host. + * @param string $prodid Specifies the identifier for the product that created the iCalendar object. If + * null then $host will be used in the generation of this. + * @param int $limit Limit the amount of upcoming events to this number + * + * @author Alex Hayes + */ + public function __construct( Calendar $calendar, $host, $prodid = null, $limit = 100 ) { + $this->calendar = $calendar; + $this->host = $host; + $this->prodid = $prodid; + $this->limit = $limit; + } + + public function sendDownload() { + header("Cache-Control: private"); + header("Content-Description: File Transfer"); + header("Content-Type: text/calendar"); + header("Content-Transfer-Encoding: binary"); + $filename = preg_replace("/[^a-zA-Z0-9s]/", "", $this->calendar->Title) . '.ics'; + if(stristr($_SERVER['HTTP_USER_AGENT'], "MSIE")) { + header("Content-disposition: filename=" . $filename . "; attachment;"); + } else { + header("Content-disposition: attachment; filename=" . $filename); + } + echo $this->getOutput(); + } + + /** + * Get the calendar as a string. + * + * @author Alex Hayes + */ + public function getOutput() { + $this->lines = array(); + + $this->addLine('BEGIN:VCALENDAR'); + $this->addLine('VERSION:2.0'); + + if( is_null($this->prodid) ) { + $this->addLine("PRODID:" . '-//'.$this->host.'//NONSGML v1.0//EN'); + } + elseif( !is_null($this->prodid) ) { + $this->addLine("PRODID:" . $this->prodid); + } + + $upcomingEvents = $this->calendar->UpcomingEvents($this->limit); /* @var $upcomingEvents DataObjectSet */ + foreach($upcomingEvents as $dateTime) { /* @var $event CalendarDateTime */ + $this->addDateTime($dateTime); + } + + $this->addLine('END:VCALENDAR'); + + return implode("\r\n", $this->lines); + } + + /** + * Add a line to the stack. + * + * @param string $line + * @return void + * + * @author Alex Hayes + */ + protected function addLine($line) { + $this->lines[] = $line; + } + + /** + * + * + * @param CalendarDateTime $dateTime + * @return string + * + * @author Alex Hayes + */ + protected function getUID( CalendarDateTime $dateTime ) { + return $dateTime->ID.'@'.$this->host; + } + + /** + * Get an ical formatted datetime string. + * + * @param Date $date + * @param Time $time + * @return string + * + * @todo Add timezone support - note atm there is no timezone support in either Date or Time. + * + * @author Alex Hayes + */ + protected function getFormatedDateTime( Date $date = null, Time $time = null ) { + $timestamp = null; + if($date && $time) { + $timestamp = strtotime($date . ' ' . $time); + } + else { + $timestamp = time(); + } + return gmdate('Ymd\THis\Z', $timestamp); + } + + /** + * Add a CalendarDateTime to the stack. + * + * @param CalendarDateTime $dateTime + * @return void + * + * @author Alex Hayes + */ + protected function addDateTime( CalendarDateTime $dateTime ) { + $this->addLine('BEGIN:VEVENT'); + $this->addLine('UID:' . $this->getUID($dateTime) ); + $this->addLine('DTSTAMP;TZID=' . Calendar::$timezone . ':' . $this->getFormatedDateTime()); + $this->addLine('DTSTART;TZID=' . Calendar::$timezone . ':' . $this->getFormatedDateTime($dateTime->StartDate, $dateTime->StartTime)); + $this->addLine('DTEND;TZID=' . Calendar::$timezone . ':' . $this->getFormatedDateTime($dateTime->StartDate, $dateTime->StartTime)); + $this->addLine('URL:' . Director::absoluteURL($dateTime->ICSLink())); + $this->addLine('SUMMARY:CHARSET=UTF-8:' . $dateTime->Event()->Title); + $this->addLine('END:VEVENT'); + } + +} diff --git a/code/RecurringDayOfMonth.php b/code/RecurringDayOfMonth.php new file mode 100755 index 0000000..8ca5aff --- /dev/null +++ b/code/RecurringDayOfMonth.php @@ -0,0 +1,48 @@ + 'Int' + ); + + + + static $belongs_many_many = array ( + 'CalendarEvent' => 'CalendarEvent' + ); + + + + static $default_sort = "Value ASC"; + + + + static function create_default_records() { + for($i = 1; $i <= 30; $i++) { + $record = new RecurringDayOfMonth(); + $record->Value = $i; + $record->write(); + } + } + + + + public function requireDefaultRecords() { + parent::requireDefaultRecords(); + $records = DataList::create("RecurringDayOfMonth"); + if(!$records->exists()) { + self::create_default_records(); + } + elseif($records->count() != 30) { + foreach($records as $record) { + $record->delete(); + } + self::create_default_records(); + } + } + + +} \ No newline at end of file diff --git a/code/RecurringDayOfWeek.php b/code/RecurringDayOfWeek.php new file mode 100755 index 0000000..08c255d --- /dev/null +++ b/code/RecurringDayOfWeek.php @@ -0,0 +1,53 @@ + 'Int' + ); + + + + static $default_sort = "Value ASC"; + + + + static $belongs_many_many = array ( + 'CalendarEvent' => 'CalendarEvent' + ); + + + + + static function create_default_records() { + for($i = 0; $i <= 6; $i++) { + $record = new RecurringDayOfWeek(); + $record->Value = $i; + $record->write(); + } + } + + + + public function requireDefaultRecords() { + parent::requireDefaultRecords(); + $records = DataList::create("RecurringDayOfWeek"); + if(!$records->exists()) { + self::create_default_records(); + } + elseif($records->count() != 7) { + foreach($records as $record) { + $record->delete(); + } + self::create_default_records(); + } + } + + + public function getTitle() { + return strftime("%a", sfDate::getInstance()->nextDay($this->Value)->get()); + } + +} \ No newline at end of file diff --git a/code/RecurringException.php b/code/RecurringException.php new file mode 100755 index 0000000..e957596 --- /dev/null +++ b/code/RecurringException.php @@ -0,0 +1,18 @@ + 'Date' + ); + + + + static $has_one = array ( + 'CalendarEvent' => 'CalendarEvent' + ); + + + static $default_sort = "ExceptionDate ASC"; + +} \ No newline at end of file diff --git a/code/RecursionReader.php b/code/RecursionReader.php new file mode 100755 index 0000000..2c46e5b --- /dev/null +++ b/code/RecursionReader.php @@ -0,0 +1,115 @@ +format('Y') * 12) + $dateObj1->format('n')) - (($dateObj2->format('Y') * 12) + $dateObj2->format('n')); + } + + + public function __construct(CalendarEvent $event) { + $this->event = $event; + $this->datetimeClass = $event->Parent()->getDateTimeClass(); + $this->eventClass = $event->Parent()->getEventClass(); + $relation = $event->Parent()->getDateToEventRelation(); + + if($datetime = DataList::create($this->datetimeClass)->where("{$relation} = {$event->ID}")->first()) { + $this->ts = strtotime($datetime->StartDate); + } + + if($event->CustomRecursionType == 2) { + if($days_of_week = $event->getManyManyComponents('RecurringDaysOfWeek')) { + foreach($days_of_week as $day) { + $this->allowedDaysOfWeek[] = $day->Value; + } + } + } + + else if($event->CustomRecursionType == 3) { + if($days_of_month = $event->getManyManyComponents('RecurringDaysOfMonth')) { + foreach($days_of_month as $day) { + $this->allowedDaysOfMonth[] = $day->Value; + } + } + } + + if($exceptions = $event->getComponents('Exceptions')) { + foreach($exceptions as $exception) { + $this->exceptions[] = $exception->ExceptionDate; + } + } + } + + + + public function recursionHappensOn($ts) + { + + $objTestDate = new sfDate($ts); + $objStartDate = new sfDate($this->ts); + + // Current date is before the recurring event begins. + if($objTestDate->get() < $objStartDate->get()) + return false; + elseif(in_array($objTestDate->date(), $this->exceptions)) + return false; + + switch($this->event->CustomRecursionType) + { + // Daily + case 1: + return $this->event->DailyInterval ? (($ts - $this->ts) / self::DAY) % $this->event->DailyInterval == 0 : false; + break; + // Weekly + case 2: + return ((($objTestDate->firstDayOfWeek()->get() - $objStartDate->firstDayOfWeek()->get()) / self::WEEK) % $this->event->WeeklyInterval == 0) + && + (in_array($objTestDate->reset()->format('w'), $this->allowedDaysOfWeek)); + break; + // Monthly + case 3: + if(self::difference_in_months($objTestDate,$objStartDate) % $this->event->MonthlyInterval == 0) { + // A given set of dates in the month e.g. 2 and 15. + if($this->event->MonthlyRecursionType1 == 1) { + return (in_array($objTestDate->reset()->format('j'), $this->allowedDaysOfMonth)); + } + // e.g. "First Monday of the month" + elseif($this->event->MonthlyRecursionType2 == 1) { + // Last day of the month? + if($this->event->MonthlyIndex == 5) { + $targetDate = $objTestDate->addMonth()->firstDayOfMonth()->previousDay($this->event->MonthlyDayOfWeek)->dump(); + } + else { + $objTestDate->subtractMonth()->finalDayOfMonth(); + for($i=0; $i < $this->event->MonthlyIndex; $i++) { + $objTestDate->nextDay($this->event->MonthlyDayOfWeek)->dump(); + } + $targetDate = $objTestDate->dump(); + } + return $objTestDate->reset()->dump() == $targetDate; + } + } + return false; + } + } + + +} \ No newline at end of file diff --git a/code/sfdate/sfDate.php b/code/sfdate/sfDate.php new file mode 100755 index 0000000..13a0581 --- /dev/null +++ b/code/sfdate/sfDate.php @@ -0,0 +1,253 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * + * sfDate class. + * + * A class for representing a date/time value as an object. + * + * This class allows for chainable calculations using the sfTime utility class. + * + * @package sfDateTimePlugin + * @author Stephen Riesenberg + * @version SVN: $Id$ + */ +class sfDate +{ + /** + * The timestamp for this sfDate instance. + */ + private $ts = null; + + /** + * The original timestamp for this sfDate instance. + */ + private $init = null; + + /** + * Retrieves a new instance of this class. + * + * NOTE: This is not the singleton pattern. Instead, it is for chainability ease-of-use. + * + * Example: + * + * echo sfDate::getInstance()->getFirstDayOfWeek()->addDay()->format('Y-m-d'); + * + * + * @param mixed timestamp, string, or sfDate object + * @return sfDate + */ + public static function getInstance($value = null) + { + return new sfDate($value); + } + + /** + * Construct an sfDate object. + * + * @param mixed timestamp, string, or sfDate object + */ + public function __construct($value = null) + { + $this->set($value); + } + + /** + * Format the date according to the date function. + * + * @return string + */ + public function format($format) + { + return date($format, $this->ts); + } + + /** + * Formats the date according to the format_date helper of the Date helper group. + * + * @return string + */ + public function date($format = 'd') + { + return date('Y-m-d', $this->ts); + } + + /** + * Formats the date according to the format_datetime helper of the Date helper group. + * + * @return string + */ + public function datetime($format = 'F') + { + return date('Y-m-d H:i:s', $this->ts); + } + + /** + * Format the date as a datetime value. + * + * @return string + */ + public function dump() + { + return date('Y-m-d H:i:s', $this->ts); + } + + /** + * Retrieves the given unit of time from the timestamp. + * + * @param int unit of time (accepts sfTime constants). + * @return int the unit of time + * + * @throws sfDateTimeException + */ + public function retrieve($unit = sfTime::DAY) + { + switch ($unit) + { + case sfTime::SECOND: + return date('s', $this->ts); + case sfTime::MINUTE: + return date('i', $this->ts); + case sfTime::HOUR: + return date('H', $this->ts); + case sfTime::DAY: + return date('d', $this->ts); + case sfTime::WEEK: + return date('W', $this->ts); + case sfTime::MONTH: + return date('m', $this->ts); + case sfTime::QUARTER: + return ceil(date('m', $this->ts) / 3); + case sfTime::YEAR: + return date('Y', $this->ts); + case sfTime::DECADE: + return ceil((date('Y', $this->ts) % 100) / 10); + case sfTime::CENTURY: + return ceil(date('Y', $this->ts) / 100); + case sfTime::MILLENIUM: + return ceil(date('Y', $this->ts) / 1000); + default: + throw new sfDateTimeException(sprintf('The unit of time provided is not valid: %s', $unit)); + } + } + + /** + * Retrieve the timestamp value of this sfDate instance. + * + * @return timestamp + */ + public function get() + { + return $this->ts; + } + + /** + * Sets the timestamp value of this sfDate instance. + * + * This function accepts several froms of a date value: + * - timestamp + * - string, parsed with strtotime + * - sfDate object + * + * @return sfDate the modified object, for chainability + */ + public function set($value = null) + { + $ts = sfDateTimeToolkit::getTS($value); + + $this->ts = $ts; + if ($this->init === null) + { + $this->init = $ts; + } + + return $this; + } + + /** + * Resets the timestamp value of this sfDate instance to its original value. + * + * @return sfDate the reset object, for chainability + */ + public function reset() + { + $this->ts = $this->init; + + return $this; + } + + /** + * Compares two date values. + * + * @param mixed timestamp, string, or sfDate object + * @return int -1, 0, or 1 + */ + public function cmp($value) + { + $ts = sfDateTimeToolkit::getTS($value); + + if ($this->ts < $ts) + { + // less than + return -1; + } + else if ($this->ts > $ts) + { + // greater than + return 1; + } + + // equal to + return 0; + } + + /** + * Gets the difference of two date values in seconds. + * + * @param mixed timestamp, string, or sfDate object + * @param int the difference in seconds + */ + public function diff($value) + { + $ts = sfDateTimeToolkit::getTS($value); + + return $this->ts - $ts; + } + + /** + * Call any function available in the sfTime library, but without the ts parameter. + * + * Example: + * + * $ts = sfTime::firstDayOfMonth(sfTime::addMonth(time(), 5)); + * // equivalent + * $dt = new sfDate(); + * $ts = $dt->addMonth(5)->firstDayOfMonth()->get(); + * + * + * @return sfDate the modified object, for chainability + */ + public function __call($method, $arguments) + { + $callable = array('sfTime', $method); + + if (!is_callable($callable)) + { + throw new sfDateTimeException(sprintf('Call to undefined function: %s::%s', 'sfDate', $method)); + } + + array_unshift($arguments, $this->ts); + + $this->ts = call_user_func_array($callable, $arguments); + + return $this; + } +} \ No newline at end of file diff --git a/code/sfdate/sfDateTimeToolkit.php b/code/sfdate/sfDateTimeToolkit.php new file mode 100755 index 0000000..3632fac --- /dev/null +++ b/code/sfdate/sfDateTimeToolkit.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * + * sfDateTimeToolkit class. + * + * A toolkit for the sfDateTimePlugin. + * + * @package sfDateTimePlugin + * @author Stephen Riesenberg + * @version SVN: $Id$ + */ +class sfDateTimeToolkit +{ + /** + * Breaks down the individual components of the timestamp. + * + * @param timestamp + * @return array + */ + public static function breakdown($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // gather individual variables + $H = date('H', $ts); // hour + $i = date('i', $ts); // minute + $s = date('s', $ts); // second + $m = date('m', $ts); // month + $d = date('d', $ts); // day + $Y = date('Y', $ts); // year + + return array($H, $i, $s, $m, $d, $Y); + } + + /** + * Returns the current timestamp. + * + * @return timestamp + * + * @see time + */ + public static function now() + { + return time(); + } + + /** + * Retrieve the timestamp from a number of different formats. + * + * @param mixed value to use for timestamp retrieval + */ + public static function getTS($value = null) + { + if ($value === null) + { + return sfDateTimeToolkit::now(); + } + else if ($value instanceof sfDate) + { + return $value->get(); + } + else if (!is_numeric($value)) + { + return strtotime($value); + } + else if (is_numeric($value)) + { + return $value; + } + + throw new sfDateTimeException(sprintf('A timestamp could not be retrieved from the value: %s', $value)); + } +} \ No newline at end of file diff --git a/code/sfdate/sfTime.php b/code/sfdate/sfTime.php new file mode 100755 index 0000000..34530be --- /dev/null +++ b/code/sfdate/sfTime.php @@ -0,0 +1,802 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * + * sfTime class. + * + * A library for manipulating dates in symfony (php). + * + * @package sfDateTimePlugin + * @author Stephen Riesenberg + * @version SVN: $Id$ + */ +class sfTime +{ + /** + * Units of time + */ + const SECOND = 0; + const MINUTE = 1; + const HOUR = 2; + const DAY = 3; + const WEEK = 4; + const MONTH = 5; + const QUARTER = 6; + const YEAR = 7; + const DECADE = 8; + const CENTURY = 9; + const MILLENIUM = 10; + + /** + * Days of the week + */ + const SUNDAY = 0; + const MONDAY = 1; + const TUESDAY = 2; + const WEDNESDAY = 3; + const THURSDAY = 4; + const FRIDAY = 5; + const SATURDAY = 6; + + /** + * Months of the year + */ + const JANUARY = 1; + const FEBRUARY = 2; + const MARCH = 3; + const APRIL = 4; + const MAY = 5; + const JUNE = 6; + const JULY = 7; + const AUGUST = 8; + const SEPTEMBER = 9; + const OCTOBER = 10; + const NOVEMBER = 11; + const DECEMBER = 12; + + /** + * Adds the specified number of given units of time to the given date. + * + * Example: + * + * // tomorrow + * $dt = sfTime::add(); + * // day after + * $dt = sfTime::add($mydate); + * // 5 days after + * $dt = sfTime::add($mydate, 5); + * // 2 months after + * $dt = sfTime::add($mydate, 2, sfTime::MONTH); + * // 4 weeks after + * $dt = sfTime::add($mydate, 4, sfTime::WEEK); + * + * + * @param timestamp a timestamp for the calculation + * @param int the number of units to add to the given date + * @param int the unit to add by + * @return timestamp the timestamp result of the calculation + * + * @throws sfDateTimeException + */ + public static function add($ts = null, $num = 1, $unit = sfTime::DAY) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // gather individual variables for readability and maintainability + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + // determine which unit of time to add by + switch ($unit) + { + case sfTime::SECOND: + return mktime($H, $i, $s + $num, $m, $d, $Y); + case sfTime::MINUTE: + return mktime($H, $i + $num, $s, $m, $d, $Y); + case sfTime::HOUR: + return mktime($H + $num, $i, $s, $m, $d, $Y); + case sfTime::DAY: + return mktime($H, $i, $s, $m, $d + $num, $Y); + case sfTime::WEEK: + return mktime($H, $i, $s, $m, $d + (7 * $num), $Y); + case sfTime::MONTH: + return mktime($H, $i, $s, $m + $num, $d, $Y); + case sfTime::QUARTER: + return mktime($H, $i, $s, $m + (3 * $num), $d, $Y); + case sfTime::YEAR: + return mktime($H, $i, $s, $m, $d, $Y + $num); + case sfTime::DECADE: + return mktime($H, $i, $s, $m, $d, $Y + (10 * $num)); + case sfTime::CENTURY: + return mktime($H, $i, $s, $m, $d, $Y + (100 * $num)); + case sfTime::MILLENIUM: + return mktime($H, $i, $s, $m, $d, $Y + (1000 * $num)); + default: + throw new sfDateTimeException(sprintf('The unit of time provided is not valid: %s', $unit)); + } + } + + /** + * Subtracts the specified number of given units of time from the given date. + * + * Example: + * + * // yesterday + * $dt = sfTime::subtract(); + * // day before + * $dt = sfTime::subtract($mydate); + * // 5 days before + * $dt = sfTime::subtract($mydate, 5); + * // 2 months before + * $dt = sfTime::subtract($mydate, 2, sfTime::MONTH); + * // 4 weeks before + * $dt = sfTime::subtract($mydate, 4, sfTime::WEEK); + * + * + * @param timestamp a timestamp for the calculation + * @param int the number of units to add to the given date + * @param int the unit to add by + * @return timestamp the timestamp result of the calculation + * + * @see add + */ + public static function subtract($ts = null, $num = 1, $unit = sfTime::DAY) + { + return sfTime::add($ts, $num * -1, $unit); + } + + /** + * Returns the timestamp with the date but without the time of day. + * + * @param timestamp + * @return timestamp + */ + public static function clearTime($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime(0, 0, 0, $m, $d, $Y); + } + + /** + * Returns the timestamp with the time of day but without the date. + * + * @deprecated This is a deprecated function. Do not use! + * + * @param timestamp + * @return timestamp + */ + public static function clearDate($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $i, $s, 0, 0, 0); + } + + /** + * Clear the second value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function clearSecond($ts = null) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $i, 0, $m, $d, $Y); + } + + /** + * Clear the minute value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function clearMinute($ts = null) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, 0, $s, $m, $d, $Y); + } + + /** + * Clear the hour value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function clearHour($ts = null) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime(0, $i, $s, $m, $d, $Y); + } + + /** + * Set the second value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function setSecond($ts = null, $second = 0) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $i, $second, $m, $d, $Y); + } + + /** + * Set the minute value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function setMinute($ts = null, $minute = 0) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $minute, $s, $m, $d, $Y); + } + + /** + * Set the hour value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function setHour($ts = null, $hour = 0) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($hour, $i, $s, $m, $d, $Y); + } + + /** + * Set the day value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function setDay($ts = null, $day = 1) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $i, $s, $m, $day, $Y); + } + + /** + * Set the month value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function setMonth($ts = null, $month = 1) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $i, $s, $month, $d, $Y); + } + + /** + * Set the year value of this timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function setYear($ts = null, $year = 1970) + { + list($H, $i, $s, $m, $d, $Y) = sfDateTimeToolkit::breakdown($ts); + + return mktime($H, $i, $s, $m, $d, $year); + } + + /** + * Returns the timestamp for tomorrow. + * + * Alias for sfTime::addDay + * + * @param timestamp + * @return timestamp + */ + public static function tomorrow($ts = null) + { + return sfTime::add($ts); + } + + /** + * Returns the timestamp for yesterday. + * + * Alias for sfTime::subtractDay + * + * @param timestamp + * @return timestamp + */ + public static function yesterday($ts = null) + { + return sfTime::subtract($ts); + } + + /** + * Adds the specified number of seconds to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addSecond($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::SECOND); + } + + /** + * Subtracts the specified number of seconds from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractSecond($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::SECOND); + } + + /** + * Adds the specified number of minutes to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addMinute($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::MINUTE); + } + + /** + * Subtracts the specified number of minutes from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractMinute($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::MINUTE); + } + + /** + * Adds the specified number of hours to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addHour($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::HOUR); + } + + /** + * Subtracts the specified number of hours from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractHour($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::HOUR); + } + + /** + * Adds the specified number of days to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addDay($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::DAY); + } + + /** + * Subtracts the specified number of days from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractDay($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::DAY); + } + + /** + * Adds the specified number of weeks to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addWeek($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::WEEK); + } + + /** + * Subtracts the specified number of weeks from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractWeek($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::WEEK); + } + + /** + * Adds the specified number of months to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addMonth($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::MONTH); + } + + /** + * Subtracts the specified number of months from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractMonth($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::MONTH); + } + + /** + * Adds the specified number of quarters to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addQuarter($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::QUARTER); + } + + /** + * Subtracts the specified number of quarters from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractQuarter($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::QUARTER); + } + + /** + * Adds the specified number of years to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addYear($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::YEAR); + } + + /** + * Subtracts the specified number of years from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractYear($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::YEAR); + } + + /** + * Adds the specified number of decades to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addDecade($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::DECADE); + } + + /** + * Subtracts the specified number of decades from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractDecade($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::DECADE); + } + + /** + * Adds the specified number of centuries to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addCentury($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::CENTURY); + } + + /** + * Subtracts the specified number of centuries from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractCentury($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::CENTURY); + } + + /** + * Adds the specified number of millenia to the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function addMillenium($ts = null, $num = 1) + { + return sfTime::add($ts, $num, sfTime::MILLENIUM); + } + + /** + * Subtracts the specified number of millenia from the timestamp. + * + * @param timestamp + * @param int + * @return timestamp + */ + public static function subtractMillenium($ts = null, $num = 1) + { + return sfTime::subtract($ts, $num, sfTime::MILLENIUM); + } + + /** + * Returns the timestamp for first day of the week for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function firstDayOfWeek($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + return sfTime::subtractDay($ts, date('w', $ts)); + } + + /** + * Returns the timestamp for last day of the week for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function finalDayOfWeek($ts = null) + { + return sfTime::subtractDay(sfTime::firstDayOfWeek(sfTime::addWeek($ts))); + } + + /** + * Returns the timestamp for first day of the month for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function firstDayOfMonth($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + return sfTime::subtractDay($ts, date('d', $ts) - 1); + } + + /** + * Returns the timestamp for last day of the month for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function finalDayOfMonth($ts = null) + { + return sfTime::subtractDay(sfTime::firstDayOfMonth(sfTime::addMonth($ts))); + } + + /** + * Returns the timestamp for first day of thequarter for the given date. + * + * NOTE: Computes the quarter as: + * + * $quarter = ceil(date('m', $ts) / 3); // 1 - 4 + * + * + * @param timestamp + * @return timestamp + */ + public static function firstDayOfQuarter($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // variables for computation + $month = date('m', $ts); + $quarter = ceil($month / 3) - 1; // zero based quarter + + return sfTime::subtractMonth(sfTime::firstDayOfMonth($ts), $month - ($quarter * 3) - 1); + } + + /** + * Returns the timestamp for last day of the quarter for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function finalDayOfQuarter($ts = null) + { + return sfTime::subtractDay(sfTime::firstDayOfQuarter(sfTime::addQuarter($ts))); + } + + /** + * Returns the timestamp for first day of the year for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function firstDayOfYear($ts = null) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + return sfTime::subtractMonth(sfTime::firstDayOfMonth($ts), date('m', $ts) - 1); + } + + /** + * Returns the timestamp for last day of the year for the given date. + * + * @param timestamp + * @return timestamp + */ + public static function finalDayOfYear($ts = null) + { + return sfTime::subtractDay(sfTime::firstDayOfYear(sfTime::addYear($ts))); + } + + /** + * Returns the timestamp for the next occurance of [day]. + * + * @param timestamp + * @param int the day of week + * @return timestamp + */ + public static function nextDay($ts = null, $day = sfTime::SUNDAY) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // get offsets from sunday + $offset1 = date('w', $ts); + $offset2 = $day; + + // adjust if date wraps into next week + $offset2 += $offset2 > $offset1 ? 0 : 7; + + return sfTime::addDay($ts, $offset2 - $offset1); + } + + /** + * Returns the timestamp for the most recent (previous) occurance of [day]. + * + * @param timestamp + * @param int the day of week + * @return timestamp + */ + public static function previousDay($ts = null, $day = sfTime::SUNDAY) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // get offsets from sunday + $offset1 = date('w', $ts); + $offset2 = $day; + + // adjust if date wraps into last week + $offset1 += $offset1 > $offset2 ? 0 : 7; + + return sfTime::subtractDay($ts, $offset1 - $offset2); + } + + /** + * Returns the timestamp for the next occurance of [month]. + * + * @param timestamp + * @param int the month of year + * @return timestamp + */ + public static function nextMonth($ts = null, $month = sfTime::JANUARY) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // get offsets from january + $offset1 = date('m', $ts); + $offset2 = $month; + + // adjust if date wraps into next year + $offset2 += $offset2 > $offset1 ? 0 : 12; + + return sfTime::addMonth($ts, $offset2 - $offset1); + } + + /** + * Returns the timestamp for the most recent (previous) occurance of [month]. + * + * @param timestamp + * @param int the month of year + * @return timestamp + */ + public static function previousMonth($ts = null, $month = sfTime::JANUARY) + { + // default to now + if ($ts === null) $ts = sfDateTimeToolkit::now(); + + // get offsets from january + $offset1 = date('m', $ts); + $offset2 = $month; + + // adjust if date wraps into last year + $offset1 += $offset1 > $offset2 ? 0 : 12; + + return sfTime::subtractMonth($ts, $offset1 - $offset2); + } +} \ No newline at end of file diff --git a/css/calendar.css b/css/calendar.css new file mode 100755 index 0000000..b6cb9ea --- /dev/null +++ b/css/calendar.css @@ -0,0 +1,116 @@ +.calendar-view-more {display:block;} +.calendar-view-more.loading {background:url(../images/loader.gif) no-repeat;text-indent:-9999em;} +.calendar-quick-nav li {display:inline;} +.calendar-quick-nav li a.current {background: #ddd}; + +/* [YYYY]-[MM]-[DD]T[hh]:[mm]-[hh] */ + +/*Button styling +===============================*/ + +a.btn { display:block; padding:4px; color:#fff; background:#225eaf; border:1px solid #dedede; font-size:9px; +text-transform:uppercase; text-align:center; text-decoration:none; } +a.btn:hover { color:#fff; background:#6a6a6a; border:1px solid #dedede; } + +a.add { width:16px; height:16px; background:transparent url(../images/calendar__plus.png) 0 0 no-repeat; padding:0; border:0; text-indent:-9999em; } +a.add:hover { background:transparent url(../images/calendar__plus.png) 0 0 no-repeat; border:0; } + +/*Top Heading +===============================*/ +#topHeading { border-bottom:3px solid #6a6a6a; padding:12px 0 4px 0; } +#topHeading h2 {clear:both;} +#topHeading span { display:block; margin-top:12px;float:left; } +#topHeading span.feed { padding:3px 0 3px 20px;float:right; background:transparent url('../images/feed.png') left top no-repeat; } + +/*Date Heading*/ +#dateHeader {text-align:center; margin:8px 0 ; padding: 18px 0 16px 0; border-top:3px double #D4D4D4; border-bottom:3px double #D4D4D4;} +#dateHeader h3 {line-height: 0; margin: 0; padding:0;} + +/*Month Navs +==============================*/ +#monthNav { text-align:center; background: #dedede; padding:8px 0; margin:10px 0 22px; } +#monthNav a { width:20px;} +#monthNav h2 {cursor:pointer;} +#monthNav h2:hover {color:#aaa;} +#monthNav a:hover { } +#monthNav a.prev { float:left; } +#monthNav a.next { float:right; } +#monthNav h3 { padding:0; margin:0; font-size:18px; } + +#monthSelect { background:#dedede; border:2px solid #c4c4c4; text-align:center; padding:10px; margin-top:10px; } +#monthSelect select { width:100%; } + +/*Sort and View Type +==============================*/ +#calView { border-bottom: 2px solid #dedede; } +#calView ul { display:inline; float:left; margin:8px 0 0 140px; } +#calView ul li { display:inline; margin:0; } +#calView ul li span { float:left; margin-right:8px; padding-bottom:6px; } + +#calView ul li a { float:left; padding-bottom:3px; margin-left:5px; text-decoration:none; } +#calView ul li a:hover, #calView ul li a.active { border-bottom:3px solid #666; text-decoration:none; } +#calView ul li a:hover { border-color:#aaa; } + +#calView ul li.more a, #calView ul li.less a { display:block; text-decoration:none; padding:0 0 3px; margin:0 5px 0 0; } +#calView ul li.more a.open, #calView ul li.less a.open { border-bottom:3px solid #666; } +#calView ul li span {font-weight:700;} +#calView h5 {float:right; display: inline; margin:7px 8px 0 0; padding: 0;} + +/*Event Summary (single date) +==============================*/ +.vevent { border-bottom: 1px solid #dedede; padding:10px 0; } + +.vevent div.dates, .vevent div.details { float:left; } +.vevent ul.utility { float:right; } +.vevent div.dates { width:20%; margin-right:15px; } +.vevent div.details { width:54%; margin-right:0; padding-left:15px; border-left:1px solid #dedede; } +.vevent ul.utility { width:100px; margin:0; list-style:none; } +.vevent ul.utility li { list-style:none; margin:0 0 5px; } + +.vevent dl.more-dates { margin:10px 0 0; } +.vevent dl.more-dates dt { font-weight:700; } + +.vevent .summary { margin:0 0 5px; } +.vevent .location { text-transform:uppercase; font-size:11px; } +.vevent .description { margin:5px 0; clear:both; } + +.vevent .dtstart { display:inline; font-weight:700; text-transform:uppercase; border:0; margin:0 0 10px; } +.vevent .dtend { display:inline; font-weight:700; border:0; } +.vevent h5 span.dtstart, .vevent h5 span.dtend {display: inline;} + +.vevent dt span.dtstart, .vevent dt span.dtend { display:inline; } + +.vevent dl, .vevent dt, .vevent dd { margin:0; } +.vevent dt { font-weight:700; clear:both; float:left; margin-bottom:4px; } +.vevent dd { float:left; } +.vevent h4.dates { font-size:12px; margin:5px 0; border-bottom:1px solid #ccc; width:30%; } + +.vevent #eventImage img { float:right; } + +/* Event Detail with Image*/ +#eventImage { padding:5px; } + +#Form_CalendarFilterForm {padding:0 10px;} +#Form_CalendarFilterForm fieldset {border:0;} +#Form_CalendarFilterForm div.field {padding:5px 0;} +#Form_CalendarFilterForm div.middleColumn {background:none;margin:0;padding:0;} +#Form_CalendarFilterForm label {display:block;margin:0;} +#Form_CalendarFilterForm div.field {width:216px;text-align:left;} +#Form_CalendarFilterForm select {width:auto;clear:both;} +#Form_CalendarFilterForm option {width:auto;padding:0 5px;} +#Form_CalendarFilterForm div.Actions {margin:0;text-align:left;} +#Form_CalendarFilterForm div.fieldgroup select {font-size:12px;font-weight:normal;} + +#StartMonthStartDayStartYear div.fieldgroupField, +#StartDayStartMonthStartYear div.fieldgroupField {display:inline;} +#EndMonthEndDayEndYear div.fieldgroupField, +#EndDayEndMonthEndYear div.fieldgroupField {display:inline;} +#Form_CalendarFilterForm div.fieldgroup label {display:block;width:200px;border-bottom:1px solid #aaa;margin-bottom:5px;} + +/* clearfix */ +.clearfix:after {content: "."; display: block; height: 0; clear: both; visibility: hidden;} +.clearfix {display: inline-block;} +/* Hides from IE-mac \*/ +* html .clearfix {height: 1%;} +.clearfix {display: block;} +/* End hide from IE-mac */ \ No newline at end of file diff --git a/css/calendar_widget.css b/css/calendar_widget.css new file mode 100755 index 0000000..ff8a38d --- /dev/null +++ b/css/calendar_widget.css @@ -0,0 +1,12 @@ +table.calendar-widget-table td.calendar-day, +table.calendar-widget-table td.show-week {cursor: pointer;} +table.calendar-widget-table td.calendar-day:hover, +table.calendar-widget-table td.show-week:hover {background-color:#ccc;} +table.calendar-widget-table td.calendar-day.selected {background-color:#aaa;} + +table.calendar-widget-table td.out-of-month {color:#999;background-color::#999;} +table.calendar-widget-table td.today {border-bottom: 2px solid rgb(158, 0, 0);} +table.calendar-widget-table td.hasEvent {background-image:url(../images/dot.png);background-position:center;background-repeat: no-repeat;} + +table.calendar-widget-table thead th {text-align:center;} + diff --git a/images/calendar-file.gif b/images/calendar-file.gif new file mode 100755 index 0000000..6c001c6 Binary files /dev/null and b/images/calendar-file.gif differ diff --git a/images/calendar__plus.png b/images/calendar__plus.png new file mode 100755 index 0000000..a859502 Binary files /dev/null and b/images/calendar__plus.png differ diff --git a/images/calendar_widget_bg.gif b/images/calendar_widget_bg.gif new file mode 100755 index 0000000..4a06e13 Binary files /dev/null and b/images/calendar_widget_bg.gif differ diff --git a/images/check.png b/images/check.png new file mode 100755 index 0000000..6157669 Binary files /dev/null and b/images/check.png differ diff --git a/images/dot.png b/images/dot.png new file mode 100755 index 0000000..8dd57c1 Binary files /dev/null and b/images/dot.png differ diff --git a/images/event-file.gif b/images/event-file.gif new file mode 100755 index 0000000..52825da Binary files /dev/null and b/images/event-file.gif differ diff --git a/images/feed.png b/images/feed.png new file mode 100755 index 0000000..e757fd1 Binary files /dev/null and b/images/feed.png differ diff --git a/images/loader.gif b/images/loader.gif new file mode 100644 index 0000000..1af958b Binary files /dev/null and b/images/loader.gif differ diff --git a/javascript/calendar.js b/javascript/calendar.js new file mode 100644 index 0000000..c8fabcd --- /dev/null +++ b/javascript/calendar.js @@ -0,0 +1,19 @@ +(function($) { +$(function() { + $('.calendar-view-more').live("click",function(e) { + e.preventDefault(); + $(this).addClass('loading'); + var $t = $(this); + $.ajax({ + url: $t.attr('href'), + success: function(data) { + $t.remove(); + $('#events').append(data); + } + + }) + }); + +}); + +})(jQuery); \ No newline at end of file diff --git a/javascript/calendar_cms.js b/javascript/calendar_cms.js new file mode 100755 index 0000000..5ad79ef --- /dev/null +++ b/javascript/calendar_cms.js @@ -0,0 +1,99 @@ +(function($) { +$(function() { + + $('#DefaultView select').live("change",function() { + $('#DefaultFutureMonths').hide(); + if($(this).val() == "upcoming") { + $('#DefaultFutureMonths').show(); + } + }).change(); + + + $('#Recursion').livequery(function() { + + + var $tab = $(this).closest('.tab'); + var $recursion = $(this); + var $customRecursionType = $tab.find('#CustomRecursionType').hide(); + var $dailyInterval = $tab.find('.dailyinterval').hide(); + var $weeklyInterval = $tab.find('.weeklyinterval').hide(); + var $monthlyInterval = $tab.find('.monthlyinterval').hide(); + var $monthlyIndex = $tab.find('.monthlyindex').hide(); + var $recurringDaysOfWeek = $tab.find('#RecurringDaysOfWeek').hide(); + var $recurringDaysOfMonth = $tab.find('#RecurringDaysOfMonth').hide(); + var $monthlyRecursionType1 = $tab.find('#MonthlyRecursionType1').hide(); + var $monthlyRecursionType2 = $tab.find('#MonthlyRecursionType2').hide(); + + var resetPanels = function () { + $dailyInterval.hide(); + $weeklyInterval.hide(); + $monthlyInterval.hide(); + $recurringDaysOfWeek.hide(); + $recurringDaysOfMonth.hide().find(':checkbox').attr('disabled', true); + $monthlyRecursionType1.hide(); + $monthlyRecursionType2.hide(); + $monthlyIndex.hide().find('select').attr('disabled', true); + } + + $recursion.find('input').change(function() { + if($(this).is(':checked')) { + $customRecursionType.show(); + } + else { + $tab.find(':checkbox, :radio').attr('checked', false); + $customRecursionType.hide(); + resetPanels(); + } + }).change(); + + $customRecursionType.find('input').change(function() { + if($(this).is(':checked')) { + resetPanels(); + switch($(this).val()) { + case "1": + $dailyInterval.show(); + break; + + case "2": + $weeklyInterval.show(); + $recurringDaysOfWeek.show(); + break; + + case "3": + $monthlyInterval.show(); + $recurringDaysOfMonth.show(); + $monthlyRecursionType1.show(); + $monthlyRecursionType2.show(); + $monthlyIndex.show(); + break; + } + } + }).change(); + + $monthlyRecursionType1.find('input').change(function() { + if($(this).is(':checked')) { + $recurringDaysOfMonth.find(':checkbox').attr('disabled', false); + $monthlyIndex.find('select').attr('disabled', true); + $monthlyRecursionType2.find('input').attr('checked', false).change(); + } + }).change(); + + $monthlyRecursionType2.find('input').change(function() { + if($(this).is(':checked')) { + $recurringDaysOfMonth.find(':checkbox').attr('disabled', true); + $monthlyIndex.find('select').attr('disabled', false); + $monthlyRecursionType1.find('input').attr('checked', false).change(); + } + }).change(); + + + + }); + + + + + + +}); +})(jQuery); \ No newline at end of file diff --git a/javascript/calendar_widget.js b/javascript/calendar_widget.js new file mode 100755 index 0000000..d6b7f1a --- /dev/null +++ b/javascript/calendar_widget.js @@ -0,0 +1,253 @@ +(function($){ + + var CalendarWidget = function() { + this.settings = {}; + if($.CalendarWidget) { + this.settings = $.CalendarWidget.settings; + } + } + + $.extend(CalendarWidget.prototype, { + + attachTo: function(element) { + this.element = element; + }, + + + getContainer: function() { + return this.element; + }, + + + setMonth: function(month, year) { + this.month = month; + this.year = year; + this.firstDay = new Date(this.year, this.month, 1); + this.startingDay = this.firstDay.getDay(); + if($.CalendarWidget.settings.startOnMonday) { + this.startingDay--; + } + this.monthLength = this.settings.calDaysInMonth[this.month]; + // compensate for leap year + if (this.month == 1) { // February only! + if((this.year % 4 == 0 && this.year % 100 != 0) || this.year % 400 == 0){ + this.monthLength = 29; + } + } + + html = this.generateHTML(); + $(this.element).html(html); + + var calendar = this; + + $('.prev', this.element).click(function() { + calendar.previousMonth(); + }); + + + $('.next', this.element).click(function() { + calendar.nextMonth(); + }); + + + $('.calendar-day',this.element).click(function() { + calendar.showDay(this); + }); + + + $('.show-week', this.element).click(function() { + calendar.showWeek(this); + }); + + + $('.show-month',this.element).click(function() { + calendar.showMonth(this); + }); + }, + + + + getPaddedMonth: function() { + return this.pad(this.month+1); + }, + + + + setOptions: function(options) { + $.extend(this.settings, options); + }, + + + + getNextMonthYear: function() { + var m = this.month+1; + var y = this.year; + if(m > 11) { + m = 0; + y++; + } + return [m, y]; + }, + + + + getPrevMonthYear: function() { + var m = this.month-1; + var y = this.year; + if(m < 0) { + m = 11; + y--; + } + return [m, y]; + }, + + + + previousMonth: function() { + result = this.getPrevMonthYear(); + this.setMonth(result[0], result[1]); + this.settings.onMonthChange(result[0]+1, result[1], this); + }, + + + + nextMonth: function() { + result = this.getNextMonthYear(); + this.setMonth(result[0], result[1]); + this.settings.onMonthChange(result[0]+1, result[1], this); + }, + + + + showDay: function(element) { + this.settings.onShowDay($(element).data('date'), this); + }, + + + + showWeek: function(element) { + var start = $(element).closest('tr').find('td').eq(0); + var end = $(element).closest('tr').find('td').eq(6); + this.settings.onShowWeek(start.data('date'), end.data('date'), this); + }, + + + + showMonth: function(element) { + month = this.getPaddedMonth(); + start = this.year + '-' + month + '-01'; + end = this.year + '-' + month + '-' + this.monthLength; + this.settings.onShowMonth(start, end, this); + }, + + + generateHTML: function() { + // do the header + var monthName = this.settings.calMonthsLabels[this.month] + var html = ''; + html += ''; + html += ''; + html += ''; + html += ''; + html += ''; + for(var i = 0; i <= 6; i++ ){ + html += ''; + } + html += "" + html += ''; + + var cell = 1; + var empties = 0; + var row_count = Math.ceil(this.monthLength/7); + for (var i = 0; i < row_count; i++) { + for (var j = 0; j <= 6; j++) { + date = ""; + diff = cell-this.startingDay; + out_of_month = false; + if(diff < 1) { + out_of_month = true; + r = this.getPrevMonthYear(); + m = r[0]; + y = r[1]; + days = this.settings.calDaysInMonth[m]; + day = days + diff; + empties++; + } + else { + day = cell-empties; + m = this.month; + y = this.year; + } + if(!out_of_month && day > this.monthLength) { + out_of_month = true; + r = this.getNextMonthYear(); + m = r[0]; + y = r[1]; + days = this.settings.calDaysInMonth[m]; + day = cell-empties-this.monthLength; + } + today = new Date(); + if(today.getFullYear() == this.year && today.getMonth() == m && today.getDate() == day) { + klass = "today"; + } + else { + klass = out_of_month ? "out-of-month" : "in-month"; + } + d = this.pad(day); + m = this.pad(m+1); + date = y+'-'+m+'-'+d; + html += ''; + cell++; + + } + html += ""; + html += ''; + } + html += '
'; + html += ''+monthName + " " + this.year + ''; + html += '
'; + html += this.settings.calDaysLabels[i]; + html += ' 
'+day+'<<
'; + return html; + }, + + pad: function(num) { + str = new String(num); + return (str.length == 1) ? "0"+str : str; + } + + + }); + + + + $.fn.extend({ + CalendarWidget: function(options) { + return this.each(function() { + var c = new CalendarWidget(); + c.setOptions(options); + c.attachTo(this); + var currentDate = new Date(); + var year = options.year ? options.year : currentDate.getFullYear(); + var month = options.month ? options.month : currentDate.getMonth(); + c.setMonth(month, year); + c.settings.onInit(c); + }); + } + }); + + // Create the singleton for global settings + $.CalendarWidget = new CalendarWidget(); + $.CalendarWidget.setOptions({ + onShowDay: function(date) {}, + onShowWeek: function(start, end) {}, + onShowMonth: function(start, end) {}, + onMonthChange: function(month, year) {}, + onInit: function(calendar) {}, + startOnMonday: false, + calDaysInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], + calDaysLabels: [], + calMonthsLabels: [] + }); + +})( jQuery ); diff --git a/javascript/calendar_widget_init.js b/javascript/calendar_widget_init.js new file mode 100755 index 0000000..91f54b1 --- /dev/null +++ b/javascript/calendar_widget_init.js @@ -0,0 +1,121 @@ +(function($) { +$(function() { + +var calendar_url = $('.calendar-widget').data('url'); +var loaded_months = {}; + +function loadMonthJson(month, year) { + $.ajax({ + url: calendar_url+"monthjson/"+year+month, + async: false, + dataType: 'json', + success: function(data) { + json = data; + loaded_months[year+month] = data; + } + }); +} + + +function applyMonthJson(month, year) { + json = loaded_months[year+month]; + for(date in json) { + if(json[date].events.length) { + $('[data-date="'+date+'"]').addClass('hasEvent').attr('title', json[date].events.join("\n")); + + } + } +} + + +function setSelection(calendar) { + var $e = $(calendar.getContainer()); + if($e.data('start') && $e.data('end')) { + var starts = $e.data('start').split("-"); + var ends = $e.data('end').split("-"); + var startDate = new Date(starts[0], Number(starts[1])-1, Number(starts[2])); + var endDate = new Date(ends[0], Number(ends[1])-1, Number(ends[2])); + var startTime = startDate.getTime(); + var endTime = endDate.getTime()+86400000; + + for(loopTime = startTime; loopTime < endTime; loopTime += 86400000) { + var loopDay=new Date(loopTime) + var stamp = loopDay.getFullYear()+"-"+calendar.pad(loopDay.getMonth()+1)+"-"+calendar.pad(loopDay.getDate()); + $('.calendar-day[data-date="'+stamp+'"]').addClass('selected'); + + } + } + +} + +$('.calendar-widget').each(function() { + var opts = { + onShowDay: function(date) { + window.location = calendar_url+"show/"+date; + }, + onShowWeek: function(start, end) { + window.location = calendar_url+"show/"+start+"/"+end; + }, + onShowMonth: function(start, end) { + window.location = calendar_url+"show/"+start+"/"+end; + }, + + onMonthChange: function(month, year, calendar) { + var json; + m = calendar.pad(month); + if(!loaded_months[year+m]) { + loadMonthJson(m, year); + } + json = loaded_months[year+m]; + applyMonthJson(m, year); + setSelection(calendar); + }, + + onInit: function(calendar) { + previous = calendar.getPrevMonthYear(); + next = calendar.getNextMonthYear(); + + this_month = calendar.getPaddedMonth(); + this_year = calendar.year; + prev_month = calendar.pad(previous[0]+1); + next_month = calendar.pad(next[0]+1); + prev_year = previous[1]; + next_year = next[1]; + + loadMonthJson( + this_month, + this_year + ); + + loadMonthJson( + prev_month, + prev_year + ); + + loadMonthJson( + next_month, + next_year + ); + applyMonthJson(this_month, this_year); + applyMonthJson(prev_month, prev_year); + applyMonthJson(next_month, next_year); + + setSelection(calendar); + + } + + }; + + if($(this).data('start')) { + var parts = $(this).data('start').split('-'); + opts.month = Number(parts[1])-1; + opts.year = parts[0]; + + + } + $(this).CalendarWidget(opts); + +}) + +}); +})(jQuery); \ No newline at end of file diff --git a/javascript/i18n b/javascript/i18n new file mode 160000 index 0000000..a0b35a4 --- /dev/null +++ b/javascript/i18n @@ -0,0 +1 @@ +Subproject commit a0b35a4e0bfe2893f2db9edc0b58a886aadbc9e6 diff --git a/lang/de.yml b/lang/de.yml new file mode 100755 index 0000000..1726e25 --- /dev/null +++ b/lang/de.yml @@ -0,0 +1,126 @@ + +
+
$DateRange
+
+

<% if Announcement %>$Event.Title<% else %>$Event.Title<% end_if %>

+
+ <% if AllDay %> +
<% _t('ALLDAY','All Day') %>
+ <% else %> + <% if StartTime %> +
<% _t('TIME','Time') %>: 
+
$TimeRange
+ <% end_if %> + <% end_if %> +
+
+ <% if Announcement %> + $Content + <% else %> + <% with Event %>$Content.LimitWordCount(60)<% end_with %> <% _t('MORE','more...') %> + <% end_if %> + <% if OtherDates %> +

<% _t('SEEALSO','See also') %>:

+
    + <% loop OtherDates %> +
  • $DateRange + <% if StartTime %> +
      +
    • $TimeRange
    • +
    + <% end_if %> +
  • + <% end_loop %> +
+ <% end_if %> +
+
+ +
+ <% end_loop %> + <% if MoreEvents %> + <% _t('Calendar.VIEWMOREEVENTS','View more...') %> + <% end_if %> diff --git a/templates/Includes/MonthJumper.ss b/templates/Includes/MonthJumper.ss new file mode 100644 index 0000000..267741a --- /dev/null +++ b/templates/Includes/MonthJumper.ss @@ -0,0 +1,4 @@ +

<% _t('Calendar.JUMPTOMONTH','Jump to a Month') %>

+
+ $MonthJumpForm +
diff --git a/templates/Includes/QuickNav.ss b/templates/Includes/QuickNav.ss new file mode 100644 index 0000000..4caf3ac --- /dev/null +++ b/templates/Includes/QuickNav.ss @@ -0,0 +1,18 @@ + + + \ No newline at end of file diff --git a/templates/Layout/Calendar.ss b/templates/Layout/Calendar.ss new file mode 100755 index 0000000..b246214 --- /dev/null +++ b/templates/Layout/Calendar.ss @@ -0,0 +1,28 @@ +
+ +
+ <% if DateHeader %> +

$DateHeader

+ <% end_if %> +
+ +$CalendarWidget + + +$MonthJumper + +<% include QuickNav %> + + +<% if Events %> +
+ <% include EventList %> +
+<% else %> + <% _t('NOEVENTS','There are no events') %>. +<% end_if %> +
\ No newline at end of file diff --git a/templates/Layout/CalendarEvent.ss b/templates/Layout/CalendarEvent.ss new file mode 100755 index 0000000..ab3ddc3 --- /dev/null +++ b/templates/Layout/CalendarEvent.ss @@ -0,0 +1,39 @@ +
+ +
+

$Title

+
+ <% if Level(2) %> + <% include BreadCrumbs %> + <% end_if %> +
+ <% if OtherDates %> +
+

<% _t('CalendarEvent.ADDITIONALDATES','Additional Dates') %>

+
+ <% loop OtherDates %> +
$DateRange
+ <% end_loop %> +
+
+ <% end_if %> +

$Title

+ + <% with CurrentDate %> +

add: $DateRange

+ <% if StartTime %> +
    +
  • $TimeRange
  • +
+ <% end_if %> + <% end_with %> + + $Content + $Form + $PageComments +
+
\ No newline at end of file diff --git a/templates/ics.ss b/templates/ics.ss new file mode 100755 index 0000000..a20cf98 --- /dev/null +++ b/templates/ics.ss @@ -0,0 +1,12 @@ +BEGIN:VCALENDAR +PRODID:-//{$HOST}//{$LANGUAGE} +CALSCALE:{$CALSCALE} +VERSION:2.0 +METHOD:PUBLISH +BEGIN:VEVENT +DTSTART;TZID=$TIMEZONE:$START_TIMESTAMP +DTEND;TZID=$TIMEZONE:$END_TIMESTAMP +URL:$URL +SUMMARY;CHARSET=UTF-8:$TITLE +END:VEVENT +END:VCALENDAR \ No newline at end of file