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 converting files between different file formats using google drive. 19 * 20 * @package fileconverter_googledrive 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 namespace fileconverter_googledrive; 25 26 defined('MOODLE_INTERNAL') || die(); 27 28 use stored_file; 29 use moodle_exception; 30 use moodle_url; 31 use \core_files\conversion; 32 33 /** 34 * Class for converting files between different formats using unoconv. 35 * 36 * @package fileconverter_googledrive 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 converter implements \core_files\converter_interface { 41 42 /** @var array $imports List of supported import file formats */ 43 private static $imports = [ 44 'doc' => 'application/vnd.google-apps.document', 45 'docx' => 'application/vnd.google-apps.document', 46 'rtf' => 'application/vnd.google-apps.document', 47 'xls' => 'application/vnd.google-apps.spreadsheet', 48 'xlsx' => 'application/vnd.google-apps.spreadsheet', 49 'ppt' => 'application/vnd.google-apps.presentation', 50 'pptx' => 'application/vnd.google-apps.presentation', 51 'html' => 'application/vnd.google-apps.document' 52 ]; 53 54 /** @var array $export List of supported export file formats */ 55 private static $exports = [ 56 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 57 'rtf' => 'application/rtf', 58 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 59 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 60 'pdf' => 'application/pdf', 61 'txt' => 'text/plain' 62 ]; 63 64 /** 65 * Convert a document to a new format and return a conversion object relating to the conversion in progress. 66 * 67 * @param \core_files\conversion $conversion The file to be converted 68 * @return this 69 */ 70 public function start_document_conversion(\core_files\conversion $conversion) { 71 global $CFG; 72 73 $file = $conversion->get_sourcefile(); 74 $format = $conversion->get('targetformat'); 75 76 $issuerid = get_config('fileconverter_googledrive', 'issuerid'); 77 if (empty($issuerid)) { 78 $conversion->set('status', conversion::STATUS_FAILED); 79 return $this; 80 } 81 82 $issuer = \core\oauth2\api::get_issuer($issuerid); 83 if (empty($issuer)) { 84 $conversion->set('status', conversion::STATUS_FAILED); 85 return $this; 86 } 87 $client = \core\oauth2\api::get_system_oauth_client($issuer); 88 89 $service = new \fileconverter_googledrive\rest($client); 90 91 $contenthash = $file->get_contenthash(); 92 93 $originalname = $file->get_filename(); 94 if (strpos($originalname, '.') === false) { 95 $conversion->set('status', conversion::STATUS_FAILED); 96 return $this; 97 } 98 $importextension = substr($originalname, strrpos($originalname, '.') + 1); 99 100 $importformat = self::$imports[$importextension]; 101 $exportformat = self::$exports[$format]; 102 103 $metadata = [ 104 'name' => $contenthash, 105 'mimeType' => $importformat 106 ]; 107 108 $filecontent = $file->get_content(); 109 $filesize = $file->get_filesize(); 110 $filemimetype = $file->get_mimetype(); 111 112 // Start resumable upload. 113 // First create empty file. 114 $params = [ 115 'uploadType' => 'resumable', 116 'fields' => 'id,name' 117 ]; 118 119 $client->setHeader('X-Upload-Content-Type: ' . $filemimetype); 120 $client->setHeader('X-Upload-Content-Length: ' . $filesize); 121 122 $headers = $service->call('upload', $params, json_encode($metadata)); 123 124 $uploadurl; 125 // Google returns a location header with the location for the upload. 126 foreach ($headers as $header) { 127 if (stripos($header, 'Location:') === 0) { 128 $uploadurl = trim(substr($header, strpos($header, ':') + 1)); 129 } 130 } 131 132 if (empty($uploadurl)) { 133 $conversion->set('status', conversion::STATUS_FAILED); 134 return $this; 135 } 136 137 $params = [ 138 'uploadurl' => $uploadurl 139 ]; 140 $result = $service->call('upload_content', $params, $filecontent, $filemimetype); 141 142 $fileid = $result->id; 143 // Now export it again. 144 $params = ['mimeType' => $exportformat]; 145 $sourceurl = new moodle_url('https://www.googleapis.com/drive/v3/files/' . $fileid . '/export', $params); 146 $source = $sourceurl->out(false); 147 148 $tmp = make_request_directory(); 149 $downloadto = $tmp . '/' . $fileid . '.' . $format; 150 151 $options = ['filepath' => $downloadto, 'timeout' => 15, 'followlocation' => true, 'maxredirs' => 5]; 152 $success = $client->download_one($source, null, $options); 153 154 if ($success) { 155 $conversion->store_destfile_from_path($downloadto); 156 $conversion->set('status', conversion::STATUS_COMPLETE); 157 $conversion->update(); 158 } else { 159 $conversion->set('status', conversion::STATUS_FAILED); 160 } 161 // Cleanup. 162 $params = [ 163 'fileid' => $fileid 164 ]; 165 $service->call('delete', $params); 166 167 return $this; 168 } 169 170 /** 171 * Generate and serve the test document. 172 * 173 * @return stored_file 174 */ 175 public function serve_test_document() { 176 global $CFG; 177 require_once($CFG->libdir . '/filelib.php'); 178 179 $filerecord = [ 180 'contextid' => \context_system::instance()->id, 181 'component' => 'test', 182 'filearea' => 'fileconverter_googledrive', 183 'itemid' => 0, 184 'filepath' => '/', 185 'filename' => 'conversion_test.docx' 186 ]; 187 188 // Get the fixture doc file content and generate and stored_file object. 189 $fs = get_file_storage(); 190 $testdocx = $fs->get_file($filerecord['contextid'], $filerecord['component'], $filerecord['filearea'], 191 $filerecord['itemid'], $filerecord['filepath'], $filerecord['filename']); 192 193 if (!$testdocx) { 194 $fixturefile = dirname(__DIR__) . '/tests/fixtures/source.docx'; 195 $testdocx = $fs->create_file_from_pathname($filerecord, $fixturefile); 196 } 197 198 $conversion = new \core_files\conversion(0, (object) [ 199 'targetformat' => 'pdf', 200 ]); 201 202 $conversion->set_sourcefile($testdocx); 203 $conversion->create(); 204 205 // Convert the doc file to pdf and send it direct to the browser. 206 $this->start_document_conversion($conversion); 207 208 $testfile = $conversion->get_destfile(); 209 readfile_accel($testfile, 'application/pdf', true); 210 } 211 212 /** 213 * Poll an existing conversion for status update. 214 * 215 * @param conversion $conversion The file to be converted 216 * @return $this; 217 */ 218 public function poll_conversion_status(conversion $conversion) { 219 return $this; 220 } 221 222 /** 223 * Whether the plugin is configured and requirements are met. 224 * 225 * @return bool 226 */ 227 public static function are_requirements_met() { 228 $issuerid = get_config('fileconverter_googledrive', 'issuerid'); 229 if (empty($issuerid)) { 230 return false; 231 } 232 233 $issuer = \core\oauth2\api::get_issuer($issuerid); 234 if (empty($issuer)) { 235 return false; 236 } 237 238 if (!$issuer->get('enabled')) { 239 return false; 240 } 241 242 if (!$issuer->is_system_account_connected()) { 243 return false; 244 } 245 246 return true; 247 } 248 249 /** 250 * Whether a file conversion can be completed using this converter. 251 * 252 * @param string $from The source type 253 * @param string $to The destination type 254 * @return bool 255 */ 256 public static function supports($from, $to) { 257 // This is not a one-liner because of php 5.6. 258 $imports = self::$imports; 259 $exports = self::$exports; 260 return isset($imports[$from]) && isset($exports[$to]); 261 } 262 263 /** 264 * A list of the supported conversions. 265 * 266 * @return string 267 */ 268 public function get_supported_conversions() { 269 return implode(', ', ['rtf', 'doc', 'xls', 'docx', 'xlsx', 'ppt', 'pptx', 'pdf', 'html']); 270 } 271 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body