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 * File containing the class activity information renderable.
19 *
20 * @package    core_course
21 * @copyright  2021 Jun Pataleta <jun@moodle.com>
22 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23 */
24namespace core_course\output;
25
26defined('MOODLE_INTERNAL') || die();
27
28use cm_info;
29use completion_info;
30use context;
31use core\activity_dates;
32use core_availability\info;
33use core_completion\cm_completion_details;
34use core_user;
35use core_user\fields;
36use renderable;
37use renderer_base;
38use stdClass;
39use templatable;
40
41/**
42 * The activity information renderable class.
43 *
44 * @package    core_course
45 * @copyright  2021 Jun Pataleta <jun@moodle.com>
46 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
47 */
48class activity_information implements renderable, templatable {
49
50    /** @var cm_info The course module information. */
51    protected $cminfo = null;
52
53    /** @var array The array of relevant dates for this activity. */
54    protected $activitydates = [];
55
56    /** @var cm_completion_details The user's completion details for this activity. */
57    protected $cmcompletion = null;
58
59    /**
60     * Constructor.
61     *
62     * @param cm_info $cminfo The course module information.
63     * @param cm_completion_details $cmcompletion The course module information.
64     * @param array $activitydates The activity dates.
65     */
66    public function __construct(cm_info $cminfo, cm_completion_details $cmcompletion, array $activitydates) {
67        $this->cminfo = $cminfo;
68        $this->cmcompletion = $cmcompletion;
69        $this->activitydates = $activitydates;
70    }
71
72    /**
73     * Export this data so it can be used as the context for a mustache template.
74     *
75     * @param renderer_base $output Renderer base.
76     * @return stdClass
77     */
78    public function export_for_template(renderer_base $output): stdClass {
79        $data = $this->build_completion_data();
80
81        $data->cmid = $this->cminfo->id;
82        $data->activityname = $this->cminfo->name;
83        $this->build_dates_data($data);
84        $data->hasdates = !empty($this->activitydates);
85
86        return $data;
87    }
88
89    /**
90     * Builds the dates data for export.
91     *
92     * @param stdClass $data
93     */
94    protected function build_dates_data(stdClass $data): void {
95        foreach ($this->activitydates as $date) {
96            if (empty($date['relativeto'])) {
97                $date['datestring'] = userdate($date['timestamp'], get_string('strftimedaydatetime', 'core_langconfig'));
98            } else {
99                $diffstr = get_time_interval_string($date['timestamp'], $date['relativeto']);
100                if ($date['timestamp'] >= $date['relativeto']) {
101                    $date['datestring'] = get_string('relativedatessubmissionduedateafter', 'core_course',
102                        ['datediffstr' => $diffstr]);
103                } else {
104                    $date['datestring'] = get_string('relativedatessubmissionduedatebefore', 'core_course',
105                        ['datediffstr' => $diffstr]);
106                }
107            }
108            $data->activitydates[] = $date;
109        }
110    }
111
112    /**
113     * Builds the completion data for export.
114     *
115     * @return stdClass
116     */
117    protected function build_completion_data(): stdClass {
118        global $CFG;
119
120        $data = new stdClass();
121
122        $data->hascompletion = $this->cmcompletion->has_completion();
123        $data->isautomatic = $this->cmcompletion->is_automatic();
124        $data->showmanualcompletion = $this->cmcompletion->show_manual_completion();
125
126        // Get the name of the user overriding the completion condition, if available.
127        $data->overrideby = null;
128        $overrideby = $this->cmcompletion->overridden_by();
129        $overridebyname = null;
130        if (!empty($overrideby)) {
131            $userfields = fields::for_name();
132            $overridebyrecord = core_user::get_user($overrideby, 'id ' . $userfields->get_sql()->selects, MUST_EXIST);
133            $data->overrideby = fullname($overridebyrecord);
134        }
135
136        // We'll show only the completion conditions and not the completion status if we're not tracking completion for this user
137        // (e.g. a teacher, admin).
138        $data->istrackeduser = $this->cmcompletion->is_tracked_user();
139
140        // Overall completion states.
141        $overallcompletion = $this->cmcompletion->get_overall_completion();
142        $data->overallcomplete = $overallcompletion == COMPLETION_COMPLETE;
143        $data->overallincomplete = $overallcompletion == COMPLETION_INCOMPLETE;
144
145        // Set an accessible description for manual completions with overridden completion state.
146        if (!$data->isautomatic && $data->overrideby) {
147            $setbydata = (object)[
148                'activityname' => $this->cminfo->name,
149                'setby' => $data->overrideby,
150            ];
151            $setbylangkey = $data->overallcomplete ? 'completion_setby:manual:done' : 'completion_setby:manual:markdone';
152            $data->accessibledescription = get_string($setbylangkey, 'course', $setbydata);
153        }
154
155        // Whether the completion of this activity controls the availability of other activities/sections in the course.
156        $data->withavailability = false;
157        $course = $this->cminfo->get_course();
158        // An activity with manual completion tracking which is used to enable access to other activities/sections in
159        // the course needs to refresh the page after having its completion state toggled. This withavailability flag will enable
160        // this functionality on the course homepage. Otherwise, the completion toggling will just happen normally via ajax.
161        if ($this->cmcompletion->has_completion() && !$this->cmcompletion->is_automatic()) {
162            $data->withavailability = !empty($CFG->enableavailability) && info::completion_value_used($course, $this->cminfo->id);
163        }
164
165        // Whether this activity is visible to the user. If not, completion information will not be shown.
166        $data->uservisible = $this->cminfo->uservisible;
167
168        // Build automatic completion details.
169        $details = [];
170        foreach ($this->cmcompletion->get_details() as $key => $detail) {
171            // Set additional attributes for the template.
172            $detail->key = $key;
173            $detail->statuscomplete = in_array($detail->status, [COMPLETION_COMPLETE, COMPLETION_COMPLETE_PASS]);
174            $detail->statuscompletefail = $detail->status == COMPLETION_COMPLETE_FAIL;
175            $detail->statusincomplete = $detail->status == COMPLETION_INCOMPLETE;
176
177            // Add an accessible description to be used for title and aria-label attributes for overridden completion details.
178            if ($data->overrideby) {
179                $setbydata = (object)[
180                    'condition' => $detail->description,
181                    'setby' => $data->overrideby,
182                ];
183                $overridestatus = $detail->statuscomplete ? 'done' : 'todo';
184                $detail->accessibledescription = get_string('completion_setby:auto:' . $overridestatus, 'course', $setbydata);
185            }
186
187            // We don't need the status in the template.
188            unset($detail->status);
189
190            $details[] = $detail;
191        }
192        $data->completiondetails = $details;
193
194        return $data;
195    }
196}
197