diff --git a/lang/en/moodle.php b/lang/en/moodle.php index 1dfcb74b4f8..5caa7836f08 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -698,6 +698,8 @@ $string['emailresetconfirmationsubject'] = '{$a}: Password reset request'; $string['emailresetconfirmsent'] = 'An email has been sent to your address at {$a}.
It contains easy instructions to confirm and complete this password change. If you continue to have difficulty, contact the site administrator.'; +$string['emailtoprivatefiles'] = 'You can also e-mail files as attachments straight to your private files space. Simply attach your files to an e-mail and send it to {$a}'; +$string['emailtoprivatefilesdenied'] = 'Your administrator has disabled the option to upload your own private files.'; $string['emptydragdropregion'] = 'empty region'; $string['enable'] = 'Enable'; $string['encryptedcode'] = 'Encrypted code'; @@ -1445,6 +1447,8 @@ $string['previous'] = 'Previous'; $string['previouslyselectedusers'] = 'Previously selected users not matching \'{$a}\''; $string['previoussection'] = 'Previous section'; $string['primaryadminsetup'] = 'Setup administrator account'; +$string['private_files_handler'] = 'Store attachments to an e-mail in the user\'s private files storage space.'; +$string['private_files_handler_name'] = 'Email to Private files'; $string['profile'] = 'Profile'; $string['profilenotshown'] = 'This profile description will not be shown until this person is enrolled in at least one course.'; $string['publicprofile'] = 'Public profile'; diff --git a/lib/classes/message/inbound/private_files_handler.php b/lib/classes/message/inbound/private_files_handler.php new file mode 100644 index 00000000000..07c63ca1d79 --- /dev/null +++ b/lib/classes/message/inbound/private_files_handler.php @@ -0,0 +1,138 @@ +. + +/** + * A Handler to store attachments sent in e-mails as private files. + * + * @package core_message + * @copyright 2014 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace core\message\inbound; + +defined('MOODLE_INTERNAL') || die(); + +/** + * A Handler to store attachments sent in e-mails as private files. + * + * @package core + * @copyright 2014 Andrew Nicols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class private_files_handler extends handler { + + /** + * Return a description for the current handler. + * + * @return string + */ + public function get_description() { + return get_string('private_files_handler', 'moodle'); + } + + /** + * Return a short name for the current handler. + * This appears in the admin pages as a human-readable name. + * + * @return string + */ + public function get_name() { + return get_string('private_files_handler_name', 'moodle'); + } + + /** + * Process a message received and validated by the Inbound Message processor. + * + * @throws \core\message\inbound\processing_failed_exception + * @param \stdClass $record The Inbound Message record + * @param \stdClass $data The message data packet + * @return bool Whether the message was successfully processed. + */ + public function process_message(\stdClass $record, \stdClass $data) { + global $USER, $CFG; + + $context = \context_user::instance($USER->id); + + if (!has_capability('moodle/user:manageownfiles', $context)) { + throw new \core\message\inbound\processing_failed_exception('emailtoprivatefilesdenied', 'moodle', $data); + } + + // Initial setup. + $component = 'user'; + $filearea = 'private'; + $itemid = 0; + $license = $CFG->sitedefaultlicense; + $author = fullname($USER); + + // Determine the quota space for this user. + $maxbytes = $CFG->userquota; + if (has_capability('moodle/user:ignoreuserquota', $context)) { + $maxbytes = USER_CAN_IGNORE_FILE_SIZE_LIMITS; + } + + // Keep track of files which were uploaded, and which were skipped. + $skippedfiles = array(); + $uploadedfiles = array(); + $failedfiles = array(); + + $fs = get_file_storage(); + foreach ($data->attachments as $attachmenttype => $attachments) { + foreach ($attachments as $attachment) { + mtrace("--- Processing attachment '{$attachment->filename}'"); + + if (file_is_draft_area_limit_reached($itemid, $maxbytes, $attachment->filesize)) { + // The user quota will be exceeded if this file is included. + $skippedfiles[] = $attachment; + mtrace("---- Skipping attacment. User will be over quota."); + continue; + } + + // Create a new record for this file. + $record = new \stdClass(); + $record->filearea = $filearea; + $record->component = $component; + $record->filepath = '/'; + $record->itemid = $itemid; + $record->license = $license; + $record->author = $author; + $record->contextid = $context->id; + $record->userid = $USER->id; + + $record->filename = $fs->get_unused_filename($context->id, $record->component, $record->filearea, + $record->itemid, $record->filepath, $attachment->filename); + + mtrace("--> Attaching {$record->filename} to " . + "/{$record->contextid}/{$record->component}/{$record->filearea}/" . + "{$record->itemid}{$record->filepath}{$record->filename}"); + + if ($fs->create_file_from_string($record, $attachment->content)) { + // File created successfully. + mtrace("---- File uploaded successfully as {$record->filename}."); + $uploadedfiles[] = $attachment; + } else { + mtrace("---- Skipping attacment. Unknown failure during creation."); + $failedfiles[] = $attachment; + } + } + } + + // TODO send the user a confirmation e-mail. + // Note, some files may have failed because the user has been pushed over quota. This does not constitute a failure. + + return true; + } +} diff --git a/lib/db/messageinbound_handlers.php b/lib/db/messageinbound_handlers.php new file mode 100644 index 00000000000..05e1ae8b2a6 --- /dev/null +++ b/lib/db/messageinbound_handlers.php @@ -0,0 +1,32 @@ +. + +/** + * Inbound Message Handlers for core. + * + * @package core_message + * @copyright 2014 Andrew NIcols + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +defined('MOODLE_INTERNAL') || die(); + +$handlers = array( + array( + 'classname' => '\core\message\inbound\private_files_handler', + 'defaultexpiration' => null, + ), +); diff --git a/user/files.php b/user/files.php index f1f03b77f66..00d1ebada6f 100644 --- a/user/files.php +++ b/user/files.php @@ -64,6 +64,12 @@ $options = array('subdirs' => 1, 'maxbytes' => $maxbytes, 'maxfiles' => -1, 'acc 'areamaxbytes' => $maxareabytes); file_prepare_standard_filemanager($data, 'files', $options, $context, 'user', 'private', 0); +// Attempt to generate an inbound message address to support e-mail to private files. +$generator = new \core\message\inbound\address_manager(); +$generator->set_handler('\core\message\inbound\private_files_handler'); +$generator->set_data(-1); +$data->emaillink = $generator->generate($USER->id); + $mform = new user_files_form(null, array('data' => $data, 'options' => $options)); if ($mform->is_cancelled()) { @@ -75,6 +81,8 @@ if ($mform->is_cancelled()) { echo $OUTPUT->header(); echo $OUTPUT->box_start('generalbox'); + + $mform->display(); echo $OUTPUT->box_end(); echo $OUTPUT->footer(); diff --git a/user/files_form.php b/user/files_form.php index 9c0947ef3d2..709b3c9a86d 100644 --- a/user/files_form.php +++ b/user/files_form.php @@ -45,6 +45,11 @@ class user_files_form extends moodleform { $mform->addElement('filemanager', 'files_filemanager', get_string('files'), null, $options); $mform->addElement('hidden', 'returnurl', $data->returnurl); + if (isset($data->emaillink)) { + $emaillink = html_writer::link(new moodle_url('mailto:' . $data->emaillink), $data->emaillink); + $mform->addElement('static', 'emailaddress', '', + get_string('emailtoprivatefiles', 'moodle', $emaillink)); + } $mform->setType('returnurl', PARAM_LOCALURL); $this->add_action_buttons(true, get_string('savechanges')); diff --git a/version.php b/version.php index da46cfee28f..67c83fbaa1c 100644 --- a/version.php +++ b/version.php @@ -29,7 +29,7 @@ defined('MOODLE_INTERNAL') || die(); -$version = 2014100800.00; // YYYYMMDD = weekly release date of this DEV branch. +$version = 2014100800.01; // YYYYMMDD = weekly release date of this DEV branch. // RR = release increments - 00 in DEV branches. // .XX = incremental changes.