From 851f0d335409a25e1bf1361bce5697412d20f859 Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Tue, 25 Aug 2020 22:18:09 +0200 Subject: [PATCH 1/4] MDL-69582 tool_customlang: add label to textarea --- admin/tool/customlang/templates/translator.mustache | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/tool/customlang/templates/translator.mustache b/admin/tool/customlang/templates/translator.mustache index 400464cb0de..a7569684265 100644 --- a/admin/tool/customlang/templates/translator.mustache +++ b/admin/tool/customlang/templates/translator.mustache @@ -129,8 +129,8 @@ {{#str}}headinglocal, tool_customlang{{/str}}
- - + + {{#checkupdated}}
From ccd0b96591acd0095dd0c84d7391ab1c6a254ace Mon Sep 17 00:00:00 2001 From: Ferran Recio Date: Fri, 28 Aug 2020 16:36:48 +0200 Subject: [PATCH 2/4] MDL-69582 tool_customlang: fix options layout --- admin/tool/customlang/classes/output/renderer.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/admin/tool/customlang/classes/output/renderer.php b/admin/tool/customlang/classes/output/renderer.php index b183daf93ee..9ad60c5839b 100644 --- a/admin/tool/customlang/classes/output/renderer.php +++ b/admin/tool/customlang/classes/output/renderer.php @@ -57,8 +57,9 @@ class renderer extends \plugin_renderer_base { protected function render_tool_customlang_menu(\tool_customlang_menu $menu) { $output = ''; foreach ($menu->get_items() as $item) { - $output .= $this->single_button($item->url, $item->title, $item->method); + $button = $this->single_button($item->url, $item->title, $item->method); + $output .= $this->box($button, 'menu'); } - return $this->box($output, 'menu'); + return $output; } } From 78076a621f6409fc7ccedd718ad6bff321c88288 Mon Sep 17 00:00:00 2001 From: Thomas Wedekind Date: Mon, 24 Aug 2020 15:42:34 +0200 Subject: [PATCH 3/4] MDL-69582 tool_customlang: add export langstring feature --- admin/tool/customlang/classes/form/export.php | 68 +++++++++++++++++++ admin/tool/customlang/db/access.php | 9 +++ admin/tool/customlang/export.php | 68 +++++++++++++++++++ admin/tool/customlang/filter_form.php | 1 - admin/tool/customlang/index.php | 14 +++- .../customlang/lang/en/tool_customlang.php | 11 +++ admin/tool/customlang/locallib.php | 2 +- .../tests/behat/customisation_create.feature | 30 ++++++++ .../customlang/tests/behat/export.feature | 49 +++++++++++++ admin/tool/customlang/version.php | 2 +- 10 files changed, 249 insertions(+), 5 deletions(-) create mode 100644 admin/tool/customlang/classes/form/export.php create mode 100644 admin/tool/customlang/export.php create mode 100644 admin/tool/customlang/tests/behat/customisation_create.feature create mode 100644 admin/tool/customlang/tests/behat/export.feature diff --git a/admin/tool/customlang/classes/form/export.php b/admin/tool/customlang/classes/form/export.php new file mode 100644 index 00000000000..843b09a8fda --- /dev/null +++ b/admin/tool/customlang/classes/form/export.php @@ -0,0 +1,68 @@ +. + +/** + * Creates Formular for customlang file export + * + * @package tool_customlang + * @copyright 2020 Thomas Wedekind + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace tool_customlang\form; + +use tool_customlang_utils; + +/** + * Formular for customlang file export + * + * @copyright 2020 Thomas Wedekind + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class export extends \moodleform { + + /** + * Add elements to form + */ + public function definition() { + $lng = $this->_customdata['lng']; + $mform = $this->_form; + + $langdir = tool_customlang_utils::get_localpack_location($lng); + + // The export button only appears if a local lang is present. + if (!check_dir_exists($langdir) || !count(glob("$langdir/*"))) { + print_error('nolocallang', 'tool_customlang'); + } + + $langfiles = scandir($langdir); + $fileoptions = []; + foreach ($langfiles as $file) { + if (substr($file, 0, 1) != '.') { + $fileoptions[$file] = $file; + } + } + + $mform->addElement('hidden', 'lng', $lng); + $mform->setType('lng', PARAM_LANG); + + $select = $mform->addElement('select', 'files', get_string('exportfilter', 'tool_customlang'), $fileoptions); + $select->setMultiple(true); + $mform->addRule('files', get_string('required'), 'required', null, 'client'); + $mform->setDefault('files', $fileoptions); + + $this->add_action_buttons(true, get_string('export', 'tool_customlang')); + } +} diff --git a/admin/tool/customlang/db/access.php b/admin/tool/customlang/db/access.php index 12f07926500..46f3981f5a9 100644 --- a/admin/tool/customlang/db/access.php +++ b/admin/tool/customlang/db/access.php @@ -46,5 +46,14 @@ $capabilities = array( 'manager' => CAP_ALLOW ), ), + /* allows the user to export the current language customization */ + 'tool/customlang:export' => array( + 'riskbitmask' => RISK_CONFIG, + 'captype' => 'read', + 'contextlevel' => CONTEXT_SYSTEM, + 'archetypes' => array( + 'manager' => CAP_ALLOW + ), + ), ); diff --git a/admin/tool/customlang/export.php b/admin/tool/customlang/export.php new file mode 100644 index 00000000000..e6493601c1a --- /dev/null +++ b/admin/tool/customlang/export.php @@ -0,0 +1,68 @@ +. + +/** + * Performs the custom lang export. + * + * @package tool_customlang + * @subpackage customlang + * @copyright 2020 Thomas Wedekind + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require(__DIR__ . '/../../../config.php'); +require_once($CFG->dirroot.'/'.$CFG->admin.'/tool/customlang/locallib.php'); +require_once($CFG->libdir.'/adminlib.php'); + +global $PAGE, $CFG; + +require_login(SITEID, false); +require_capability('tool/customlang:export', context_system::instance()); + +$lng = required_param('lng', PARAM_LANG); + +admin_externalpage_setup('toolcustomlang', '', null, + new moodle_url('/admin/tool/customlang/import.php', ['lng' => $lng])); + +$form = new \tool_customlang\form\export(null, ['lng' => $lng]); + +if ($form->is_cancelled()) { + redirect('index.php'); + die(); +} else if ($formdata = $form->get_data()) { + $tempzip = tempnam($CFG->tempdir . '/', 'tool_customlang_export'); + $filelist = []; + foreach ($formdata->files as $file) { + $filepath = tool_customlang_utils::get_localpack_location($lng). '/' . $file; + if (file_exists($filepath)) { + $filelist[$file] = $filepath; + } + } + $zipper = new zip_packer(); + + if (!empty($filelist) && $zipper->archive_to_pathname($filelist, $tempzip)) { + // Filename include the lang name so the file can be imported with automatic language detection. + send_temp_file($tempzip, "customlang_$lng.zip"); + die(); + } +} + +$output = $PAGE->get_renderer('tool_customlang'); + +echo $output->header(); +echo $output->heading(get_string('pluginname', 'tool_customlang')); +$form->display(); +echo $OUTPUT->footer(); diff --git a/admin/tool/customlang/filter_form.php b/admin/tool/customlang/filter_form.php index 19d74c1c768..6c92e813174 100644 --- a/admin/tool/customlang/filter_form.php +++ b/admin/tool/customlang/filter_form.php @@ -32,7 +32,6 @@ class tool_customlang_filter_form extends moodleform { function definition() { $mform = $this->_form; - $current = $this->_customdata['current']; $mform->addElement('header', 'filtersettings', get_string('filter', 'tool_customlang')); diff --git a/admin/tool/customlang/index.php b/admin/tool/customlang/index.php index 90313296560..d796e8cdcc2 100644 --- a/admin/tool/customlang/index.php +++ b/admin/tool/customlang/index.php @@ -35,6 +35,7 @@ require_capability('tool/customlang:view', context_system::instance()); $action = optional_param('action', '', PARAM_ALPHA); $confirm = optional_param('confirm', false, PARAM_BOOL); $lng = optional_param('lng', '', PARAM_LANG); +$next = optional_param('next', 'edit', PARAM_ALPHA); admin_externalpage_setup('toolcustomlang'); $langs = get_string_manager()->get_list_of_translations(); @@ -59,11 +60,10 @@ if ($action === 'checkout') { raise_memory_limit(MEMORY_EXTRA); tool_customlang_utils::checkout($lng, $progressbar); - echo $output->continue_button(new moodle_url('/admin/tool/customlang/edit.php', array('lng' => $lng)), 'get'); + echo $output->continue_button(new moodle_url("/admin/tool/customlang/{$next}.php", array('lng' => $lng)), 'get'); echo $output->footer(); exit; } - if ($action === 'checkin') { require_sesskey(); require_capability('tool/customlang:edit', context_system::instance()); @@ -133,6 +133,16 @@ if (has_capability('tool/customlang:edit', context_system::instance())) { ); } } +if (has_capability('tool/customlang:export', context_system::instance())) { + $langdir = tool_customlang_utils::get_localpack_location($lng); + if (check_dir_exists(dirname($langdir)) && count(glob("$langdir/*"))) { + $menu['export'] = [ + 'title' => get_string('export', 'tool_customlang'), + 'url' => new moodle_url("/admin/tool/customlang/export.php", ['lng' => $lng]), + 'method' => 'post', + ]; + } +} echo $output->render(new tool_customlang_menu($menu)); echo $output->footer(); diff --git a/admin/tool/customlang/lang/en/tool_customlang.php b/admin/tool/customlang/lang/en/tool_customlang.php index a7375f1f4dc..8ac0c2afd74 100644 --- a/admin/tool/customlang/lang/en/tool_customlang.php +++ b/admin/tool/customlang/lang/en/tool_customlang.php @@ -30,9 +30,19 @@ $string['checkin'] = 'Save strings to language pack'; $string['checkout'] = 'Open language pack for editing'; $string['checkoutdone'] = 'Language pack loaded'; $string['checkoutinprogress'] = 'Loading language pack'; +$string['cliexportfileexists'] = 'File for {$a->lang} already exists, skipping. If you want to overwrite add the --override=true option.'; +$string['cliexportheading'] = 'Starting to export lang files.'; +$string['cliexportnofilefoundforlang'] = 'No file found to export. Skipping export for this language.'; +$string['cliexportfilenotfoundforcomponent'] = 'File {$a->filepath} not found for language {$a->lang}.Skipping this file.'; +$string['cliexportstartexport'] = 'Exporting language "{$a}"'; +$string['cliexportzipdone'] = 'Zip created: {$a}'; +$string['cliexportzipfail'] = 'Cannot create zip {$a}'; $string['confirmcheckin'] = 'You are about to save modifications to your local language pack. This will export the customised strings from the translator into your site data directory and your site will start using the modified strings. Press \'Continue\' to proceed with saving.'; $string['customlang:edit'] = 'Edit local translation'; +$string['customlang:export'] = 'Export local translation'; $string['customlang:view'] = 'View local translation'; +$string['export'] = 'Export custom strings'; +$string['exportfilter'] = 'Select component(s) to export'; $string['filter'] = 'Filter strings'; $string['filtercomponent'] = 'Show strings of these components'; $string['filtercustomized'] = 'Customised only'; @@ -50,6 +60,7 @@ $string['markinguptodate_help'] = 'The customised translation may get outdated i $string['markuptodate'] = 'mark as up-to-date'; $string['modifiedno'] = 'There are no modified strings to save.'; $string['modifiednum'] = 'There are {$a} modified strings. Do you wish to save these changes to your local language pack?'; +$string['nolocallang'] = 'No local strings found.'; $string['nostringsfound'] = 'No strings found, please modify the filter settings'; $string['placeholder'] = 'Placeholders'; $string['placeholder_help'] = 'Placeholders are special statements like `{$a}` or `{$a->something}` within the string. They are replaced with a value when the string is actually printed. diff --git a/admin/tool/customlang/locallib.php b/admin/tool/customlang/locallib.php index 2f02b458f98..5bbb8c2e277 100644 --- a/admin/tool/customlang/locallib.php +++ b/admin/tool/customlang/locallib.php @@ -243,7 +243,7 @@ class tool_customlang_utils { * @param string $lang language code * @return string full path */ - protected static function get_localpack_location($lang) { + public static function get_localpack_location($lang) { global $CFG; return $CFG->langlocalroot.'/'.$lang.'_local'; diff --git a/admin/tool/customlang/tests/behat/customisation_create.feature b/admin/tool/customlang/tests/behat/customisation_create.feature new file mode 100644 index 00000000000..84f71bb8ba9 --- /dev/null +++ b/admin/tool/customlang/tests/behat/customisation_create.feature @@ -0,0 +1,30 @@ +@tool @tool_customlang +Feature: Within a moodle instance, an administrator should be able to modify langstrings for the entire Moodle installation. + In order to change langstrings in the adminsettings of the instance, + As an admin + I need to be able to access and change values in the the language customisation of the language pack. + + Background: + Given I log in as "admin" + And I navigate to "Language > Language customisation" in site administration + And I set the field "lng" to "en" + And I press "Open language pack for editing" + And I press "Continue" + And I set the field "Show strings of these components" to "moodle.php" + And I set the field "String identifier" to "administrationsite" + And I press "Show strings" + And I set the field "core/administrationsite" to "Custom string example" + + @javascript + Scenario: Edit an string but don't save it to lang pack. + When I press "Apply changes and continue editing" + Then I should see "Site administration" in the "page-header" "region" + And I should not see "Custom string example" in the "page-header" "region" + + @javascript + Scenario: Customize an string as admin and save it to lang pack. + Given I press "Save changes to the language pack" + And I should see "There are 1 modified strings." + When I click on "Continue" "button" + Then I should see "Custom string example" in the "page-header" "region" + And I should not see "Site administration" in the "page-header" "region" diff --git a/admin/tool/customlang/tests/behat/export.feature b/admin/tool/customlang/tests/behat/export.feature new file mode 100644 index 00000000000..a42e6722b08 --- /dev/null +++ b/admin/tool/customlang/tests/behat/export.feature @@ -0,0 +1,49 @@ +@tool @tool_customlang +Feature: Within a moodle instance, an administrator should be able to export modified langstrings. + In order to export modified langstrings in the adminsettings of the instance, + As an admin + I need to be able to export the php-files of the language customisation of a language. + + @javascript + Scenario: Export button should not appear if no customization is made + Given I log in as "admin" + And I navigate to "Language > Language customisation" in site administration + And I set the field "lng" to "en" + Then I should see "Open language pack for editing" + And I should not see "Export custom strings" + + @javascript + Scenario: Export button should not appear if no customization is saved into langpack + Given I log in as "admin" + And I navigate to "Language > Language customisation" in site administration + And I set the field "lng" to "en" + And I press "Open language pack for editing" + And I press "Continue" + And I set the field "Show strings of these components" to "moodle.php" + And I set the field "String identifier" to "accept" + And I press "Show strings" + And I set the field "core/accept" to "Accept-custom_export" + When I press "Apply changes and continue editing" + And I navigate to "Language > Language customisation" in site administration + And I set the field "lng" to "en" + Then I should see "Open language pack for editing" + And I should see "There are 1 modified strings." + And I should not see "Export custom strings" + + @javascript + Scenario: Export the php-file including a customised langstring. + Given I log in as "admin" + And I navigate to "Language > Language customisation" in site administration + And I set the field "lng" to "en" + And I press "Open language pack for editing" + And I press "Continue" + And I set the field "Show strings of these components" to "moodle.php" + And I set the field "String identifier" to "accept" + And I press "Show strings" + And I set the field "core/accept" to "Accept-custom_export" + When I press "Save changes to the language pack" + And I should see "There are 1 modified strings." + And I click on "Continue" "button" + Then I set the field "lng" to "en" + And I click on "Export custom strings" "button" + And I set the field "Select component(s) to export" to "moodle.php" diff --git a/admin/tool/customlang/version.php b/admin/tool/customlang/version.php index ac32029a75e..0c13af4a789 100644 --- a/admin/tool/customlang/version.php +++ b/admin/tool/customlang/version.php @@ -25,6 +25,6 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2020061500; +$plugin->version = 2020100101; $plugin->requires = 2020060900; $plugin->component = 'tool_customlang'; // Full name of the plugin (used for diagnostics) From 3540343ceb1f2883475a1bcc16c7ad4ca65ef725 Mon Sep 17 00:00:00 2001 From: Thomas Wedekind Date: Tue, 25 Aug 2020 17:26:07 +0200 Subject: [PATCH 4/4] MDL-69582 tool_customlang: export cli script --- admin/tool/customlang/cli/export.php | 135 +++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 admin/tool/customlang/cli/export.php diff --git a/admin/tool/customlang/cli/export.php b/admin/tool/customlang/cli/export.php new file mode 100644 index 00000000000..29655de7d91 --- /dev/null +++ b/admin/tool/customlang/cli/export.php @@ -0,0 +1,135 @@ +. + +/** + * Export custom language strings to zip files. + * + * @package tool_customlang + * @subpackage customlang + * @copyright 2020 Thomas Wedekind + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('CLI_SCRIPT', true); + +require(__DIR__ . '/../../../../config.php'); +require_once("$CFG->libdir/clilib.php"); +require_once("$CFG->dirroot/$CFG->admin/tool/customlang/locallib.php"); + +$usage = <<tempdir/customlang +-o, --overwrite Overwrite existing files in the target folder. + Note: If the target is not set, the files are always overwritten! +-h, --help Print out this help + +Examples: +Export all custom language files to the default folder: +\$ sudo -u www-data /usr/bin/php admin/tool/customlang/cli/export.php + +Export just the english files of moodle core and the activity 'quiz' in a subfolder in my home folder: +\$ sudo -u www-data /usr/bin/php admin/tool/customlang/cli/export.php --lang='en' --components='moodle,quiz' --target='~/customdir' + +EOF; + +$dafaulttarget = "$CFG->tempdir/customlang/"; + +// Now get cli options. +list($options, $unrecognized) = cli_get_params( + [ + 'lang' => '', + 'components' => '', + 'target' => $dafaulttarget, + 'overwrite' => false, + 'help' => false, + ], + ['h' => 'help', 'c' => 'components', 't' => 'target', 'o' => 'overwrite'] +); + +if ($unrecognized) { + $unrecognized = implode("\n ", $unrecognized); + cli_error(get_string('cliunknowoption', 'admin', $unrecognized)); +} + +if ($options['help']) { + echo $usage; + die; +} +if (!file_exists($options['target'])) { + mkdir($options['target'], 0777, true); +} + +cli_writeln(get_string('cliexportheading', 'tool_customlang')); +$langs = []; +if ($options['lang']) { + $langs = explode(',', $options['lang']); +} else { + // No language set. We export all installed languages. + $langs = array_keys(get_string_manager()->get_list_of_translations(true)); +} + +foreach ($langs as $lang) { + $filename = $options['target'] . get_string('exportzipfilename', 'tool_customlang', ['lang' => $lang]); + // If the file exists and we are not using the temp folder it requires an ovewrite. + if ($options['target'] != $dafaulttarget && file_exists($filename) && !$options['overwrite']) { + cli_problem(get_string('cliexportfileexists', 'tool_customlang', $lang)); + continue; + } + cli_heading(get_string('cliexportstartexport', 'tool_customlang', $lang)); + $langdir = tool_customlang_utils::get_localpack_location($lang); + if (!file_exists($langdir)) { + // No custom files set for this language set. + cli_writeln(get_string('cliexportnofilefoundforlang', 'tool_customlang', ['lang' => $lang])); + continue; + } + $zipper = get_file_packer(); + $tempzip = tempnam($CFG->tempdir . '/', 'tool_customlang_export'); + $filelist = []; + if ($options['components']) { + $components = explode(',', $options['components']); + foreach ($components as $component) { + $filepath = "$langdir/$component.php"; + if (file_exists($filepath)) { + $filelist["$component.php"] = $filepath; + } else { + cli_problem( + get_string('cliexportfilenotfoundforcomponent', 'tool_customlang', ['lang' => $lang, 'file' => $filepath]) + ); + } + } + } else { + $langfiles = scandir($langdir); + foreach ($langfiles as $file) { + if (substr($file, 0, 1) != '.') { + $filelist[$file] = "$langdir/$file"; + } + } + } + if (empty($filelist)) { + cli_problem(get_string('cliexportnofilefoundforlang', 'tool_customlang', ['lang' => $lang])); + continue; + } + if ($zipper->archive_to_pathname($filelist, $filename)) { + cli_writeln(get_string('cliexportzipdone', 'tool_customlang', $filename)); + } else { + cli_problem(get_string('cliexportzipfail', 'tool_customlang', $filename)); + } +}