AutorÃa | Ultima modificación | Ver Log |
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Custom lang importer.
*
* @package tool_customlang
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_customlang\local;
use tool_customlang\local\mlang\phpparser;
use tool_customlang\local\mlang\logstatus;
use tool_customlang\local\mlang\langstring;
use core\output\notification;
use stored_file;
use coding_exception;
use moodle_exception;
use core_component;
use stdClass;
/**
* Class containing tha custom lang importer
*
* @package tool_customlang
* @copyright 2020 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class importer {
/** @var int imports will only create new customizations */
public const IMPORTNEW = 1;
/** @var int imports will only update the current customizations */
public const IMPORTUPDATE = 2;
/** @var int imports all strings */
public const IMPORTALL = 3;
/**
* @var string the language name
*/
protected $lng;
/**
* @var int the importation mode (new, update, all)
*/
protected $importmode;
/**
* @var string request folder path
*/
private $folder;
/**
* @var array import log messages
*/
private $log;
/**
* Constructor for the importer class.
*
* @param string $lng the current language to import.
* @param int $importmode the import method (IMPORTALL, IMPORTNEW, IMPORTUPDATE).
*/
public function __construct(string $lng, int $importmode = self::IMPORTALL) {
$this->lng = $lng;
$this->importmode = $importmode;
$this->log = [];
}
/**
* Returns the last parse log.
*
* @return logstatus[] mlang logstatus with the messages
*/
public function get_log(): array {
return $this->log;
}
/**
* Import customlang files.
*
* @param stored_file[] $files array of files to import
*/
public function import(array $files): void {
// Create a temporal folder to store the files.
$this->folder = make_request_directory(false);
$langfiles = $this->deploy_files($files);
$this->process_files($langfiles);
}
/**
* Deploy all files into a request folder.
*
* @param stored_file[] $files array of files to deploy
* @return string[] of file paths
*/
private function deploy_files(array $files): array {
$result = [];
// Desploy all files.
foreach ($files as $file) {
if ($file->get_mimetype() == 'application/zip') {
$result = array_merge($result, $this->unzip_file($file));
} else {
$path = $this->folder.'/'.$file->get_filename();
$file->copy_content_to($path);
$result = array_merge($result, [$path]);
}
}
return $result;
}
/**
* Unzip a file into the request folder.
*
* @param stored_file $file the zip file to unzip
* @return string[] of zip content paths
*/
private function unzip_file(stored_file $file): array {
$fp = get_file_packer('application/zip');
$zipcontents = $fp->extract_to_pathname($file, $this->folder);
if (!$zipcontents) {
throw new moodle_exception("Error Unzipping file", 1);
}
$result = [];
foreach ($zipcontents as $contentname => $success) {
if ($success) {
$result[] = $this->folder.'/'.$contentname;
}
}
return $result;
}
/**
* Import strings from a list of langfiles.
*
* @param string[] $langfiles an array with file paths
*/
private function process_files(array $langfiles): void {
$parser = phpparser::get_instance();
foreach ($langfiles as $filepath) {
$component = $this->component_from_filepath($filepath);
if ($component) {
$strings = $parser->parse(file_get_contents($filepath));
$this->import_strings($strings, $component);
}
}
}
/**
* Try to get the component from a filepath.
*
* @param string $filepath the filepath
* @return stdCalss|null the DB record of that component
*/
private function component_from_filepath(string $filepath) {
global $DB;
// Get component from filename.
$pathparts = pathinfo($filepath);
if (empty($pathparts['filename'])) {
throw new coding_exception("Cannot get filename from $filepath", 1);
}
$filename = $pathparts['filename'];
$normalized = core_component::normalize_component($filename);
if (count($normalized) == 1 || empty($normalized[1])) {
$componentname = $normalized[0];
} else {
$componentname = implode('_', $normalized);
}
$result = $DB->get_record('tool_customlang_components', ['name' => $componentname]);
if (!$result) {
$this->log[] = new logstatus('notice_missingcomponent', notification::NOTIFY_ERROR, null, $componentname);
return null;
}
return $result;
}
/**
* Import an array of strings into the customlang tables.
*
* @param langstring[] $strings the langstring to set
* @param stdClass $component the target component
*/
private function import_strings(array $strings, stdClass $component): void {
global $DB;
foreach ($strings as $newstring) {
// Check current DB entry.
$customlang = $DB->get_record('tool_customlang', [
'componentid' => $component->id,
'stringid' => $newstring->id,
'lang' => $this->lng,
]);
if (!$customlang) {
$customlang = null;
}
if ($this->can_save_string($customlang, $newstring, $component)) {
$customlang->local = $newstring->text;
$customlang->timecustomized = $newstring->timemodified;
$customlang->outdated = 0;
$customlang->modified = 1;
$DB->update_record('tool_customlang', $customlang);
}
}
}
/**
* Determine if a specific string can be saved based on the current importmode.
*
* @param stdClass $customlang customlang original record
* @param langstring $newstring the new strign to store
* @param stdClass $component the component target
* @return bool if the string can be stored
*/
private function can_save_string(?stdClass $customlang, langstring $newstring, stdClass $component): bool {
$result = false;
$message = 'notice_success';
if (empty($customlang)) {
$message = 'notice_inexitentstring';
$this->log[] = new logstatus($message, notification::NOTIFY_ERROR, null, $component->name, $newstring);
return $result;
}
switch ($this->importmode) {
case self::IMPORTNEW:
$result = empty($customlang->local);
$warningmessage = 'notice_ignoreupdate';
break;
case self::IMPORTUPDATE:
$result = !empty($customlang->local);
$warningmessage = 'notice_ignorenew';
break;
case self::IMPORTALL:
$result = true;
break;
}
if ($result) {
$errorlevel = notification::NOTIFY_SUCCESS;
} else {
$errorlevel = notification::NOTIFY_ERROR;
$message = $warningmessage;
}
$this->log[] = new logstatus($message, $errorlevel, null, $component->name, $newstring);
return $result;
}
}