From 3e7236ecfff5ab9c1384ebf63575601cac96d4a9 Mon Sep 17 00:00:00 2001 From: Andrej Vitez Date: Sat, 20 May 2017 13:26:57 +0200 Subject: [PATCH 1/2] Added command to support importing of user profile pictures. --- .../Generic/User/ImportUserPictures.php | 212 ++++++++++++++++++ 1 file changed, 212 insertions(+) create mode 100644 Moosh/Command/Generic/User/ImportUserPictures.php diff --git a/Moosh/Command/Generic/User/ImportUserPictures.php b/Moosh/Command/Generic/User/ImportUserPictures.php new file mode 100644 index 00000000..dfc6d9a3 --- /dev/null +++ b/Moosh/Command/Generic/User/ImportUserPictures.php @@ -0,0 +1,212 @@ + + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace Moosh\Command\Generic\User; + +use Moosh\MooshCommand; + +/** + * Class ImportUserPictures + * @package Moosh\Command\Generic\User + */ +class ImportUserPictures extends MooshCommand +{ + const LOG_MESSAGE = 0; + const LOG_VERBOSE = 1; + + private $supportedFileTypes = array('jpg', 'jpeg', 'gif', 'png'); + private $overwrite; + private $userField; + + /** + * ImportUserPictures constructor. + */ + public function __construct() + { + parent::__construct('import-pictures', 'user'); + + $this->addArgument('import_dir'); + $this->addOption('overwrite', 'Replace existing user pictures', false); + $this->addOption('i|id', 'Map image filename to user ID field'); + $this->addOption('n|idnumber', 'Map image filename to user idnumber field'); + $this->addOption('u|username', 'Map image filename to user username field'); + } + + public function execute() + { + global $CFG; + + try { + $this->overwrite = (bool)$this->expandedOptions['overwrite']; + $this->userField = $this->getMapFieldName(); + $inputDir = $this->arguments[0]; + + require_once($CFG->libdir . '/uploadlib.php'); + require_once($CFG->libdir . '/adminlib.php'); + require_once($CFG->libdir . '/gdlib.php'); + + // check if input directory exists + if (!file_exists($inputDir)) { + throw new \RuntimeException(sprintf("Input directory '%s' does not exist.", $inputDir)); + } + + $this->processDirectory($inputDir); + } catch (\Exception $ex) { + $this->logError($this->verbose ? $ex->__toString() : $ex->getMessage()); + } + } + + /** + * Returns user record field name for mapping image filename to Moodle user. + * + * @return string id, idnumber or username. + * @throws \Exception If field name is not set by user. + */ + private function getMapFieldName() + { + $selectedFieldName = null; + + foreach (array('id', 'idnumber', 'username') as $fieldName) { + if (empty($this->expandedOptions[$fieldName])) { + continue; + } + + if ($selectedFieldName) { + throw new \Exception('Only one user field mapping can be used'); + } + + $selectedFieldName = $fieldName; + } + + if (!$selectedFieldName) { + throw new \RuntimeException('Please specify mapped user field with available options'); + } + + return $selectedFieldName; + } + + /** + * Process directory contents recursively. + * + * @param string $dir Directory absolute path + */ + private function processDirectory($dir) + { + if (!($handle = opendir($dir))) { + throw new \RuntimeException(sprintf("Cannot open import dir '%s'", $dir)); + } + + while (false !== ($item = readdir($handle))) { + if ($item == '.' || $item == '..') { + continue; + } + + $filename = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $item; + + if (is_dir($filename)) { + $this->processDirectory($filename); + } else if (is_file($filename)) { + $this->processFile($filename); + } else { + throw new \RuntimeException(sprintf("Could not process file '%s'", $filename)); + } + } + + closedir($handle); + } + + /** + * Finds Moodle user and imports picture. + * + * @param string $filename Image filename + */ + private function processFile($filename) + { + global $DB; + + // obtain file info + $path_parts = pathinfo(cleardoubleslashes($filename)); + $basename = $path_parts['filename']; // ignore file extension + $extension = strtolower($path_parts['extension']); + + if (!in_array($extension, $this->supportedFileTypes)) { + $this->log(sprintf("Ignoring unsupported file type '%s'", $filename), self::LOG_VERBOSE); + return; + } + + // load user by mapped field name + $user = $DB->get_record('user', array($this->userField => $basename)); + + if (!$user) { + $this->logError( + sprintf( + "Cannot find user by %s value '%s' for filename '%s'", + $this->userField, + $basename, + $filename + ) + ); + return; + } + + // picture field might not have been cached by moodle so query database again + $hasPicture = $DB->get_field('user', 'picture', array('id' => $user->id)); + if ($hasPicture && !$this->overwrite) { + $this->log( + sprintf("Refusing to overwrite user ID:%s picture with file '%s'", $user->id, $filename), + self::LOG_VERBOSE + ); + return; + } + + // grab user's context and process image file + $context = \context_user::instance($user->id); + $fileId = process_new_icon($context, 'user', 'icon', 0, $filename); + + if ($fileId) { + $DB->set_field('user', 'picture', $fileId, array('id' => $user->id)); + $this->log(sprintf("Updated user id:%s picture '%s'", $user->id, $filename)); + } else { + $this->logError( + sprintf("Cannot process image '%s' for user ID:%s", $filename, $user->id) + ); + } + } + + /** + * Outputs message to stdout. + * + * @param string $message Output message. + * @param int $level Verbosity level, see self:LOG_* constants. + */ + private function log($message, $level = self::LOG_MESSAGE) + { + if ($level == self::LOG_VERBOSE && !$this->verbose) { + return; + } + + echo "$message\n"; + } + + /** + * Outputs error message to stderr. + * + * @param string $message Output message. + */ + private function logError($message) + { + fwrite(STDERR, "ERROR: $message\n"); + } + + public function bootstrapLevel() + { + return self::$BOOTSTRAP_FULL; + } +} From 71166b2ef5d08d1baf38d4e8a705e17921eb15c7 Mon Sep 17 00:00:00 2001 From: Andrej Vitez Date: Sun, 21 May 2017 22:52:00 +0200 Subject: [PATCH 2/2] - moved command from Generic to Moodle22 namespace - adjusted user filtering (ignoring deleted) - added command documentation --- .../User/ImportUserPictures.php | 4 ++-- www/commands/index.md | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) rename Moosh/Command/{Generic => Moodle22}/User/ImportUserPictures.php (98%) diff --git a/Moosh/Command/Generic/User/ImportUserPictures.php b/Moosh/Command/Moodle22/User/ImportUserPictures.php similarity index 98% rename from Moosh/Command/Generic/User/ImportUserPictures.php rename to Moosh/Command/Moodle22/User/ImportUserPictures.php index dfc6d9a3..aaee7c0c 100644 --- a/Moosh/Command/Generic/User/ImportUserPictures.php +++ b/Moosh/Command/Moodle22/User/ImportUserPictures.php @@ -8,7 +8,7 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -namespace Moosh\Command\Generic\User; +namespace Moosh\Command\Moodle22\User; use Moosh\MooshCommand; @@ -142,7 +142,7 @@ private function processFile($filename) } // load user by mapped field name - $user = $DB->get_record('user', array($this->userField => $basename)); + $user = $DB->get_record('user', array($this->userField => $basename, 'deleted' => 0)); if (!$user) { $this->logError( diff --git a/www/commands/index.md b/www/commands/index.md index da840e0b..b251875b 100755 --- a/www/commands/index.md +++ b/www/commands/index.md @@ -1288,6 +1288,27 @@ Example 1: This command has been deprecated. Use user-list instead. + +user-import-pictures + +-------- + +Provides capability of importing user pictures from a specific directory (recursively including subdirectories). +Image filename can be mapped to user ID, idnumber or username database field. Supported image types are jpg, gif and png. +--overwrite option flag can be used to force overwriting of existing user pictures. + +Example 1: import user pictures from directory and map file names to user's ID value. + + moosh user-import-pictures -i /path/to/import/dir + +Example 2: import user pictures from directory and map file names to user's idnumber value. + + moosh user-import-pictures -n /path/to/import/dir + +Example 3: import user pictures from directory and map file names to user's username value. + + moosh user-import-pictures -u /path/to/import/dir + user-list --------