Added a couple of clarifying comments.
[timetracker.git] / WEB-INF / lib / form / Calendar.class.php
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Anuko Time Tracker
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) Anuko International Ltd. (https://www.anuko.com)
6 // +----------------------------------------------------------------------+
7 // | LIBERAL FREEWARE LICENSE: This source code document may be used
8 // | by anyone for any purpose, and freely redistributed alone or in
9 // | combination with other software, provided that the license is obeyed.
10 // |
11 // | There are only two ways to violate the license:
12 // |
13 // | 1. To redistribute this code in source form, with the copyright
14 // |    notice or license removed or altered. (Distributing in compiled
15 // |    forms without embedded copyright notices is permitted).
16 // |
17 // | 2. To redistribute modified versions of this code in *any* form
18 // |    that bears insufficient indications that the modifications are
19 // |    not the work of the original author(s).
20 // |
21 // | This license applies to this document only, not any other software
22 // | that it may be combined with.
23 // |
24 // +----------------------------------------------------------------------+
25 // | Contributors:
26 // | https://www.anuko.com/time_tracker/credits.htm
27 // +----------------------------------------------------------------------+
28
29 import('form.FormElement');
30 import('DateAndTime');
31
32 class Calendar extends FormElement {
33   var $holidays = array();
34   var $showHolidays = true;
35   var $weekStartDay = 0;
36   
37     var $mHeader = "padding: 5px; font-size: 8pt; color: #333333; background-color: #d9d9d9;";
38     var $mDayCell = "padding: 5px; border: 1px solid silver; font-size: 8pt; color: #333333; background-color: #ffffff;";
39     var $mDaySelected = "padding: 5px; border: 1px solid silver; font-size: 8pt; color: #666666; background-color: #a6ccf7;";
40     var $mDayWeekend = "padding: 5px; border: 1px solid silver; font-size: 8pt; color: #666666; background-color: #f7f7f7;";
41     var $mDayHoliday = "padding: 5px; border: 1px solid silver; font-size: 8pt; color: #666666; background-color: #f7f7f7;";
42     var $mDayHeader = "padding: 5px; border: 1px solid white; font-size: 8pt; color: #333333;";
43     var $mDayHeaderWeekend = "padding: 5px; border: 1px solid white; font-size: 8pt; color: #999999;";
44
45     var $controlName = "";
46     var $highlight = "time"; // Determines what type of active days to highlight ("time" or "expenses"). 
47
48     function __construct($name) {
49       $this->class = 'Calendar';
50       $this->controlName = $name; // TODO: why controlName? Other classes have "name".
51       $this->mMonthNames = array('January','February','March','April','May','June','July','August','September','October','November','December');
52       $this->mWeekDayShortNames = array('Su','Mo','Tu','We','Th','Fr','Sa');
53     }
54
55     function setHighlight($highlight) {
56         if ($highlight && $highlight != 'time')
57           $this->highlight = $highlight;
58     }
59
60     function localize() {
61       global $user;
62       global $i18n;
63       
64       $this->mMonthNames = $i18n->monthNames;
65       $this->mWeekDayShortNames = $i18n->weekdayShortNames;
66       $this->weekStartDay = $user->getWeekStart();
67     }
68
69     function setStyle($style) { $this->style = $style; }
70     function setCellStyle($style) { $this->mCellStyle = $style; }
71     function setACellStyle($style) { $this->mACellStyle = $style; }
72     function setLinkStyle($style) { $this->mLinkStyle = $style; }
73
74     function setShowHolidays($value) {
75       $this->showHolidays = $value;
76     }
77
78     /**
79      * @return void
80      * @param date
81      * @desc Enter description here...
82      */
83     function toString($date="") {
84       global $i18n;
85         
86       $indate = $this->value;
87       if (!$indate) $indate = strftime(DB_DATEFORMAT);
88
89       //current year and month
90       if ( strlen ( $indate ) > 0 ) {
91         $indateObj = new DateAndTime(DB_DATEFORMAT, $indate);
92         $thismonth = $indateObj->getMonth();
93         $thisyear = $indateObj->getYear();
94       } else {
95         $thismonth = date("m");
96         $thisyear = date("Y");
97       }
98
99       // next date, month, year
100       $next = mktime ( 2, 0, 0, $thismonth + 1, 1, $thisyear );
101       $nextyear = date ( "Y", $next );
102       $nextmonth = date ( "m", $next );
103       $nextdate = strftime (DB_DATEFORMAT, $next );
104
105       // prev date, month, year
106       $prev = mktime ( 2, 0, 0, $thismonth - 1, 1, $thisyear );
107       $prevyear = date ( "Y", $prev );
108       $prevmonth = date ( "m", $prev );
109       $prevdate = strftime(DB_DATEFORMAT, $prev );
110
111       $str = $this->_genStyles();
112
113       $str .= '<table cellpadding="0" cellspacing="0" border="0" width="100%">
114           <tr><td align="center"><div class="CalendarHeader">'.
115           //'<a href="?date='.$prevyear.'">&lt;&lt;</a> '.
116           '<a href="?date='.$prevdate.'" tabindex="-1">&lt;&lt;&lt;</a>  '.
117           $this->mMonthNames[$thismonth-1].'&nbsp;'.$thisyear.
118           '  <a href="?date='.$nextdate.'" tabindex="-1">&gt;&gt;&gt;</a>'.
119           //' <a href="?date='.$nextyear.'">&gt;&gt;</a>'.
120           '</div></td></tr>
121           </table>';
122
123       $str .= '<center>
124           <table border="0" cellpadding="1" cellspacing="1" width="100%">
125           <tr>';
126
127       $str .= "<tr>";
128
129       // TODO: refactor this entire class, as $weekend_start and $weekend_end
130       // are not what their names suggest (debug with non zero week start to see it).
131       $weekend_start = 6 - $this->weekStartDay;      // Saturday by default.
132       $weekend_end = (7 - $this->weekStartDay) % 7;  // Sunday by default.
133       if (defined('WEEKEND_START_DAY')) {
134         $weekend_start = (7 + WEEKEND_START_DAY - $this->weekStartDay) % 7;
135         $weekend_end = (7 + WEEKEND_START_DAY + 1 - $this->weekStartDay) % 7;
136       } 
137
138       for ( $i=0; $i<7; $i++ ) {
139         $weekdayNameIdx = ($i + $this->weekStartDay) % 7;
140         if ($i==$weekend_start || $i==$weekend_end) {
141           $str .= '<td class="CalendarDayHeaderWeekend">'.$this->mWeekDayShortNames[$weekdayNameIdx].'</td>';
142         } else {
143           $str .= '<td class="CalendarDayHeader">'.$this->mWeekDayShortNames[$weekdayNameIdx].'</td>';
144         }
145       }
146
147       $str .= "</tr>\n";
148
149       list($wkstart,$monthstart,$monthend,$start_date) = $this->_getWeekDayBefore( $thisyear, $thismonth );
150
151       $active_dates = $this->_getActiveDates($monthstart, $monthend);
152
153       for ( $i = $wkstart; $i<=$monthend;  $i=mktime(0,0,0,$thismonth,$start_date+=7,$thisyear) ) {
154         $str .= "<TR>\n";
155           for ( $j = 0; $j < 7; $j++ ) {
156             $date = mktime(0,0,0,$thismonth,$start_date+$j,$thisyear);
157             if (($date >= $monthstart) && ($date <= $monthend)) {
158
159             $stl_cell = "";
160             $stl_link = "";
161
162             // weekend
163             if ($j==$weekend_start || $j==$weekend_end) {
164               $stl_cell = ' class="CalendarDayWeekend"';
165               $stl_link = ' class="CalendarLinkWeekend"';
166             } else {
167               $stl_cell = ' class="CalendarDay"';
168             }
169
170             // holidays
171             $date_to_check = ttTimeHelper::dateInDatabaseFormat($thisyear, $thismonth, $start_date+$j);
172             if (ttTimeHelper::isHoliday($date_to_check)) {
173               $stl_cell = ' class="CalendarDayHoliday"';
174               $stl_link = ' class="CalendarLinkHoliday"';
175             }
176
177             // selected day
178             if ( $indate == strftime(DB_DATEFORMAT, $date))
179               $stl_cell = ' class="CalendarDaySelected"';
180
181
182             $str .= '<td'.$stl_cell.'>';
183
184             // Entries exist.
185             if($active_dates) {
186               if( in_array(strftime(DB_DATEFORMAT, $date), $active_dates) )
187                 $stl_link = ' class="CalendarLinkRecordsExist"';
188             }
189
190             $str .= "<a".$stl_link." href=\"?".$this->controlName."=".strftime(DB_DATEFORMAT, $date)."\" tabindex=\"-1\">".date("d",$date)."</a>";
191
192             $str .= "</TD>";
193           }
194           else {
195             $str .= "<TD>&nbsp;</TD>\n";
196           }
197         }
198         $str .= "</TR>\n";
199       }
200
201       $str .= "<tr><td colspan=\"7\" align=\"center\"><a id=\"today_link\" href=\"?".$this->controlName."=".strftime(DB_DATEFORMAT)."\" tabindex=\"-1\">".$i18n->get('label.today')."</a></td></tr>\n";
202       $str .= "</table>\n";
203
204       $str .= "<input type=\"hidden\" name=\"$this->controlName\" value=\"$indate\">\n";
205
206       // Add script to adjust today link to match browser today, as PHP may run in a different timezone.
207       $str .= "<script>\n";
208       $str .= "function adjustToday() {\n";
209       $str .= "  var browser_today = new Date();\n";
210       $str .= "  document.getElementById('today_link').href = '?$this->controlName='+browser_today.strftime('".DB_DATEFORMAT."');\n";
211       $str .= "}\n";
212       $str .= "adjustToday();\n";
213       $str .= "</script>\n";
214       
215       return $str;
216     }
217
218     function getHtml() {
219         return $this->toString();
220     }
221
222     function _getWeekDayBefore($year, $month) {
223       $weekday = date ( "w", mktime ( 2, 0, 0, $month, 1 - $this->weekStartDay, $year ) );
224       return array(
225         mktime ( 0, 0, 0, $month, 1 - $weekday, $year ),
226         mktime ( 0, 0, 0, $month, 1, $year ),
227       mktime ( 0, 0, 0, $month + 1, 0, $year ),
228       (1 - $weekday)
229       );
230     }
231
232     function _genStyles() {
233       $str = "<style>\n";
234       $str .= ".CalendarHeader {". $this->mHeader ."}\n";
235       $str .= ".CalendarDay {". $this->mDayCell  ."}\n";
236       $str .= ".CalendarDaySelected {". $this->mDaySelected  ."}\n";
237       $str .= ".CalendarDayWeekend {". $this->mDayWeekend ."}\n";
238       $str .= ".CalendarDayHoliday {". $this->mDayHoliday ."}\n";
239       $str .= ".CalendarDayHeader {". $this->mDayHeader ."}\n";
240       $str .= ".CalendarDayHeaderWeekend {". $this->mDayHeaderWeekend ."}\n";
241       
242       $str .= ".CalendarLinkWeekend {color: #999999;}\n";
243       $str .= ".CalendarLinkHoliday {color: #999999;}\n";
244       $str .= ".CalendarLinkRecordsExist {color: #FF0000;}\n";
245         $str .= "</style>\n";
246         return $str;
247     }
248     
249     // _getActiveDates returns an array of dates, for which entries exist for user.
250     // Type of entries (time or expenses) is determined by $this->highlight value.
251     function _getActiveDates($start, $end) {
252       
253       global $user;
254       $user_id = $user->getUser();
255       
256       $table = ($this->highlight == 'expenses') ? 'tt_expense_items' : 'tt_log';
257       
258       $mdb2 = getConnection();
259
260       $start_date = date("Y-m-d", $start);
261       $end_date = date("Y-m-d", $end);
262       $sql = "SELECT date FROM $table WHERE date >= '$start_date' AND date <= '$end_date' AND user_id = $user_id AND status = 1";
263       $res = $mdb2->query($sql);
264       if (!is_a($res, 'PEAR_Error')) {
265         while ($row = $res->fetchRow()) {
266           $out[] = date('Y-m-d', strtotime($row['date']));
267         }
268         return @$out;
269       }
270       else
271         return false;
272     }
273 }