Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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/>.
  16  
  17  /**
  18   * Contains event class for displaying the month view.
  19   *
  20   * @package   core_calendar
  21   * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  namespace core_calendar\external;
  26  
  27  defined('MOODLE_INTERNAL') || die();
  28  
  29  use core\external\exporter;
  30  use renderer_base;
  31  use moodle_url;
  32  
  33  /**
  34   * Class for displaying the month view.
  35   *
  36   * @package   core_calendar
  37   * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
  38   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class month_exporter extends exporter {
  41  
  42      /**
  43       * @var \calendar_information $calendar The calendar to be rendered.
  44       */
  45      protected $calendar;
  46  
  47      /**
  48       * @var int $firstdayofweek The first day of the week.
  49       */
  50      protected $firstdayofweek;
  51  
  52      /**
  53       * @var moodle_url $url The URL for the events page.
  54       */
  55      protected $url;
  56  
  57      /**
  58       * @var bool $includenavigation Whether navigation should be included on the output.
  59       */
  60      protected $includenavigation = true;
  61  
  62      /**
  63       * @var bool $initialeventsloaded Whether the events have been loaded for this month.
  64       */
  65      protected $initialeventsloaded = true;
  66  
  67      /**
  68       * @var bool $showcoursefilter Whether to render the course filter selector as well.
  69       */
  70      protected $showcoursefilter = false;
  71  
  72      /**
  73       * Constructor for month_exporter.
  74       *
  75       * @param \calendar_information $calendar The calendar being represented
  76       * @param \core_calendar\type_base $type The calendar type (e.g. Gregorian)
  77       * @param array $related The related information
  78       */
  79      public function __construct(\calendar_information $calendar, \core_calendar\type_base $type, $related) {
  80          $this->calendar = $calendar;
  81          $this->firstdayofweek = $type->get_starting_weekday();
  82  
  83          $this->url = new moodle_url('/calendar/view.php', [
  84                  'view' => 'month',
  85                  'time' => $calendar->time,
  86              ]);
  87  
  88          if ($this->calendar->course && SITEID !== $this->calendar->course->id) {
  89              $this->url->param('course', $this->calendar->course->id);
  90          } else if ($this->calendar->categoryid) {
  91              $this->url->param('category', $this->calendar->categoryid);
  92          }
  93  
  94          $related['type'] = $type;
  95  
  96          $data = [
  97              'url' => $this->url->out(false),
  98          ];
  99  
 100          parent::__construct($data, $related);
 101      }
 102  
 103      protected static function define_properties() {
 104          return [
 105              'url' => [
 106                  'type' => PARAM_URL,
 107              ],
 108          ];
 109      }
 110  
 111      /**
 112       * Return the list of additional properties.
 113       *
 114       * @return array
 115       */
 116      protected static function define_other_properties() {
 117          return [
 118              'courseid' => [
 119                  'type' => PARAM_INT,
 120              ],
 121              'categoryid' => [
 122                  'type' => PARAM_INT,
 123                  'optional' => true,
 124                  'default' => 0,
 125              ],
 126              'filter_selector' => [
 127                  'type' => PARAM_RAW,
 128                  'optional' => true,
 129              ],
 130              'weeks' => [
 131                  'type' => week_exporter::read_properties_definition(),
 132                  'multiple' => true,
 133              ],
 134              'daynames' => [
 135                  'type' => day_name_exporter::read_properties_definition(),
 136                  'multiple' => true,
 137              ],
 138              'view' => [
 139                  'type' => PARAM_ALPHA,
 140              ],
 141              'date' => [
 142                  'type' => date_exporter::read_properties_definition(),
 143              ],
 144              'periodname' => [
 145                  // Note: We must use RAW here because the calendar type returns the formatted month name based on a
 146                  // calendar format.
 147                  'type' => PARAM_RAW,
 148              ],
 149              'includenavigation' => [
 150                  'type' => PARAM_BOOL,
 151                  'default' => true,
 152              ],
 153              // Tracks whether the first set of events have been loaded and provided
 154              // to the exporter.
 155              'initialeventsloaded' => [
 156                  'type' => PARAM_BOOL,
 157                  'default' => true,
 158              ],
 159              'previousperiod' => [
 160                  'type' => date_exporter::read_properties_definition(),
 161              ],
 162              'previousperiodlink' => [
 163                  'type' => PARAM_URL,
 164              ],
 165              'previousperiodname' => [
 166                  // Note: We must use RAW here because the calendar type returns the formatted month name based on a
 167                  // calendar format.
 168                  'type' => PARAM_RAW,
 169              ],
 170              'nextperiod' => [
 171                  'type' => date_exporter::read_properties_definition(),
 172              ],
 173              'nextperiodname' => [
 174                  // Note: We must use RAW here because the calendar type returns the formatted month name based on a
 175                  // calendar format.
 176                  'type' => PARAM_RAW,
 177              ],
 178              'nextperiodlink' => [
 179                  'type' => PARAM_URL,
 180              ],
 181              'larrow' => [
 182                  // The left arrow defined by the theme.
 183                  'type' => PARAM_RAW,
 184              ],
 185              'rarrow' => [
 186                  // The right arrow defined by the theme.
 187                  'type' => PARAM_RAW,
 188              ],
 189              'defaulteventcontext' => [
 190                  'type' => PARAM_INT,
 191                  'default' => 0,
 192              ],
 193          ];
 194      }
 195  
 196      /**
 197       * Get the additional values to inject while exporting.
 198       *
 199       * @param renderer_base $output The renderer.
 200       * @return array Keys are the property names, values are their values.
 201       */
 202      protected function get_other_values(renderer_base $output) {
 203          $previousperiod = $this->get_previous_month_data();
 204          $nextperiod = $this->get_next_month_data();
 205          $date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
 206  
 207          $nextperiodlink = new moodle_url($this->url);
 208          $nextperiodlink->param('time', $nextperiod[0]);
 209  
 210          $previousperiodlink = new moodle_url($this->url);
 211          $previousperiodlink->param('time', $previousperiod[0]);
 212  
 213          $return = [
 214              'courseid' => $this->calendar->courseid,
 215              'weeks' => $this->get_weeks($output),
 216              'daynames' => $this->get_day_names($output),
 217              'view' => 'month',
 218              'date' => (new date_exporter($date))->export($output),
 219              'periodname' => userdate($this->calendar->time, get_string('strftimemonthyear')),
 220              'previousperiod' => (new date_exporter($previousperiod))->export($output),
 221              'previousperiodname' => userdate($previousperiod[0], get_string('strftimemonthyear')),
 222              'previousperiodlink' => $previousperiodlink->out(false),
 223              'nextperiod' => (new date_exporter($nextperiod))->export($output),
 224              'nextperiodname' => userdate($nextperiod[0], get_string('strftimemonthyear')),
 225              'nextperiodlink' => $nextperiodlink->out(false),
 226              'larrow' => $output->larrow(),
 227              'rarrow' => $output->rarrow(),
 228              'includenavigation' => $this->includenavigation,
 229              'initialeventsloaded' => $this->initialeventsloaded,
 230          ];
 231  
 232          if ($this->showcoursefilter) {
 233              $return['filter_selector'] = $this->get_course_filter_selector($output);
 234          }
 235  
 236          if ($context = $this->get_default_add_context()) {
 237              $return['defaulteventcontext'] = $context->id;
 238          }
 239  
 240          if ($this->calendar->categoryid) {
 241              $return['categoryid'] = $this->calendar->categoryid;
 242          }
 243  
 244          return $return;
 245      }
 246  
 247      /**
 248       * Get the course filter selector.
 249       *
 250       * @param renderer_base $output
 251       * @return string The html code for the course filter selector.
 252       */
 253      protected function get_course_filter_selector(renderer_base $output) {
 254          $content = '';
 255          $content .= $output->course_filter_selector($this->url, '', $this->calendar->course->id);
 256  
 257          return $content;
 258      }
 259  
 260      /**
 261       * Get the list of day names for display, re-ordered from the first day
 262       * of the week.
 263       *
 264       * @param   renderer_base $output
 265       * @return  day_name_exporter[]
 266       */
 267      protected function get_day_names(renderer_base $output) {
 268          $weekdays = $this->related['type']->get_weekdays();
 269          $daysinweek = count($weekdays);
 270  
 271          $daynames = [];
 272          for ($i = 0; $i < $daysinweek; $i++) {
 273              // Bump the currentdayno and ensure it loops.
 274              $dayno = ($i + $this->firstdayofweek + $daysinweek) % $daysinweek;
 275              $dayname = new day_name_exporter($dayno, $weekdays[$dayno]);
 276              $daynames[] = $dayname->export($output);
 277          }
 278  
 279          return $daynames;
 280      }
 281  
 282      /**
 283       * Get the list of week days, ordered into weeks and padded according
 284       * to the value of the first day of the week.
 285       *
 286       * @param renderer_base $output
 287       * @return array The list of weeks.
 288       */
 289      protected function get_weeks(renderer_base $output) {
 290          $weeks = [];
 291          $alldays = $this->get_days();
 292  
 293          $daysinweek = count($this->related['type']->get_weekdays());
 294  
 295          // Calculate which day number is the first, and last day of the week.
 296          $firstdayofweek = $this->firstdayofweek;
 297  
 298          // The first week is special as it may have padding at the beginning.
 299          $day = reset($alldays);
 300          $firstdayno = $day['wday'];
 301  
 302          $prepadding = ($firstdayno + $daysinweek - $firstdayofweek) % $daysinweek;
 303          $daysinfirstweek = $daysinweek - $prepadding;
 304          $days = array_slice($alldays, 0, $daysinfirstweek);
 305          $week = new week_exporter($this->calendar, $days, $prepadding, ($daysinweek - count($days) - $prepadding), $this->related);
 306          $weeks[] = $week->export($output);
 307  
 308          // Now chunk up the remaining day. and turn them into weeks.
 309          $daychunks = array_chunk(array_slice($alldays, $daysinfirstweek), $daysinweek);
 310          foreach ($daychunks as $days) {
 311              $week = new week_exporter($this->calendar, $days, 0, ($daysinweek - count($days)), $this->related);
 312              $weeks[] = $week->export($output);
 313          }
 314  
 315          return $weeks;
 316      }
 317  
 318      /**
 319       * Get the list of days with the matching date array.
 320       *
 321       * @return array
 322       */
 323      protected function get_days() {
 324          $date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
 325          $monthdays = $this->related['type']->get_num_days_in_month($date['year'], $date['mon']);
 326  
 327          $days = [];
 328          for ($dayno = 1; $dayno <= $monthdays; $dayno++) {
 329              // Get the gregorian representation of the day.
 330              $timestamp = $this->related['type']->convert_to_timestamp($date['year'], $date['mon'], $dayno);
 331  
 332              $days[] = $this->related['type']->timestamp_to_date_array($timestamp);
 333          }
 334  
 335          return $days;
 336      }
 337  
 338      /**
 339       * Returns a list of objects that are related.
 340       *
 341       * @return array
 342       */
 343      protected static function define_related() {
 344          return [
 345              'events' => '\core_calendar\local\event\entities\event_interface[]',
 346              'cache' => '\core_calendar\external\events_related_objects_cache',
 347              'type' => '\core_calendar\type_base',
 348          ];
 349      }
 350  
 351      /**
 352       * Get the current month timestamp.
 353       *
 354       * @return int The month timestamp.
 355       */
 356      protected function get_month_data() {
 357          $date = $this->related['type']->timestamp_to_date_array($this->calendar->time);
 358          $monthtime = $this->related['type']->convert_to_gregorian($date['year'], $date['month'], 1);
 359  
 360          return make_timestamp($monthtime['year'], $monthtime['month']);
 361      }
 362  
 363      /**
 364       * Get the previous month timestamp.
 365       *
 366       * @return int The previous month timestamp.
 367       */
 368      protected function get_previous_month_data() {
 369          $type = $this->related['type'];
 370          $date = $type->timestamp_to_date_array($this->calendar->time);
 371          list($date['mon'], $date['year']) = $type->get_prev_month($date['year'], $date['mon']);
 372          $time = $type->convert_to_timestamp($date['year'], $date['mon'], 1);
 373  
 374          return $type->timestamp_to_date_array($time);
 375      }
 376  
 377      /**
 378       * Get the next month timestamp.
 379       *
 380       * @return int The next month timestamp.
 381       */
 382      protected function get_next_month_data() {
 383          $type = $this->related['type'];
 384          $date = $type->timestamp_to_date_array($this->calendar->time);
 385          list($date['mon'], $date['year']) = $type->get_next_month($date['year'], $date['mon']);
 386          $time = $type->convert_to_timestamp($date['year'], $date['mon'], 1);
 387  
 388          return $type->timestamp_to_date_array($time);
 389      }
 390  
 391      /**
 392       * Set whether the navigation should be shown.
 393       *
 394       * @param   bool    $include
 395       * @return  $this
 396       */
 397      public function set_includenavigation($include) {
 398          $this->includenavigation = $include;
 399  
 400          return $this;
 401      }
 402  
 403      /**
 404       * Set whether the initial events have already been loaded and
 405       * provided to the exporter.
 406       *
 407       * @param   bool    $loaded
 408       * @return  $this
 409       */
 410      public function set_initialeventsloaded(bool $loaded) {
 411          $this->initialeventsloaded = $loaded;
 412  
 413          return $this;
 414      }
 415  
 416      /**
 417       * Set whether the course filter selector should be shown.
 418       *
 419       * @param   bool    $show
 420       * @return  $this
 421       */
 422      public function set_showcoursefilter(bool $show) {
 423          $this->showcoursefilter = $show;
 424  
 425          return $this;
 426      }
 427  
 428      /**
 429       * Get the default context for use when adding a new event.
 430       *
 431       * @return null|\context
 432       */
 433      protected function get_default_add_context() {
 434          if (calendar_user_can_add_event($this->calendar->course)) {
 435              return \context_course::instance($this->calendar->course->id);
 436          }
 437  
 438          return null;
 439      }
 440  }