See Release Notes
Long Term Support Release
Differences Between: [Versions 401 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 * Class for loading/storing data requests from the DB. 19 * 20 * @package tool_dataprivacy 21 * @copyright 2018 Jun Pataleta 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace tool_dataprivacy; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use lang_string; 30 use core\persistent; 31 32 /** 33 * Class for loading/storing data requests from the DB. 34 * 35 * @copyright 2018 Jun Pataleta 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class data_request extends persistent { 39 40 /** The table name this persistent object maps to. */ 41 const TABLE = 'tool_dataprivacy_request'; 42 43 /** Data request created manually. */ 44 const DATAREQUEST_CREATION_MANUAL = 0; 45 46 /** Data request created automatically. */ 47 const DATAREQUEST_CREATION_AUTO = 1; 48 49 /** 50 * Return the definition of the properties of this model. 51 * 52 * @return array 53 */ 54 protected static function define_properties() { 55 return [ 56 'type' => [ 57 'choices' => [ 58 api::DATAREQUEST_TYPE_EXPORT, 59 api::DATAREQUEST_TYPE_DELETE, 60 api::DATAREQUEST_TYPE_OTHERS, 61 ], 62 'type' => PARAM_INT 63 ], 64 'comments' => [ 65 'type' => PARAM_TEXT, 66 'message' => new lang_string('errorinvalidrequestcomments', 'tool_dataprivacy'), 67 'default' => '' 68 ], 69 'commentsformat' => [ 70 'choices' => [ 71 FORMAT_HTML, 72 FORMAT_MOODLE, 73 FORMAT_PLAIN, 74 FORMAT_MARKDOWN 75 ], 76 'type' => PARAM_INT, 77 'default' => FORMAT_PLAIN 78 ], 79 'userid' => [ 80 'default' => function() { 81 global $USER; 82 return $USER->id; 83 }, 84 'type' => PARAM_INT 85 ], 86 'requestedby' => [ 87 'default' => 0, 88 'type' => PARAM_INT 89 ], 90 'status' => [ 91 'default' => api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 92 'choices' => [ 93 api::DATAREQUEST_STATUS_PENDING, 94 api::DATAREQUEST_STATUS_AWAITING_APPROVAL, 95 api::DATAREQUEST_STATUS_APPROVED, 96 api::DATAREQUEST_STATUS_PROCESSING, 97 api::DATAREQUEST_STATUS_COMPLETE, 98 api::DATAREQUEST_STATUS_CANCELLED, 99 api::DATAREQUEST_STATUS_REJECTED, 100 api::DATAREQUEST_STATUS_DOWNLOAD_READY, 101 api::DATAREQUEST_STATUS_EXPIRED, 102 api::DATAREQUEST_STATUS_DELETED, 103 ], 104 'type' => PARAM_INT 105 ], 106 'dpo' => [ 107 'default' => 0, 108 'type' => PARAM_INT, 109 'null' => NULL_ALLOWED 110 ], 111 'dpocomment' => [ 112 'default' => '', 113 'type' => PARAM_TEXT, 114 'null' => NULL_ALLOWED 115 ], 116 'dpocommentformat' => [ 117 'choices' => [ 118 FORMAT_HTML, 119 FORMAT_MOODLE, 120 FORMAT_PLAIN, 121 FORMAT_MARKDOWN 122 ], 123 'type' => PARAM_INT, 124 'default' => FORMAT_PLAIN 125 ], 126 'systemapproved' => [ 127 'default' => false, 128 'type' => PARAM_BOOL, 129 ], 130 'creationmethod' => [ 131 'default' => self::DATAREQUEST_CREATION_MANUAL, 132 'choices' => [ 133 self::DATAREQUEST_CREATION_MANUAL, 134 self::DATAREQUEST_CREATION_AUTO 135 ], 136 'type' => PARAM_INT 137 ], 138 ]; 139 } 140 141 /** 142 * Determines whether a completed data export request has expired. 143 * The response will be valid regardless of the expiry scheduled task having run. 144 * 145 * @param data_request $request the data request object whose expiry will be checked. 146 * @return bool true if the request has expired. 147 */ 148 public static function is_expired(data_request $request) { 149 $result = false; 150 151 // Only export requests expire. 152 if ($request->get('type') == api::DATAREQUEST_TYPE_EXPORT) { 153 switch ($request->get('status')) { 154 // Expired requests are obviously expired. 155 case api::DATAREQUEST_STATUS_EXPIRED: 156 $result = true; 157 break; 158 // Complete requests are expired if the expiry time has elapsed. 159 case api::DATAREQUEST_STATUS_DOWNLOAD_READY: 160 $expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry'); 161 if ($expiryseconds > 0 && time() >= ($request->get('timemodified') + $expiryseconds)) { 162 $result = true; 163 } 164 break; 165 } 166 } 167 168 return $result; 169 } 170 171 /** 172 * Fetch completed data requests which are due to expire. 173 * 174 * @param int $userid Optional user ID to filter by. 175 * 176 * @return array Details of completed requests which are due to expire. 177 */ 178 public static function get_expired_requests($userid = 0) { 179 global $DB; 180 181 $expiryseconds = get_config('tool_dataprivacy', 'privacyrequestexpiry'); 182 $expirytime = strtotime("-{$expiryseconds} second"); 183 $table = self::TABLE; 184 $sqlwhere = 'type = :export_type AND status = :completestatus AND timemodified <= :expirytime'; 185 $params = array( 186 'export_type' => api::DATAREQUEST_TYPE_EXPORT, 187 'completestatus' => api::DATAREQUEST_STATUS_DOWNLOAD_READY, 188 'expirytime' => $expirytime, 189 ); 190 $sort = 'id'; 191 $fields = 'id, userid'; 192 193 // Filter by user ID if specified. 194 if ($userid > 0) { 195 $sqlwhere .= ' AND (userid = :userid OR requestedby = :requestedby)'; 196 $params['userid'] = $userid; 197 $params['requestedby'] = $userid; 198 } 199 200 return $DB->get_records_select_menu($table, $sqlwhere, $params, $sort, $fields, 0, 2000); 201 } 202 203 /** 204 * Expire a given set of data requests. 205 * Update request status and delete the files. 206 * 207 * @param array $expiredrequests [requestid => userid] 208 * 209 * @return void 210 */ 211 public static function expire($expiredrequests) { 212 global $DB; 213 214 $ids = array_keys($expiredrequests); 215 216 if (count($ids) > 0) { 217 list($insql, $inparams) = $DB->get_in_or_equal($ids); 218 $initialparams = array(api::DATAREQUEST_STATUS_EXPIRED, time()); 219 $params = array_merge($initialparams, $inparams); 220 221 $update = "UPDATE {" . self::TABLE . "} 222 SET status = ?, timemodified = ? 223 WHERE id $insql"; 224 225 if ($DB->execute($update, $params)) { 226 $fs = get_file_storage(); 227 228 foreach ($expiredrequests as $id => $userid) { 229 $usercontext = \context_user::instance($userid); 230 $fs->delete_area_files($usercontext->id, 'tool_dataprivacy', 'export', $id); 231 } 232 } 233 } 234 } 235 236 /** 237 * Whether this request is in a state appropriate for reset/resubmission. 238 * 239 * Note: This does not check whether any other completed requests exist for this user. 240 * 241 * @return bool 242 */ 243 public function is_resettable() : bool { 244 if (api::DATAREQUEST_TYPE_OTHERS == $this->get('type')) { 245 // It is not possible to reset 'other' reqeusts. 246 return false; 247 } 248 249 $resettable = [ 250 api::DATAREQUEST_STATUS_APPROVED => true, 251 api::DATAREQUEST_STATUS_REJECTED => true, 252 ]; 253 254 return isset($resettable[$this->get('status')]); 255 } 256 257 /** 258 * Whether this request is 'active'. 259 * 260 * @return bool 261 */ 262 public function is_active() : bool { 263 $active = [ 264 api::DATAREQUEST_STATUS_APPROVED => true, 265 ]; 266 267 return isset($active[$this->get('status')]); 268 } 269 270 /** 271 * Reject this request and resubmit it as a fresh request. 272 * 273 * Note: This does not check whether any other completed requests exist for this user. 274 * 275 * @return self 276 */ 277 public function resubmit_request() : data_request { 278 if ($this->is_active()) { 279 $this->set('status', api::DATAREQUEST_STATUS_REJECTED)->save(); 280 } 281 282 if (!$this->is_resettable()) { 283 throw new \moodle_exception('cannotreset', 'tool_dataprivacy'); 284 } 285 286 $currentdata = $this->to_record(); 287 unset($currentdata->id); 288 289 // Clone the original request, but do not notify. 290 $clone = api::create_data_request( 291 $this->get('userid'), 292 $this->get('type'), 293 $this->get('comments'), 294 $this->get('creationmethod'), 295 false 296 ); 297 $clone->set('comments', $this->get('comments')); 298 $clone->set('dpo', $this->get('dpo')); 299 $clone->set('requestedby', $this->get('requestedby')); 300 $clone->save(); 301 302 return $clone; 303 } 304 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body