Merge branch 'MDL-10971_cloze_shuffle_fix' of git://github.com/timhunt/moodle
[moodle.git] / availability / condition / date / classes / condition.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Date condition.
19  *
20  * @package availability_date
21  * @copyright 2014 The Open University
22  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 namespace availability_date;
27 defined('MOODLE_INTERNAL') || die();
29 /**
30  * Date condition.
31  *
32  * @package availability_date
33  * @copyright 2014 The Open University
34  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
35  */
36 class condition extends \core_availability\condition {
37     /** @var string Availabile only from specified date. */
38     const DIRECTION_FROM = '>=';
40     /** @var string Availabile only until specified date. */
41     const DIRECTION_UNTIL = '<';
43     /** @var string One of the DIRECTION_xx constants. */
44     private $direction;
46     /** @var int Time (Unix epoch seconds) for condition. */
47     private $time;
49     /** @var int Forced current time (for unit tests) or 0 for normal. */
50     private static $forcecurrenttime = 0;
52     /**
53      * Constructor.
54      *
55      * @param \stdClass $structure Data structure from JSON decode
56      * @throws \coding_exception If invalid data structure.
57      */
58     public function __construct($structure) {
59         // Get direction.
60         if (isset($structure->d) && in_array($structure->d,
61                 array(self::DIRECTION_FROM, self::DIRECTION_UNTIL))) {
62             $this->direction = $structure->d;
63         } else {
64             throw new \coding_exception('Missing or invalid ->d for date condition');
65         }
67         // Get time.
68         if (isset($structure->t) && is_int($structure->t)) {
69             $this->time = $structure->t;
70         } else {
71             throw new \coding_exception('Missing or invalid ->t for date condition');
72         }
73     }
75     public function save() {
76         return (object)array('type' => 'date',
77                 'd' => $this->direction, 't' => $this->time);
78     }
80     public function is_available($not, \core_availability\info $info, $grabthelot, $userid) {
81         return $this->is_available_for_all($not);
82     }
84     public function is_available_for_all($not = false) {
85         // Check condition.
86         $now = self::get_time();
87         switch ($this->direction) {
88             case self::DIRECTION_FROM:
89                 $allow = $now >= $this->time;
90                 break;
91             case self::DIRECTION_UNTIL:
92                 $allow = $now < $this->time;
93                 break;
94             default:
95                 throw new \coding_exception('Unexpected direction');
96         }
97         if ($not) {
98             $allow = !$allow;
99         }
101         return $allow;
102     }
104     /**
105      * Obtains the actual direction of checking based on the $not value.
106      *
107      * @param bool $not True if condition is negated
108      * @return string Direction constant
109      * @throws \coding_exception
110      */
111     protected function get_logical_direction($not) {
112         switch ($this->direction) {
113             case self::DIRECTION_FROM:
114                 return $not ? self::DIRECTION_UNTIL : self::DIRECTION_FROM;
115             case self::DIRECTION_UNTIL:
116                 return $not ? self::DIRECTION_FROM : self::DIRECTION_UNTIL;
117             default:
118                 throw new \coding_exception('Unexpected direction');
119         }
120     }
122     public function get_description($full, $not, \core_availability\info $info) {
123         return $this->get_either_description($not, false);
124     }
126     public function get_standalone_description(
127             $full, $not, \core_availability\info $info) {
128         return $this->get_either_description($not, true);
129     }
131     /**
132      * Shows the description using the different lang strings for the standalone
133      * version or the full one.
134      *
135      * @param bool $not True if NOT is in force
136      * @param bool $standalone True to use standalone lang strings
137      */
138     protected function get_either_description($not, $standalone) {
139         $direction = $this->get_logical_direction($not);
140         $midnight = self::is_midnight($this->time);
141         $midnighttag = $midnight ? '_date' : '';
142         $satag = $standalone ? 'short_' : 'full_';
143         switch ($direction) {
144             case self::DIRECTION_FROM:
145                 return get_string($satag . 'from' . $midnighttag, 'availability_date',
146                         self::show_time($this->time, $midnight, false));
147             case self::DIRECTION_UNTIL:
148                 return get_string($satag . 'until' . $midnighttag, 'availability_date',
149                         self::show_time($this->time, $midnight, true));
150         }
151     }
153     protected function get_debug_string() {
154         return $this->direction . ' ' . gmdate('Y-m-d H:i:s', $this->time);
155     }
157     /**
158      * Gets time. This function is implemented here rather than calling time()
159      * so that it can be overridden in unit tests. (Would really be nice if
160      * Moodle had a generic way of doing that, but it doesn't.)
161      *
162      * @return int Current time (seconds since epoch)
163      */
164     protected static function get_time() {
165         if (self::$forcecurrenttime) {
166             return self::$forcecurrenttime;
167         } else {
168             return time();
169         }
170     }
172     /**
173      * Forces the current time for unit tests.
174      *
175      * @param int $forcetime Time to return from the get_time function
176      */
177     public static function set_current_time_for_test($forcetime = 0) {
178         self::$forcecurrenttime = $forcetime;
179     }
181     /**
182      * Shows a time either as a date or a full date and time, according to
183      * user's timezone.
184      *
185      * @param int $time Time
186      * @param bool $dateonly If true, uses date only
187      * @param bool $until If true, and if using date only, shows previous date
188      * @return string Date
189      */
190     protected function show_time($time, $dateonly, $until = false) {
191         // For 'until' dates that are at midnight, e.g. midnight 5 March, it
192         // is better to word the text as 'until end 4 March'.
193         $daybefore = false;
194         if ($until && $dateonly) {
195             $daybefore = true;
196             $time = strtotime('-1 day', $time);
197         }
198         return userdate($time,
199                 get_string($dateonly ? 'strftimedate' : 'strftimedatetime', 'langconfig'));
200     }
202     /**
203      * Checks whether a given time refers exactly to midnight (in current user
204      * timezone).
205      *
206      * @param int $time Time
207      * @return bool True if time refers to midnight, false otherwise
208      */
209     protected static function is_midnight($time) {
210         return usergetmidnight($time) == $time;
211     }
213     public function update_after_restore(
214             $restoreid, $courseid, \base_logger $logger, $name) {
215         // Update the date, if restoring with changed date.
216         $dateoffset = \core_availability\info::get_restore_date_offset($restoreid);
217         if ($dateoffset) {
218             $this->time += $dateoffset;
219             return true;
220         }
221         return false;
222     }