MDL-18375 calendar: huge refactor of the initial patch
[moodle.git] / calendar / type / calendartype.class.php
CommitLineData
2f00e1b2
MN
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
18/**
19 * Defines functions used by calendar type plugins.
20 *
21 * This library provides a unified interface for calendar types.
22 *
23 * @package core_calendar
24 * @author Shamim Rezaie <support@foodle.org>
25 * @author Mark Nelson <markn@moodle.com>
26 * @copyright 2008 onwards Foodle Group {@link http://foodle.org}
27 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 */
29abstract class calendar_type_plugin_base {
30
31 /**
32 * Returns a list of all the possible days for all months.
33 *
34 * This is used to generate the select box for the days
35 * in the date selector elements. Some months contain more days
36 * than others so this function should return all possible days as
37 * we can not predict what month will be chosen (the user
38 * may have JS turned off and we need to support this situation in
39 * Moodle).
40 *
41 * @return array the days
42 */
43 public abstract function get_days();
44
45 /**
46 * Returns a list of all the names of the months.
47 *
48 * @return array the month names
49 */
50 public abstract function get_months();
51
52 /**
53 * Returns the minimum year of the calendar.
54 *
55 * @return int the minumum year
56 */
57 public abstract function get_min_year();
58
59 /**
60 * Returns the maximum year of the calendar.
61 *
62 * @return int the max year
63 */
64 public abstract function get_max_year();
65
66 /**
67 * Provided with a day, month, year, hour and minute in the specific
68 * calendar type convert it into the equivalent Gregorian date.
69 *
70 * @param int $year
71 * @param int $month
72 * @param int $day
73 * @param int $hour
74 * @param int $minute
75 * @return array the converted day, month and year.
76 */
77 public abstract function convert_to_gregorian($year, $month, $day, $hour = 0, $minute = 0);
78
79 /**
80 * Provided with a day, month, year, hour and minute in a Gregorian date
81 * convert it into the specific calendar type date.
82 *
83 * @param int $year
84 * @param int $month
85 * @param int $day
86 * @param int $hour
87 * @param int $minute
88 * @return array the converted day, month and year.
89 */
90 public abstract function convert_from_gregorian($year, $month, $day, $hour = 0, $minute = 0);
91
92 /**
93 * Returns a formatted string that represents a date in user time.
94 *
95 * Returns a formatted string that represents a date in user time
96 * <b>WARNING: note that the format is for strftime(), not date().</b>
97 * Because of a bug in most Windows time libraries, we can't use
98 * the nicer %e, so we have to use %d which has leading zeroes.
99 * A lot of the fuss in the function is just getting rid of these leading
100 * zeroes as efficiently as possible.
101 *
102 * If parameter fixday = true (default), then take off leading
103 * zero from %d, else maintain it.
104 *
105 * @param int $date the timestamp in UTC, as obtained from the database.
106 * @param string $format strftime format. You should probably get this using
107 * get_string('strftime...', 'langconfig');
108 * @param int|float|string $timezone by default, uses the user's time zone. if numeric and
109 * not 99 then daylight saving will not be added.
110 * {@link http://docs.moodle.org/dev/Time_API#Timezone}
111 * @param bool $fixday if true (default) then the leading zero from %d is removed.
112 * If false then the leading zero is maintained.
113 * @param bool $fixhour if true (default) then the leading zero from %I is removed.
114 * @return string the formatted date/time.
115 */
116 function userdate($date, $format, $timezone, $fixday, $fixhour) {
117 global $CFG;
118
119 if (empty($format)) {
120 $format = get_string('strftimedaydatetime', 'langconfig');
121 }
122
123 if (!empty($CFG->nofixday)) { // Config.php can force %d not to be fixed.
124 $fixday = false;
125 } else if ($fixday) {
126 $formatnoday = str_replace('%d', 'DD', $format);
127 $fixday = ($formatnoday != $format);
128 $format = $formatnoday;
129 }
130
131 // Note: This logic about fixing 12-hour time to remove unnecessary leading
132 // zero is required because on Windows, PHP strftime function does not
133 // support the correct 'hour without leading zero' parameter (%l).
134 if (!empty($CFG->nofixhour)) {
135 // Config.php can force %I not to be fixed.
136 $fixhour = false;
137 } else if ($fixhour) {
138 $formatnohour = str_replace('%I', 'HH', $format);
139 $fixhour = ($formatnohour != $format);
140 $format = $formatnohour;
141 }
142
143 // Add daylight saving offset for string timezones only, as we can't get dst for
144 // float values. if timezone is 99 (user default timezone), then try update dst.
145 if ((99 == $timezone) || !is_numeric($timezone)) {
146 $date += dst_offset_on($date, $timezone);
147 }
148
149 $timezone = get_user_timezone_offset($timezone);
150
151 // If we are running under Windows convert to windows encoding and then back to UTF-8
152 // (because it's impossible to specify UTF-8 to fetch locale info in Win32).
153 if (abs($timezone) > 13) { // Server time.
154 $datestring = date_format_string($date, $format, $timezone);
155 if ($fixday) {
156 $daystring = ltrim(str_replace(array(' 0', ' '), '', strftime(' %d', $date)));
157 $datestring = str_replace('DD', $daystring, $datestring);
158 }
159 if ($fixhour) {
160 $hourstring = ltrim(str_replace(array(' 0', ' '), '', strftime(' %I', $date)));
161 $datestring = str_replace('HH', $hourstring, $datestring);
162 }
163 } else {
164 $date += (int)($timezone * 3600);
165 $datestring = date_format_string($date, $format, $timezone);
166 if ($fixday) {
167 $daystring = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %d', $date)));
168 $datestring = str_replace('DD', $daystring, $datestring);
169 }
170 if ($fixhour) {
171 $hourstring = ltrim(str_replace(array(' 0', ' '), '', gmstrftime(' %I', $date)));
172 $datestring = str_replace('HH', $hourstring, $datestring);
173 }
174 }
175
176 return $datestring;
177 }
178
179 /**
180 * Given a $time timestamp in GMT (seconds since epoch), returns an array that
181 * represents the date in user time.
182 *
183 * @param int $time Timestamp in GMT
184 * @param float|int|string $timezone offset's time with timezone, if float and not 99, then no
185 * dst offset is applyed {@link http://docs.moodle.org/dev/Time_API#Timezone}
186 * @return array An array that represents the date in user time
187 */
188 function usergetdate($time, $timezone) {
189 // Save input timezone, required for dst offset check.
190 $passedtimezone = $timezone;
191
192 $timezone = get_user_timezone_offset($timezone);
193
194 if (abs($timezone) > 13) { // Server time.
195 return getdate($time);
196 }
197
198 // Add daylight saving offset for string timezones only, as we can't get dst for
199 // float values. if timezone is 99 (user default timezone), then try update dst.
200 if ($passedtimezone == 99 || !is_numeric($passedtimezone)) {
201 $time += dst_offset_on($time, $passedtimezone);
202 }
203
204 $time += intval((float)$timezone * HOURSECS);
205
206 $datestring = gmstrftime('%B_%A_%j_%Y_%m_%w_%d_%H_%M_%S', $time);
207
208 // Be careful to ensure the returned array matches that produced by getdate() above.
209 list (
210 $getdate['month'],
211 $getdate['weekday'],
212 $getdate['yday'],
213 $getdate['year'],
214 $getdate['mon'],
215 $getdate['wday'],
216 $getdate['mday'],
217 $getdate['hours'],
218 $getdate['minutes'],
219 $getdate['seconds']
220 ) = explode('_', $datestring);
221
222 // Set correct datatype to match with getdate().
223 $getdate['seconds'] = (int) $getdate['seconds'];
224 $getdate['yday'] = (int) $getdate['yday'] - 1;
225 $getdate['year'] = (int) $getdate['year'];
226 $getdate['mon'] = (int) $getdate['mon'];
227 $getdate['wday'] = (int) $getdate['wday'];
228 $getdate['mday'] = (int) $getdate['mday'];
229 $getdate['hours'] = (int) $getdate['hours'];
230 $getdate['minutes'] = (int) $getdate['minutes'];
231
232 return $getdate;
233 }
234}
235
236/**
237 * Class calendar_type_plugin_factory.
238 *
239 * Factory class producing required subclasses of {@link calendar_type_plugin_base}.
240 */
241class calendar_type_plugin_factory {
242
243 /**
244 * Returns an instance of the currently used calendar type.
245 *
246 * @return calendar_type_plugin_* the created calendar_type class
247 * @throws coding_exception if the calendar type file could not be loaded
248 */
249 static function factory() {
250 global $CFG;
251
252 $type = self::get_calendar_type();
253 $file = 'calendar/type/' . $type . '/lib.php';
254 $fullpath = $CFG->dirroot . '/' . $file;
255 if (is_readable($fullpath)) {
256 require_once($fullpath);
257 $class = "calendar_type_plugin_$type";
258 return new $class();
259 } else {
260 throw new coding_exception("The calendar type file $file could not be initialised, check that it exists
261 and that the web server has permission to read it.");
262 }
263 }
264
265 /**
266 * Returns a list of calendar typess available for use.
267 *
268 * @return array the list of calendar types
269 */
270 static function get_list_of_calendar_types() {
271 $calendars = array();
272 $calendardirs = core_component::get_plugin_list('calendartype');
273
274 foreach ($calendardirs as $name => $location) {
275 $calendars[$name] = get_string('name', "calendartype_{$name}");
276 }
277
278 return $calendars;
279 }
280
281 /**
282 * Returns the current calendar type in use.
283 *
284 * @return string the current calendar type being used
285 */
286 static function get_calendar_type() {
287 global $CFG, $USER, $SESSION, $COURSE;
288
289 if (!empty($COURSE->id) and $COURSE->id != SITEID and !empty($COURSE->calendartype)) { // Course calendartype can override all other settings for this page.
290 $return = $COURSE->calendartype;
291 } else if (!empty($SESSION->calendartype)) { // Session calendartype can override other settings.
292 $return = $SESSION->calendartype;
293 } else if (!empty($USER->calendartype)) {
294 $return = $USER->calendartype;
295 } else if (!empty($CFG->calendartype)) {
296 $return = $CFG->calendartype;
297 } else {
298 $return = 'gregorian';
299 }
300
301 return $return;
302 }
303}
304