├── tests ├── fixtures │ ├── mod_new │ │ ├── lang │ │ │ └── en │ │ │ │ └── new.php │ │ ├── version.php │ │ ├── subtype │ │ │ └── subplug │ │ │ │ └── lang │ │ │ │ └── en │ │ │ │ └── newsubtype_subplug.php │ │ └── db │ │ │ └── subplugins.json │ ├── tool_old │ │ ├── lang │ │ │ └── en │ │ │ │ └── tool_old.php │ │ ├── version.php │ │ ├── subtype │ │ │ └── subplug │ │ │ │ └── lang │ │ │ │ └── en │ │ │ │ └── oldsubtype_subplug.php │ │ └── db │ │ │ └── subplugins.php │ ├── merge002.php │ ├── parserdata003.txt │ ├── parserdata004.txt │ ├── parserdata001.txt │ ├── merge001.php │ ├── merge004.php │ ├── merge003.php │ └── parserdata002.txt ├── privacy_provider_test.php ├── source_code_test.php ├── external_plugin_translation_stats_test.php ├── external_stage_translated_string_test.php ├── external_get_string_timeline_test.php ├── git_test.php └── external_get_translator_data_test.php ├── .htaccess ├── .gitignore ├── .mdtconfig ├── scss ├── credits.scss ├── variables.scss ├── filter.scss ├── index.scss ├── comment.scss ├── translator.scss ├── stage.scss └── styles.scss ├── jobs ├── checker-separators ├── checker-pending-contributions ├── en-mergefix ├── install-packs-make ├── zip-packs-publish ├── en-track ├── zip-packs-publish-s3 ├── run ├── en-fixdrift ├── zip-packs-make ├── en-pull └── install-packs-publish ├── package.json ├── amd ├── build │ ├── filter_events.min.js │ ├── translator_events.min.js │ ├── stage.min.js │ ├── filter_events.min.js.map │ ├── translator_events.min.js.map │ └── stage.min.js.map └── src │ ├── filter_events.js │ ├── translator_events.js │ └── stage.js ├── README.md ├── CHANGES.md ├── .travis.yml ├── local_amos.yml ├── Gruntfile.js ├── templates ├── translator.mustache ├── filter_fcmp_option.mustache ├── timeline.mustache ├── translator_paginator.mustache ├── timeline_change.mustache ├── translator_root.mustache ├── stage.mustache ├── downloadpage.mustache └── frontpage.mustache ├── version.php ├── cli ├── enfix-symlinks.sh ├── config-dist.php ├── export-zip.php ├── export-php.php ├── mlangversion.php ├── enfix-export.php ├── backport.php ├── contrib-bulk.php ├── compare-packs.php ├── enfix-merge.php ├── import-strings.php ├── enfix-cleanup.php └── export-installer.php ├── HOWTO-BOOTSTRAP.txt ├── db ├── caches.php ├── tasks.php ├── messages.php ├── services.php └── access.php ├── frontpage.php ├── execute_form.php ├── HOWTO-ENFIX.txt ├── view.php ├── timeline.php ├── stash_form.php ├── classes ├── testcase.php ├── contributor_selector.php ├── maintainer_selector.php ├── task │ └── import_app_strings.php └── external │ ├── get_translator_data.php │ └── plugin_translation_stats.php ├── importfile_form.php ├── admin ├── newlanguage.php └── newlanguage_form.php ├── index.php ├── execute.php ├── untranslate.php ├── settings.php ├── credits.php └── importfile.php /tests/fixtures/mod_new/lang/en/new.php: -------------------------------------------------------------------------------- 1 | // Just a test file 2 | -------------------------------------------------------------------------------- /tests/fixtures/tool_old/lang/en/tool_old.php: -------------------------------------------------------------------------------- 1 | // Just a test file 2 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | ForceType application/x-httpd-php 3 | 4 | -------------------------------------------------------------------------------- /tests/fixtures/mod_new/version.php: -------------------------------------------------------------------------------- 1 | component = 'mod_new'; 4 | -------------------------------------------------------------------------------- /tests/fixtures/tool_old/version.php: -------------------------------------------------------------------------------- 1 | component = 'tool_old'; 4 | -------------------------------------------------------------------------------- /tests/fixtures/mod_new/subtype/subplug/lang/en/newsubtype_subplug.php: -------------------------------------------------------------------------------- 1 | // Just a test file 2 | -------------------------------------------------------------------------------- /tests/fixtures/tool_old/subtype/subplug/lang/en/oldsubtype_subplug.php: -------------------------------------------------------------------------------- 1 | // Just a test file 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | /.patches/ 4 | /phpunit.xml 5 | /cli/config.php 6 | .DS_Store 7 | /node_modules/ 8 | -------------------------------------------------------------------------------- /tests/fixtures/merge002.php: -------------------------------------------------------------------------------- 1 | 'admin/tool/old/subtype', 5 | ]; 6 | -------------------------------------------------------------------------------- /tests/fixtures/mod_new/db/subplugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugintypes": { 3 | "newsubtype": "mod\/new\/subtype" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.mdtconfig: -------------------------------------------------------------------------------- 1 | MDTSITE=lang.moodle.org 2 | MDTVERSION=3.10 3 | MDTCOMPONENT=local_amos 4 | MDTRELPATH=local/amos/ 5 | MDTEXPORT=origin/master 6 | -------------------------------------------------------------------------------- /scss/credits.scss: -------------------------------------------------------------------------------- 1 | #page-local-amos-credits { 2 | .maintainers { 3 | .maintainer { 4 | max-width: 200px; 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /jobs/checker-separators: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Execute the AMOS misconfigured separators checker 4 | # periodically: @daily 5 | 6 | php $AMOSCLIROOT/checker.php --task=separators 7 | -------------------------------------------------------------------------------- /jobs/checker-pending-contributions: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Execute the AMOS pending contributions checker 4 | # periodically: @weekly 5 | 6 | php $AMOSCLIROOT/checker.php --task=pending_contributions 7 | -------------------------------------------------------------------------------- /jobs/en-mergefix: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Remove merged strings from en_fix 4 | # It removes strings from en_fix that are the same as their en originals. 5 | # upstream: en-fixdrift 6 | 7 | php $AMOSCLIROOT/enfix-cleanup.php --execute 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moodle-local_amos", 3 | "devDependencies": { 4 | "grunt": "1.0.1", 5 | "grunt-contrib-uglify": "1.0.1", 6 | "grunt-contrib-watch": "1.0.0", 7 | "grunt-sass": "2.1.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/parserdata003.txt: -------------------------------------------------------------------------------- 1 | and should be parsed'; 6 | $string['valid2'] = 'Multiline 7 | string'; 8 | $string['valid3'] = 'What \$a\'Pe%%\\"be'; // comment 9 | 10 | ?> 11 | -------------------------------------------------------------------------------- /amd/build/filter_events.min.js: -------------------------------------------------------------------------------- 1 | define ("local_amos/filter_events",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b={submit:"".concat("local_amos/filter",":submit")};a.default=b;return a.default}); 2 | //# sourceMappingURL=filter_events.min.js.map 3 | -------------------------------------------------------------------------------- /amd/build/translator_events.min.js: -------------------------------------------------------------------------------- 1 | define ("local_amos/translator_events",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;var b={pagechange:"".concat("local_amos/translator",":pagechange")};a.default=b;return a.default}); 2 | //# sourceMappingURL=translator_events.min.js.map 3 | -------------------------------------------------------------------------------- /tests/fixtures/parserdata004.txt: -------------------------------------------------------------------------------- 1 | and} should be parsed'; 4 | $string['this is not valid'] = 'No space supported'; 5 | $string['4you'] = 'Must start with a letter'; 6 | $string['ev3n_this:is/valid-string.007'] = 'But it does not mean you should make your identifiers like that'; 7 | -------------------------------------------------------------------------------- /jobs/install-packs-make: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Make installer language packages 4 | # Generates string files to be submitted for integration into /install/lang/ folder in moodle.git 5 | # and store them in dataroot/amos/export-install/*/install/lang 6 | # periodically: H 0 * * * 7 | # downstream: install-packs-publish 8 | 9 | php $AMOSCLIROOT/export-installer.php 10 | -------------------------------------------------------------------------------- /jobs/zip-packs-publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Publish ZIP language packages at download.moodle.org 4 | # Uploads new ZIP packages to the download.moodle.org server, together with the updated stats page and md5 checksum file. 5 | # upstream: zip-packs-make 6 | # dowstream: zip-packs-commit 7 | 8 | rsync -e "$AMOSZIPRSYNCRSH" -av $AMOSDATAROOT/export-zip/ $AMOSZIPRSYNCDEST 9 | -------------------------------------------------------------------------------- /jobs/en-track: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Register English string changes in moodle.git 4 | # Reads recent commits in moodle.git. If the commit touches some string file, 5 | # it records the string changes (new, modifed and removed strings). 6 | # If the commit message contains an AMOScript, it is executed. 7 | # upstream: en-pull 8 | # downstream: en-fixdrift 9 | 10 | php $AMOSCLIROOT/parse-core.php 11 | -------------------------------------------------------------------------------- /jobs/zip-packs-publish-s3: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Publish ZIP language packages to a S3 bucket for download.moodle.org 4 | # Uses s3cmd sync to sync the files up to the specified S3 bucket 5 | # requires s3cmd to be configured properly, this will be done at the docker container layer 6 | # upstream: zip-packs-make 7 | # dowstream: zip-packs-commit 8 | 9 | s3cmd sync --delete-removed $AMOSDATAROOT/export-zip/ $AMOSS3BUCKET 10 | -------------------------------------------------------------------------------- /scss/variables.scss: -------------------------------------------------------------------------------- 1 | @import "../../../theme/boost/scss/bootstrap/functions"; 2 | @import "../../../theme/boost/scss/bootstrap/variables"; 3 | @import "../../../theme/boost/scss/bootstrap/mixins"; 4 | 5 | // Background colors indicating the status of the string in the translator. 6 | $amos-color-missing: #f8d7da; 7 | $amos-color-committable: #d4edda; 8 | $amos-color-translatable: #d4edda; 9 | $amos-color-outdated: #fff3cd; 10 | $amos-color-nontranslatable: #f7f7f7; 11 | $amos-color-staged: #d1ecf1; 12 | -------------------------------------------------------------------------------- /jobs/run: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # This is a wrapper to run AMOS jobs from Jenkins. Jenkins is supposed to provide environment variable 4 | # AMOSJOBSROOT with the full path to the folder containing the job scripts. 5 | 6 | if [[ -z $1 ]]; then 7 | echo "Usage: $0 [jobname]" >&2 8 | exit 1; 9 | fi 10 | 11 | JOBPATH=${AMOSJOBSROOT}/$1 12 | 13 | if [[ ! -x $JOBPATH ]]; then 14 | echo "Job $1 not found or not executable" >&2 15 | exit 2; 16 | fi 17 | 18 | sudo -E -H -u ${AMOSRUNAS} $JOBPATH "${@:2}" 19 | -------------------------------------------------------------------------------- /jobs/en-fixdrift: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Fix drift between moodle.git and AMOS 4 | # Sometimes, the English string changes are not parsed correctly (due to non-linearity of Git commits). 5 | # This job checks for differences between the Git and AMOS repositories. 6 | # On detected drift, the status is set to UNSTABLE and e-mail is sent. The drift is then automatically 7 | # fixed so the next execution shoudl be stable again. 8 | # upstream: en-track 9 | # downstream: en-mergefix 10 | 11 | php $AMOSCLIROOT/fix-drift.php --execute 12 | -------------------------------------------------------------------------------- /jobs/zip-packs-make: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Make ZIP language packages 4 | # Generates ZIP packages and MD5 file for the download server at moodledata/amos/export-zip/ 5 | # 0 * * * * 6 | # downstream: zip-packs-publish 7 | 8 | NOW=$(date +%s) 9 | 10 | php $AMOSCLIROOT/export-zip.php --minver=35 && \ 11 | php $AMOSCLIROOT/export-zip.php --minver=27 --maxver=34 && \ 12 | php $AMOSCLIROOT/export-zip.php --maxver=26 && \ 13 | php $AMOSCLIROOT/../../../admin/cli/cfg.php --component=local_amos --name=lastexportzip --set=${NOW} 14 | -------------------------------------------------------------------------------- /amd/build/stage.min.js: -------------------------------------------------------------------------------- 1 | define ("local_amos/stage",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;a.init=function init(){b()};var b=function(){var a=document.getElementById("amosstagestrings");a.addEventListener("click",function(a){if("toggle-diffmode"==a.target.getAttribute("data-action")){a.preventDefault();var b=a.target.closest("[data-region=\"amosstageitem\"]");if("chunks"==b.getAttribute("data-diffmode")){b.setAttribute("data-diffmode","blocks")}else{b.setAttribute("data-diffmode","chunks")}}})}}); 2 | //# sourceMappingURL=stage.min.js.map 3 | -------------------------------------------------------------------------------- /tests/fixtures/parserdata001.txt: -------------------------------------------------------------------------------- 1 | $string['notincodeblock'] = 'This should not be found'; 2 | and} should be parsed'; 19 | $string['valid2'] = 'Multiline 20 | string'; 21 | $string['valid3'] = 'What \$a\'Pe%%\\"be'; // comment 22 | $string[ 'valid4' ] = '$string[\'self\'] = \'Eh?\';' ; 23 | $string['valid5'] = 'First';$string['valid6'] = 'Second'; 24 | 25 | ?> 26 | -------------------------------------------------------------------------------- /tests/fixtures/merge001.php: -------------------------------------------------------------------------------- 1 | . 5 | 6 | /** 7 | * @package plugintype_pluginname 8 | * @subpackage plugintype_pluginname 9 | * @category optional API reference 10 | * @copyright 2013 David Mudrak 11 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 12 | */ 13 | 14 | defined('MOODLE_INTERNAL') || die(); 15 | 16 | // Everything up to the first 17 | // string declaration should stay as it is 18 | 19 | 20 | $string [ 'foo' ] = 'Foo'; 21 | $string['amos'] = 'Sucks'; 22 | $string['abc'] = 'ABC 23 | {$a->def} DEF 24 | GHI '; 25 | $string['amos'] = 'Rock\'s!'; 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Moodle AMOS local plugin 2 | ======================== 3 | 4 | AMOS stands for Automated Manipulation Of Strings. It is a plugin written for LMS 5 | [Moodle](https://moodle.org) to facilitate community translation of the project. The 6 | plugin is installed at portal. It has been written and is 7 | currently maintained by David Mudrák (). 8 | 9 | For more information see . 10 | 11 | To discuss the features please join at forums. 12 | 13 | To report bugs, please use [Moodle tracker](https://tracker.moodle.org/issues/?jql=project%20%3D%20MDLSITE%20AND%20component%20%3D%20lang.moodle.org%20ORDER%20BY%20created%20DESC), project "MDLSITE", component "lang.moodle.org". 14 | -------------------------------------------------------------------------------- /jobs/en-pull: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Pull upstream changes into moodle.git 4 | # Pulls upstream changes into the local clone of moodle.git repository. 5 | # The build status is set to SUCCESS if the local clone has been actually updated 6 | # which happens after weekly builds are released. If there are no actual changes, 7 | # the return status is set to UNSTABLE (so no other consequent builds are executed). 8 | # periodically: H/5 * * * * 9 | # downstream: en-track 10 | 11 | cd $AMOSDATAROOT/repos/moodle 12 | OLDMD5=$(md5sum version.php) 13 | git pull 14 | NEWMD5=$(md5sum version.php) 15 | 16 | if [[ "$OLDMD5" == "$NEWMD5" ]]; then 17 | echo "No changes detected in version.php" 18 | echo "JENKINS:SET-STATUS-UNSTABLE" 19 | else 20 | echo "Changes detected in version.php" 21 | fi 22 | -------------------------------------------------------------------------------- /scss/filter.scss: -------------------------------------------------------------------------------- 1 | // Strings filter at the translator page. 2 | #page-local-amos-view #amosfilter { 3 | 4 | // Default help texts are designed to be shown below the field. We display them below labels - fix the spacing. 5 | label + .form-text { 6 | margin-top: 0; 7 | margin-bottom: 0.25rem; 8 | } 9 | 10 | // Make component and language selectors resizable vertically. 11 | #amosfilter_fcmp, 12 | #amosfilter_flng { 13 | resize: vertical; 14 | } 15 | 16 | // Once a component is selected, highlight it in bold. 17 | #amosfilter_fcmp input[type=checkbox]:checked + label { 18 | font-weight: bold; 19 | } 20 | 21 | // Hide advanced settings in basic mode, unless they are forced to be shown. 22 | &[data-level-showadvanced="0"] [data-level="advanced"]:not([data-level-forceshow]) { 23 | display: none; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ### 3.6.3 ### 2 | 3 | * Currently supported versions are highlighted in green. 4 | * Clicking the "Moodle App" button below the component selector sets the filter so 5 | that only the strings used by the mobile app are selected for translation. 6 | * A new option "latest available version" can be used instead of selecting particular 7 | moodle versions. When selected, the filter picks the most recent version for each of 8 | the selected component. 9 | * A new filtering option "only strings used in the Moodle App" is available. 10 | * Strings used in the Mobile App have a little phone icon displayed. 11 | 12 | Credit goes to Pau Ferrer for contributing these improvements. 13 | 14 | ### 3.6.2 ### 15 | 16 | * Added support for registering the Moodle for Workplace strings 17 | 18 | ### 3.6.1 ### 19 | 20 | * Added support for displaying translation stats at the Moodle plugins directory 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | addons: 4 | postgresql: "9.6" 5 | 6 | services: 7 | - postgresql 8 | - docker 9 | 10 | cache: 11 | directories: 12 | - $HOME/.composer/cache 13 | - $HOME/.npm 14 | 15 | php: 16 | - 7.3 17 | 18 | env: 19 | global: 20 | - MOODLE_BRANCH=MOODLE_310_STABLE DB=pgsql 21 | 22 | before_install: 23 | - phpenv config-rm xdebug.ini 24 | - cd ../.. 25 | - composer create-project -n --no-dev --prefer-dist moodlehq/moodle-plugin-ci ci ^3 26 | - export PATH="$(cd ci/bin; pwd):$(cd ci/vendor/bin; pwd):$PATH" 27 | 28 | install: 29 | - moodle-plugin-ci install 30 | 31 | script: 32 | - moodle-plugin-ci phplint 33 | - moodle-plugin-ci phpcpd 34 | - moodle-plugin-ci phpmd 35 | - moodle-plugin-ci codechecker 36 | - moodle-plugin-ci validate 37 | - moodle-plugin-ci savepoints 38 | - moodle-plugin-ci mustache 39 | - moodle-plugin-ci grunt 40 | - moodle-plugin-ci phpdoc 41 | - moodle-plugin-ci phpunit 42 | - moodle-plugin-ci behat 43 | -------------------------------------------------------------------------------- /local_amos.yml: -------------------------------------------------------------------------------- 1 | # 2 | # AMOS recipe file for https://moodle.org/plugins/tool_pluginskel 3 | # 4 | component: local_amos 5 | name: AMOS 6 | copyright: 2018 David Mudrák 7 | 8 | privacy: 9 | haspersonaldata: true 10 | uselegacypolyfill: false 11 | meta: 12 | dbfields: 13 | amos_commits: 14 | - commitmsg 15 | - timecommitted 16 | - userinfo 17 | amos_translators: 18 | - lang 19 | - status 20 | amos_stashes: 21 | - id 22 | - languages 23 | - components 24 | - strings 25 | - timecreated 26 | - timemodified 27 | - name 28 | - message 29 | amos_contributions: 30 | - lang 31 | - subject 32 | - message 33 | - stashid 34 | - status 35 | - timecreated 36 | - timemodified 37 | subsystems: 38 | - comment 39 | external: 40 | - languagepacks: 41 | - firstname 42 | - lastname 43 | - email 44 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function (grunt) { 4 | 5 | grunt.initConfig({ 6 | watch: { 7 | // Watch for any changes to less files and compile. 8 | files: ["scss/*.scss"], 9 | tasks: ["compile"], 10 | options: { 11 | spawn: false, 12 | livereload: true 13 | } 14 | }, 15 | sass: { 16 | dist: { 17 | options: { 18 | outputStyle: 'compressed', 19 | sourceMap: false 20 | }, 21 | files: { 22 | "styles.css": "scss/styles.scss" 23 | } 24 | } 25 | }, 26 | }); 27 | 28 | // Load contrib tasks. 29 | grunt.loadNpmTasks("grunt-contrib-watch"); 30 | 31 | // Load core tasks. 32 | grunt.loadNpmTasks("grunt-sass"); 33 | 34 | // Register tasks. 35 | grunt.registerTask("default", ["compile"]); 36 | grunt.registerTask("compile", ["sass"]); 37 | }; 38 | -------------------------------------------------------------------------------- /amd/src/filter_events.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - https://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * AMOS filter related events. 18 | * 19 | * @module local_amos/filter_events 20 | * @package local_amos 21 | * @copyright 2020 David Mudrák 22 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | let prefix = 'local_amos/filter'; 26 | 27 | export default { 28 | submit: `${prefix}:submit`, 29 | }; 30 | -------------------------------------------------------------------------------- /templates/translator.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/translator 19 | 20 | Render the AMOS Translator widget and initialize the module. 21 | }} 22 | 23 |
24 | {{>local_amos/translator_root}} 25 |
26 | 27 | {{#js}} 28 | require(['local_amos/translator'], function(translatorjs) { 29 | translatorjs.init(); 30 | }); 31 | {{/js}} 32 | 33 | -------------------------------------------------------------------------------- /amd/src/translator_events.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - https://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * AMOS translator related events. 18 | * 19 | * @module local_amos/translator_events 20 | * @package local_amos 21 | * @copyright 2020 David Mudrák 22 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | let prefix = 'local_amos/translator'; 26 | 27 | export default { 28 | pagechange: `${prefix}:pagechange`, 29 | }; 30 | -------------------------------------------------------------------------------- /amd/build/filter_events.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/filter_events.js"],"names":["submit"],"mappings":"gJA0Be,CACXA,MAAM,yCADK,C","sourcesContent":["// This file is part of Moodle - https://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * AMOS filter related events.\n *\n * @module local_amos/filter_events\n * @package local_amos\n * @copyright 2020 David Mudrák \n * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nlet prefix = 'local_amos/filter';\n\nexport default {\n submit: `${prefix}:submit`,\n};\n"],"file":"filter_events.min.js"} -------------------------------------------------------------------------------- /tests/fixtures/merge004.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Strings for component 'block_mnet_hosts', language 'en_fix', branch 'MOODLE_25_STABLE' 20 | * 21 | * @package block_mnet_hosts 22 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $string['mnet_hosts:myaddinstance'] = 'Add a new network servers block to My home'; 29 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Defines the version of AMOS 19 | * 20 | * @package local_amos 21 | * @copyright 2010 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | $plugin->component = 'local_amos'; 28 | $plugin->version = 2020112700; 29 | $plugin->release = '2020.11'; 30 | $plugin->maturity = MATURITY_STABLE; 31 | $plugin->requires = 2020061500; 32 | -------------------------------------------------------------------------------- /amd/build/translator_events.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/translator_events.js"],"names":["pagechange"],"mappings":"oJA0Be,CACXA,UAAU,iDADC,C","sourcesContent":["// This file is part of Moodle - https://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * AMOS translator related events.\n *\n * @module local_amos/translator_events\n * @package local_amos\n * @copyright 2020 David Mudrák \n * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nlet prefix = 'local_amos/translator';\n\nexport default {\n pagechange: `${prefix}:pagechange`,\n};\n"],"file":"translator_events.min.js"} -------------------------------------------------------------------------------- /cli/enfix-symlinks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [[ "$2" == "" ]]; then 4 | echo "Usage: $0 " 5 | echo 6 | echo "Example:" 7 | echo "# cd /home/mudrd8mz/public_html/moodle25" 8 | echo "# mkdir tmp" 9 | echo "# $0 \$(pwd) tmp" 10 | exit 11 | fi 12 | 13 | DIRROOT=$1 14 | SYMLINKSDIR=$2 15 | 16 | if [[ ! -d $DIRROOT ]]; then 17 | echo "Moodle root directory not found!" 18 | exit 19 | fi 20 | 21 | if [[ ! -f $DIRROOT/config-dist.php ]]; then 22 | echo "Does not seem to be Moodle root directory!" 23 | exit 24 | fi 25 | 26 | if [[ ! -d $SYMLINKSDIR ]]; then 27 | echo "Target directory for symlinks not found!" 28 | exit 29 | fi 30 | 31 | ########################################### 32 | 33 | pushd $SYMLINKSDIR 34 | 35 | for f in $(find $DIRROOT -wholename '*/lang/en/*.php' -not -wholename '*/tests/fixtures/*'); do 36 | if [[ $f == $DIRROOT/install/* ]]; then 37 | echo Skipping $f 38 | else 39 | ln -s $f $(basename $f) 40 | fi 41 | done 42 | 43 | popd 44 | 45 | echo "Done! Now you may want to run something like:" 46 | echo "# php enfix-merge.php --symlinksdir=/home/mudrd8mz/public_html/moodle25/tmp --enfixdir=/home/mudrd8mz/tmp/enfix/2.5" 47 | -------------------------------------------------------------------------------- /scss/index.scss: -------------------------------------------------------------------------------- 1 | #page-local-amos-index { 2 | 3 | h2 { 4 | text-align: left; 5 | } 6 | 7 | .amostools { 8 | text-align: center; 9 | margin-bottom: 30px; 10 | 11 | .amostool { 12 | margin: 5px; 13 | display: inline-block; 14 | background-image: url([[pix:core|t/edit]]); 15 | background-repeat: no-repeat; 16 | background-position: center 15px; 17 | background-size: 24px 24px; 18 | padding-top: 50px; 19 | } 20 | 21 | .amostool-translator { 22 | background-image: url([[pix:core|t/editstring]]); 23 | } 24 | 25 | .amostool-stage { 26 | background-image: url([[pix:core|i/scheduled]]); 27 | } 28 | 29 | .amostool-stashes { 30 | background-image: url([[pix:core|i/repository]]); 31 | } 32 | 33 | .amostool-contributions { 34 | background-image: url([[pix:core|i/export]]); 35 | } 36 | 37 | .amostool-log { 38 | background-image: url([[pix:core|i/groups]]); 39 | } 40 | 41 | .amostool-credits { 42 | background-image: url([[pix:core|i/badge]]); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /HOWTO-BOOTSTRAP.txt: -------------------------------------------------------------------------------- 1 | HOW TO DEPLOY AMOS AND RELOAD THE HISTORY OF STRINGS 2 | ==================================================== 3 | 4 | 1. Install /local/amos as normal subplugin 5 | 2. Copy /local/amos/cli/config-dist.php to config.php and modify if needed 6 | 3. Prepare git repositories of Moodle core and Moodle lang packs in locations 7 | defined in config.php 8 | 4. Run cli/parse-core.php to get English strings into database 9 | 5. Run cli/parse-lang.php to get translations into database 10 | 6. Modify cli/parse-core.php and uncomment XXX'ed code to hard code the 11 | startat commit hash 12 | 7. Re-run cli/parse-core.php to execute AMOS scripts from recent commits 13 | 8. Run cli/rev-clean.php --full to delete orphaned strings 14 | 15 | HOW TO REPOPULATE THE HISTORY AGAIN 16 | =================================== 17 | 18 | Reinstall the plugin via Moodle interface to cleanup DB tables and run 19 | 20 | $ crontab -e 21 | $ rm /var/www/data/moodle-amos/amos/var/* 22 | $ php cli/parse-core.php && php cli/parse-lang.php && echo 61bb07c2573ec711a0e5d1ccafa313cf47b9fc22 > /var/www/data/moodle-amos/amos/var/MOODLE_20_STABLE.startat && php cli/parse-core.php && php cli/rev-clean.php --full 23 | $ crontab -e 24 | 25 | HOW TO KEEP AMOS DATA UP-TO-UPDATE 26 | ================================== 27 | 28 | 1. git pull both repositories 29 | 2. Run cli/parse-core.php 30 | 3. Run cli/parse-lang.php 31 | 4. Run cli/rev-clean.php (from time to time, run with --full) 32 | -------------------------------------------------------------------------------- /db/caches.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Defines the MUC caches used by AMOS. 19 | * 20 | * @package local_amos 21 | * @category cache 22 | * @copyright 2020 David Mudrák 23 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $definitions = [ 29 | 'lists' => [ 30 | 'mode' => cache_store::MODE_APPLICATION, 31 | 'simplekeys' => true, 32 | 'simpledata' => false, 33 | 'staticacceleration' => true, 34 | ], 35 | 'stats' => [ 36 | 'mode' => cache_store::MODE_APPLICATION, 37 | 'simplekeys' => true, 38 | 'simpledata' => false, 39 | 'staticacceleration' => true, 40 | ], 41 | ]; 42 | -------------------------------------------------------------------------------- /frontpage.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Displays the lang.moodle.org front page content 19 | * 20 | * @package local_amos 21 | * @copyright 2012 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 28 | 29 | $statsman = new local_amos_stats_manager(); 30 | 31 | [ 32 | 'contributedstrings' => $contributedstrings, 33 | 'listcontributors' => $listcontributors, 34 | ] = $statsman->frontpage_contribution_stats(); 35 | 36 | echo $OUTPUT->render_from_template('local_amos/frontpage', [ 37 | 'langpackstats' => $statsman->get_language_pack_ratio_stats(), 38 | 'contributedstrings' => $contributedstrings, 39 | 'listcontributors' => $listcontributors, 40 | ]); 41 | -------------------------------------------------------------------------------- /db/tasks.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Scheduled tasks provided by AMOS are declared here. 19 | * 20 | * @package local_amos 21 | * @category task 22 | * @copyright 2019 David Mudrák 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $tasks = [ 29 | [ 30 | 'classname' => '\local_amos\task\import_workplace_plugins', 31 | 'blocking' => 0, 32 | 'minute' => 'R', 33 | 'hour' => 'R', 34 | 'day' => '*', 35 | 'month' => '*', 36 | 'dayofweek' => '*', 37 | ], 38 | [ 39 | 'classname' => '\local_amos\task\import_app_strings', 40 | 'blocking' => 0, 41 | 'minute' => 'R', 42 | 'hour' => 'R', 43 | 'day' => '*', 44 | 'month' => '*', 45 | 'dayofweek' => '*', 46 | ], 47 | ]; 48 | -------------------------------------------------------------------------------- /db/messages.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Defines messages providers for AMOS 19 | * 20 | * @package local_amos 21 | * @category message 22 | * @copyright 2014 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $messageproviders = array( 29 | // Results of the {@link amos_checker} checks for the maintainers. 30 | 'checker' => array( 31 | 'capability' => 'local/amos:commit', 32 | 'defaults' => array( 33 | 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF, 34 | ), 35 | ), 36 | 37 | // Activity related to the contributed translations (workflow, comments). 38 | 'contribution' => array( 39 | 'defaults' => array( 40 | 'email' => MESSAGE_PERMITTED + MESSAGE_DEFAULT_LOGGEDIN + MESSAGE_DEFAULT_LOGGEDOFF, 41 | ), 42 | ), 43 | ); 44 | -------------------------------------------------------------------------------- /tests/fixtures/merge003.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Strings for component 'block_mnet_hosts', language 'en', branch 'MOODLE_20_STABLE' 20 | * 21 | * @package block_mnet_hosts 22 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | $string['error_authmnetneeded'] = 'MNet authentication plugin must be enabled to see the list of MNet network servers'; 27 | $string['error_localusersonly'] = 'Remote users can not jump to other MNet network servers from this host'; 28 | $string['error_roamcapabilityneeded'] = 'Users need the capability \'Roam to a remote application via MNet\' to see the list of MNet network servers'; 29 | $string['mnet_hosts:addinstance'] = 'Add a new network servers block'; 30 | $string['mnet_hosts:myaddinstance'] = 'Add a new network servers block to \'My home\''; 31 | $string['pluginname'] = 'Network servers'; 32 | $string['server'] = 'Server'; 33 | -------------------------------------------------------------------------------- /amd/src/stage.js: -------------------------------------------------------------------------------- 1 | // This file is part of Moodle - https://moodle.org/ 2 | // 3 | // Moodle is free software: you can redistribute it and/or modify 4 | // it under the terms of the GNU General Public License as published by 5 | // the Free Software Foundation, either version 3 of the License, or 6 | // (at your option) any later version. 7 | // 8 | // Moodle is distributed in the hope that it will be useful, 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | // GNU General Public License for more details. 12 | // 13 | // You should have received a copy of the GNU General Public License 14 | // along with Moodle. If not, see . 15 | 16 | /** 17 | * JS module for the AMOS stage. 18 | * 19 | * @module local_amos/stage 20 | * @package local_amos 21 | * @copyright 2020 David Mudrák 22 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | /** 26 | * @function init 27 | */ 28 | export const init = () => { 29 | registerEventListeners(); 30 | }; 31 | 32 | /** 33 | * @function registerEventListeners 34 | */ 35 | const registerEventListeners = () => { 36 | let root = document.getElementById('amosstagestrings'); 37 | 38 | root.addEventListener('click', e => { 39 | if (e.target.getAttribute('data-action') == 'toggle-diffmode') { 40 | e.preventDefault(); 41 | let item = e.target.closest('[data-region="amosstageitem"]'); 42 | 43 | if (item.getAttribute('data-diffmode') == 'chunks') { 44 | item.setAttribute('data-diffmode', 'blocks'); 45 | } else { 46 | item.setAttribute('data-diffmode', 'chunks'); 47 | } 48 | } 49 | }); 50 | }; 51 | -------------------------------------------------------------------------------- /execute_form.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package local 20 | * @subpackage amos 21 | * @copyright 2011 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir.'/formslib.php'); 28 | 29 | class local_amos_execute_form extends moodleform { 30 | 31 | public function definition() { 32 | $mform =& $this->_form; 33 | 34 | $mform->addElement('select', 'version', get_string('version', 'local_amos'), $this->_customdata['versions']); 35 | $mform->setDefault('version', $this->_customdata['versioncurrent']); 36 | $mform->addRule('version', null, 'required', null, 'client'); 37 | 38 | $mform->addElement('textarea', 'script', get_string('script', 'local_amos'), array('cols' => 60, 'rows' => 10)); 39 | $mform->setDefault('script', "AMOS BEGIN\n \nAMOS END"); 40 | $mform->setType('script', PARAM_RAW); 41 | $this->add_action_buttons(false, get_string('scriptexecute', 'local_amos')); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /HOWTO-ENFIX.txt: -------------------------------------------------------------------------------- 1 | HOWTO Prepare branches with en_fix strings for integration 2 | ========================================================== 3 | 4 | To make it easier for the community to participate on fixing/rewording strings in the English language pack for standard 5 | components (core and standard plugins), the en_fix language pack is created in AMOS. Every two months before the release, strings 6 | accumulated in AMOS are submitted for integration and merged into moodle.git. When they become part of moodle.git, strings are 7 | removed from the en_fix language pack in AMOS. This article describes how to prepare en_fix branches to be submitted for 8 | integration. 9 | 10 | 1. Create (clone) an issue in the tracker to cover this task. 11 | 12 | 13 | 14 | 2. Run enfix-export.php at the AMOS server and download data generated by it. 15 | 16 | # cd ~/tmp 17 | # ssh -t access-eu.srv.in.moodle.com 'kubectl --context production exec -it -c app $(~/bin/podname lang.moodle.org) -- /bin/bash -c "cd /opt/app/local/amos/cli/ && sudo -u www-data php enfix-export.php && cd /opt/data/amos && tar czf /tmp/export-enfix.tgz export-enfix && ls -al /tmp/export-enfix.tgz" && kubectl --context production cp -c app $(~/bin/podname lang.moodle.org):/tmp/export-enfix.tgz ~/export-enfix.tgz' && rsync -avP access-eu.srv.in.moodle.com:~/export-enfix.tgz . && tar xf export-enfix.tgz 18 | 19 | 3. Go to your mdk moodle.git checkout and make a branch to be submitted for integration. 20 | 21 | # cd /path/to/moodle/git/clone 22 | # mdk fix MDL-xxxx enfix 23 | 24 | 4. Apply the changes to the string files and push them to the tracker. 25 | 26 | # mdk run enfix 27 | 28 | 5. Repeat steps 3 and 4 for supported STABLE branches. 29 | -------------------------------------------------------------------------------- /templates/filter_fcmp_option.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/filter_fcmp_option 19 | 20 | Render filter component selector's component line. 21 | }} 22 | 23 | 24 | 25 |
26 | 28 | 29 |
30 | 31 | 32 | {{since}} 33 | {{typename}} 34 | {{#hasstdvernote}} 35 | {{stdvernote}} 36 | {{/hasstdvernote}} 37 | {{#app}} 38 | {{#str}} componentsapp, local_amos {{/str}} 39 | {{/app}} 40 | 41 | 42 | -------------------------------------------------------------------------------- /templates/timeline.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/timeline 19 | 20 | Render the string translation timeline. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * attributes Array of name / value pairs. 30 | 31 | Example context (json): 32 | { 33 | "attributes": [ 34 | { "name": "src", "value": "http://moodle.com/wp-content/themes/moodle/images/logo-hat2.png" }, 35 | { "name": "class", "value": "iconsmall" } 36 | ] 37 | } 38 | }} 39 | 40 |
41 | {{#changes}} 42 |
43 |
44 | {{#english}} 45 | {{>local_amos/timeline_change}} 46 | {{/english}} 47 |
48 |
49 | {{#translation}} 50 | {{>local_amos/timeline_change}} 51 | {{/translation}} 52 |
53 |
54 | {{/changes}} 55 |
56 | -------------------------------------------------------------------------------- /view.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Main AMOS translation page 20 | * 21 | * Displays strings filter and the translation table. 22 | * 23 | * @package local-amos 24 | * @copyright 2010 David Mudrak 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | 28 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 29 | require_once(dirname(__FILE__).'/locallib.php'); 30 | 31 | require_login(SITEID, false); 32 | require_capability('local/amos:stage', context_system::instance()); 33 | 34 | $PAGE->set_pagelayout('standard'); 35 | $PAGE->set_url('/local/amos/view.php'); 36 | $PAGE->set_title('AMOS ' . get_string('translatortool', 'local_amos')); 37 | $PAGE->set_heading('AMOS ' . get_string('translatortool', 'local_amos')); 38 | 39 | $output = $PAGE->get_renderer('local_amos'); 40 | 41 | $filter = new \local_amos\output\filter($PAGE->url); 42 | 43 | // Make sure that $USER contains the sesskey property. 44 | sesskey(); 45 | 46 | $translator = new \local_amos\output\translator($filter, $USER); 47 | 48 | echo $output->header(); 49 | echo $output->render($filter); 50 | echo $output->render($translator); 51 | echo $output->footer(); 52 | -------------------------------------------------------------------------------- /templates/translator_paginator.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/translator_paginator 19 | 20 | Template purpose and description. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * attributes Array of name / value pairs. 30 | 31 | Example context (json): 32 | { 33 | "attributes": [ 34 | { "name": "src", "value": "http://moodle.com/wp-content/themes/moodle/images/logo-hat2.png" }, 35 | { "name": "class", "value": "iconsmall" } 36 | ] 37 | } 38 | }} 39 | 40 |
41 | {{#hasmultiplepages}} 42 | {{#navigation}} 43 | 44 | {{#islink}} 45 | {{{label}}} 46 | {{/islink}} 47 | {{^islink}} 48 | {{{label}}} 49 | {{/islink}} 50 | 51 | {{/navigation}} 52 | {{/hasmultiplepages}} 53 |
54 | -------------------------------------------------------------------------------- /timeline.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Displays a timeline history of a given string 19 | * 20 | * @package local 21 | * @subpackage amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | require(__DIR__ . '/../../config.php'); 27 | 28 | require_login(SITEID, false); 29 | require_capability('local/amos:stage', context_system::instance()); 30 | 31 | $component = required_param('component', PARAM_ALPHANUMEXT); 32 | $stringid = required_param('stringid', PARAM_STRINGID); 33 | $language = required_param('language', PARAM_ALPHANUMEXT); 34 | 35 | $PAGE->set_url('/local/amos/timeline.php'); 36 | $PAGE->set_pagelayout('standard'); 37 | $PAGE->set_context(context_system::instance()); 38 | 39 | $timeline = \local_amos\external\get_string_timeline::execute($component, $stringid, $language); 40 | $timeline = external_api::clean_returnvalue(\local_amos\external\get_string_timeline::execute_returns(), $timeline); 41 | 42 | $PAGE->set_heading(get_string('timelineheading', 'local_amos', $timeline)); 43 | 44 | $output = $PAGE->get_renderer('local_amos'); 45 | 46 | echo $output->header(); 47 | echo $output->render_from_template('local_amos/timeline', $timeline); 48 | echo $output->footer(); 49 | -------------------------------------------------------------------------------- /stash_form.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Moodle forms used by stash page 20 | * 21 | * @package local-amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | require_once($CFG->dirroot . '/lib/formslib.php'); 29 | 30 | class local_amos_submit_form extends moodleform { 31 | 32 | function definition() { 33 | $mform = $this->_form; 34 | 35 | $mform->addElement('header', 'general', get_string('stashsubmitdetails', 'local_amos')); 36 | 37 | $mform->addElement('text', 'name', get_string('stashsubmitsubject', 'local_amos'), array('size'=>50, 'maxlength'=>255)); 38 | $mform->addRule('name', null, 'required', null, 'client'); 39 | $mform->setType('name', PARAM_RAW); 40 | 41 | $mform->addElement('textarea', 'message', get_string('stashsubmitmessage', 'local_amos'), array('cols'=>80, 'rows'=>10)); 42 | $mform->setType('message', PARAM_RAW); 43 | 44 | $mform->addElement('hidden', 'stashid'); 45 | $mform->setType('stashid', PARAM_INT); 46 | 47 | $mform->addElement('submit', 'submit', get_string('stashsubmit', 'local_amos')); 48 | $mform->addElement('cancel', 'cancel', get_string('cancel')); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /classes/testcase.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | defined('MOODLE_INTERNAL') || die(); 18 | 19 | global $CFG; 20 | 21 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 22 | 23 | /** 24 | * Base class for AMOS advanced test cases providing some useful utility methods. 25 | * 26 | * @package local_amos 27 | * @category test 28 | * @copyright 2020 David Mudrák 29 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 | */ 31 | abstract class local_amos_testcase extends advanced_testcase { 32 | 33 | /** 34 | * Register a language on the given branch. 35 | * 36 | * @param string $langcode the code of the language, such as 'en' 37 | * @param int $since the code of the branch to register the language at. 38 | */ 39 | protected function register_language($langcode, $since) { 40 | 41 | $stage = new mlang_stage(); 42 | 43 | $component = new mlang_component('langconfig', $langcode, mlang_version::by_code($since)); 44 | $component->add_string(new mlang_string('thislanguage', $langcode)); 45 | $component->add_string(new mlang_string('thislanguageint', $langcode)); 46 | $stage->add($component); 47 | $component->clear(); 48 | 49 | $stage->commit('Register language ' . $langcode, array('source' => 'unittest')); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /templates/timeline_change.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/timeline_change 19 | 20 | Render info about a particular string change. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * attributes Array of name / value pairs. 30 | 31 | Example context (json): 32 | { 33 | "attributes": [ 34 | { "name": "src", "value": "http://moodle.com/wp-content/themes/moodle/images/logo-hat2.png" }, 35 | { "name": "class", "value": "iconsmall" } 36 | ] 37 | } 38 | }} 39 | 40 | {{#hascontent}} 41 |
42 |
43 |
{{displaysince}} | {{langcode}} | {{displaydate}}
44 |
{{userinfo}}
45 |
{{commitmsg}}
46 |
{{commitsource}}
47 | {{#hascommithash}} 48 |
49 | {{commithash}} 50 |
51 | {{/hascommithash}} 52 |
53 |
54 | {{{displaytext}}} 55 |
56 |
57 | {{/hascontent}} 58 | -------------------------------------------------------------------------------- /importfile_form.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package local 20 | * @subpackage amos 21 | * @copyright 2010 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | defined('MOODLE_INTERNAL') || die(); 26 | 27 | require_once($CFG->libdir.'/formslib.php'); 28 | 29 | class local_amos_importfile_form extends moodleform { 30 | function definition() { 31 | $mform =& $this->_form; 32 | 33 | $mform->addElement('select', 'version', get_string('version', 'local_amos'), $this->_customdata['versions']); 34 | $mform->setDefault('version', $this->_customdata['versioncurrent']); 35 | $mform->addRule('version', null, 'required', null, 'client'); 36 | 37 | $mform->addElement('select', 'language', get_string('language', 'local_amos'), $this->_customdata['languages']); 38 | $mform->setDefault('language', $this->_customdata['languagecurrent']); 39 | $mform->addRule('language', null, 'required', null, 'client'); 40 | 41 | $fpoptions = array('accepted_types' => array('.php', '.zip'), 'maxbytes' => 2*1024*1024); 42 | $mform->addElement('filepicker', 'importfile', get_string('file'), null, $fpoptions); 43 | $mform->addRule('importfile', null, 'required', null, 'client'); 44 | 45 | $this->add_action_buttons(false, get_string('import')); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /scss/comment.scss: -------------------------------------------------------------------------------- 1 | // Contribution comments 2 | 3 | $borderColor: rgb(223, 223, 223); 4 | $baseFontSize: $font-size-base; 5 | $fontSizeSmall: $font-size-sm; 6 | 7 | #page-local-amos-contrib { 8 | 9 | .comment-area { 10 | max-width: 100%; 11 | } 12 | 13 | .commentswrapper { 14 | width: 100%; 15 | border: none; 16 | 17 | .comment-ctrl .comment-list li { 18 | // This is here to disable the ugly animation effect when a new 19 | // comment is added (the effect sets the element style="" directly) 20 | color: inherit !important; 21 | background-color: inherit !important; 22 | } 23 | 24 | .amos-comment { 25 | font-size: $baseFontSize; 26 | .comment-comment { 27 | border: 1px solid $borderColor; 28 | @include border-radius(0 0 10px 10px); 29 | padding: 1em; 30 | ul li, li { 31 | list-style-type: disc; 32 | padding: 0; 33 | } 34 | ol li { 35 | list-style-type: decimal; 36 | padding: 0; 37 | } 38 | .comment-delete { 39 | right: 6px; 40 | top: 3px; 41 | } 42 | } 43 | .comment-header { 44 | padding: 5px; 45 | border-top: 1px solid $borderColor; 46 | border-left: 1px solid $borderColor; 47 | border-right: 1px solid $borderColor; 48 | @include border-radius(10px 10px 0 0); 49 | background-color: lighten($borderColor, 11%); 50 | display: block; 51 | } 52 | .comment-userpicture, .comment-userfullname, .comment-time { 53 | display: inline-block; 54 | margin-right: 1em; 55 | } 56 | .comment-time { 57 | font-size: $fontSizeSmall; 58 | } 59 | } 60 | 61 | .amos-comment.highlighted .comment-comment { 62 | background-color: lighten($borderColor, 11%); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cli/config-dist.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Configuration of this AMOS installation 20 | * 21 | * @package local_amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') or die(); 27 | 28 | /** 29 | * Full path to the git clone of Moodle. Used by parse-core.php 30 | */ 31 | define('AMOS_REPO_MOODLE', $CFG->dataroot . '/amos/repos/moodle'); 32 | 33 | /** 34 | * Default start-at commit used mainly during the development 35 | */ 36 | define('AMOS_REPO_MOODLE_ROOT', '2e35f0aa005d07298b575f77f7cde56149103fe0'); 37 | 38 | /** 39 | * Full path to the git clone of legacy language translation files. 40 | * User by parse-lang.php to get the history of strings into database. 41 | */ 42 | define('AMOS_REPO_LANGS', $CFG->dataroot . '/amos/repos/moodle-lang'); 43 | 44 | /** 45 | * Full path to the directory where AMOS will generate language packs 46 | */ 47 | define('AMOS_EXPORT_DIR', $CFG->dataroot . '/amos/export'); 48 | 49 | /** 50 | * Full path to the directory where AMOS will generate installer strings 51 | */ 52 | define('AMOS_EXPORT_INSTALLER_DIR', $CFG->dataroot . '/amos/export-install'); 53 | 54 | /** 55 | * S3 bucket where AMOS can write to 56 | * Does not have a default option as no default makes sense 57 | */ 58 | define('AMOSS3BUCKET', ''); 59 | 60 | /** 61 | * Full path to git 62 | */ 63 | define('AMOS_PATH_GIT', '/usr/local/bin/git'); 64 | -------------------------------------------------------------------------------- /cli/export-zip.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Generate ZIP language packs for publishing 19 | * 20 | * @package local_amos 21 | * @subpackage cli 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | define('CLI_SCRIPT', true); 27 | 28 | require(__DIR__ . '/../../../config.php'); 29 | require_once($CFG->libdir . '/clilib.php'); 30 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 31 | 32 | $usage = "Generate ZIP language packs for publishing. 33 | 34 | Usage: 35 | $ php export-zip.php [--minver=] [--maxver=] 36 | 37 | Options: 38 | --minver= Generate only versions greater or equal than given code. 39 | --maxver= Generate only versions lower or equal than given code. 40 | 41 | Example: 42 | $ php export-zip.php --minver=400 43 | "; 44 | 45 | [$options, $unrecognised] = cli_get_params([ 46 | 'minver' => null, 47 | 'maxver' => null, 48 | 'help' => false, 49 | ], [ 50 | 'h' => 'help', 51 | ]); 52 | 53 | if ($unrecognised) { 54 | cli_error(get_string('cliunknowoption', 'core_admin', implode(PHP_EOL . ' ', $unrecognised))); 55 | } 56 | 57 | if ($options['help']) { 58 | cli_writeln($usage); 59 | exit(2); 60 | } 61 | 62 | $logger = new amos_cli_logger(); 63 | 64 | $exporter = new amos_export_zip($logger); 65 | $exporter->init($options['minver'], $options['maxver']); 66 | $exporter->rebuild_zip_packages(); 67 | $exporter->rebuild_output_folders(); 68 | -------------------------------------------------------------------------------- /cli/export-php.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Exports the most recent version of Moodle strings into Moodle PHP format 19 | * 20 | * @package local_amos 21 | * @copyright 2010 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | define('CLI_SCRIPT', true); 26 | 27 | require(__DIR__ . '/../../../config.php'); 28 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 30 | require_once($CFG->libdir . '/clilib.php'); 31 | 32 | $usage = "Export component strings into Moodle PHP language file format. 33 | 34 | Usage: 35 | # php export-php.php --component= --language= --version= 36 | # php export-php.php [--help|-h] 37 | "; 38 | 39 | list($options, $unrecognised) = cli_get_params([ 40 | 'help' => false, 41 | 'component' => null, 42 | 'language' => null, 43 | 'version' => null, 44 | ], [ 45 | 'h' => 'help' 46 | ]); 47 | 48 | if ($unrecognised) { 49 | $unrecognised = implode(PHP_EOL . ' ', $unrecognised); 50 | cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised)); 51 | } 52 | 53 | if ($options['help']) { 54 | cli_writeln($usage); 55 | exit(2); 56 | } 57 | 58 | if ($options['component'] === null || $options['language'] === null || $options['version'] === null) { 59 | cli_writeln($usage); 60 | exit(3); 61 | } 62 | 63 | mlang_component::from_snapshot($options['component'], $options['language'], mlang_version::by_code($options['version'])) 64 | ->export_phpfile('php://stdout'); 65 | -------------------------------------------------------------------------------- /cli/mlangversion.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides CLI access to {@see mlang_version} class features. 19 | * 20 | * @package local_amos 21 | * @category cli 22 | * @copyright 2020 David Mudrák 23 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | define('CLI_SCRIPT', true); 27 | 28 | require(__DIR__ . '/../../../config.php'); 29 | require_once($CFG->libdir . '/clilib.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | 32 | $usage = "Display information about the given version. 33 | 34 | Usage: 35 | $ php mlangversion.php --value= --type= --get= 36 | 37 | Example: 38 | $ php mlangversion.php --value=3.10 --type=dir --get=branch 39 | "; 40 | 41 | [$options, $unrecognised] = cli_get_params([ 42 | 'value' => '', 43 | 'type' => '', 44 | 'get' => '', 45 | 'help' => false, 46 | ], [ 47 | 'h' => 'help', 48 | ]); 49 | 50 | if ($unrecognised) { 51 | $unrecognised = implode(PHP_EOL . ' ', $unrecognised); 52 | cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised)); 53 | } 54 | 55 | if ($options['help']) { 56 | cli_writeln($usage); 57 | exit(2); 58 | } 59 | 60 | if (!in_array($options['type'], ['code', 'dir', 'branch'])) { 61 | cli_error('Invalid --type option value', 3); 62 | } 63 | 64 | if (!in_array($options['get'], ['code', 'dir', 'branch', 'label', 'translatable', 'current'])) { 65 | cli_error('Invalid --get option value', 3); 66 | } 67 | 68 | $methodname = 'by_' . $options['type']; 69 | $propertyname = $options['get']; 70 | 71 | cli_writeln(mlang_version::$methodname($options['value'])->$propertyname); 72 | -------------------------------------------------------------------------------- /admin/newlanguage.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Register new language 20 | * 21 | * @package local-amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | require_once(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php'); 27 | require_once(dirname(dirname(__FILE__)).'/mlanglib.php'); 28 | require_once(dirname(__FILE__).'/newlanguage_form.php'); 29 | 30 | require_login(SITEID, false); 31 | require_capability('local/amos:manage', context_system::instance()); 32 | 33 | $cache = cache::make('local_amos', 'lists'); 34 | $cache->delete('languages'); 35 | 36 | $PAGE->set_pagelayout('standard'); 37 | $PAGE->set_url('/local/amos/admin/newlanguage.php'); 38 | $PAGE->set_title('AMOS ' . get_string('newlanguage', 'local_amos')); 39 | $PAGE->set_heading('AMOS ' . get_string('newlanguage', 'local_amos')); 40 | 41 | $form = new local_amos_newlanguage_form(); 42 | 43 | if ($data = $form->get_data()) { 44 | $component = new mlang_component('langconfig', $data->langcode, mlang_version::oldest_version()); 45 | $data->langname = mlang_string::fix_syntax($data->langname); 46 | $data->langnameint = mlang_string::fix_syntax($data->langnameint); 47 | $component->add_string(new mlang_string('thislanguage', $data->langname)); 48 | $component->add_string(new mlang_string('thislanguageint', $data->langnameint)); 49 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 50 | $stage->add($component); 51 | $stage->store(); 52 | redirect(new moodle_url('/local/amos/stage.php')); 53 | } 54 | 55 | /// Output starts here 56 | echo $OUTPUT->header(); 57 | $form->display(); 58 | echo $OUTPUT->footer(); 59 | -------------------------------------------------------------------------------- /amd/build/stage.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../src/stage.js"],"names":["init","registerEventListeners","root","document","getElementById","addEventListener","e","target","getAttribute","preventDefault","item","closest","setAttribute"],"mappings":"sIA2BoB,QAAPA,CAAAA,IAAO,EAAM,CACtBC,CAAsB,EACzB,C,CAKD,GAAMA,CAAAA,CAAsB,CAAG,UAAM,CACjC,GAAIC,CAAAA,CAAI,CAAGC,QAAQ,CAACC,cAAT,CAAwB,kBAAxB,CAAX,CAEAF,CAAI,CAACG,gBAAL,CAAsB,OAAtB,CAA+B,SAAAC,CAAC,CAAI,CAChC,GAA4C,iBAAxC,EAAAA,CAAC,CAACC,MAAF,CAASC,YAAT,CAAsB,aAAtB,CAAJ,CAA+D,CAC3DF,CAAC,CAACG,cAAF,GACA,GAAIC,CAAAA,CAAI,CAAGJ,CAAC,CAACC,MAAF,CAASI,OAAT,CAAiB,iCAAjB,CAAX,CAEA,GAA0C,QAAtC,EAAAD,CAAI,CAACF,YAAL,CAAkB,eAAlB,CAAJ,CAAoD,CAChDE,CAAI,CAACE,YAAL,CAAkB,eAAlB,CAAmC,QAAnC,CACH,CAFD,IAEO,CACHF,CAAI,CAACE,YAAL,CAAkB,eAAlB,CAAmC,QAAnC,CACH,CACJ,CACJ,CAXD,CAYH,C","sourcesContent":["// This file is part of Moodle - https://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * JS module for the AMOS stage.\n *\n * @module local_amos/stage\n * @package local_amos\n * @copyright 2020 David Mudrák \n * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * @function init\n */\nexport const init = () => {\n registerEventListeners();\n};\n\n/**\n * @function registerEventListeners\n */\nconst registerEventListeners = () => {\n let root = document.getElementById('amosstagestrings');\n\n root.addEventListener('click', e => {\n if (e.target.getAttribute('data-action') == 'toggle-diffmode') {\n e.preventDefault();\n let item = e.target.closest('[data-region=\"amosstageitem\"]');\n\n if (item.getAttribute('data-diffmode') == 'chunks') {\n item.setAttribute('data-diffmode', 'blocks');\n } else {\n item.setAttribute('data-diffmode', 'chunks');\n }\n }\n });\n};\n"],"file":"stage.min.js"} -------------------------------------------------------------------------------- /scss/translator.scss: -------------------------------------------------------------------------------- 1 | // Strings translator. 2 | #page-local-amos-view #amostranslator { 3 | 4 | opacity: 1; 5 | transition: opacity 0.2s; 6 | 7 | &.loading { 8 | opacity: 0; 9 | } 10 | 11 | .amostranslatoritem { 12 | 13 | // Undo right margin on help icons. 14 | .amosinfo .icon { 15 | margin-right: 0; 16 | } 17 | 18 | .amosinfo { 19 | margin-bottom: 0.25rem; 20 | } 21 | 22 | .amosoriginal, 23 | .amostranslation { 24 | flex: 1 1 auto; 25 | min-height: 3rem; 26 | } 27 | 28 | .amosoriginal, 29 | .amostranslation, 30 | .amostranslationedit textarea { 31 | border: 1px solid rgb(204,204,204); 32 | border-radius: 4px; 33 | } 34 | 35 | .amosoriginal, 36 | .amostranslationview { 37 | padding: 10px; 38 | white-space: pre-wrap; 39 | word-wrap: break-word; 40 | } 41 | 42 | .amostranslationedit { 43 | padding: 4px; 44 | } 45 | 46 | .amostranslationedit textarea { 47 | padding: 5px; 48 | }; 49 | 50 | .amostranslationview { 51 | cursor: text; 52 | } 53 | 54 | .amostranslationview, 55 | .amostranslationedit, 56 | .amostranslationedit textarea { 57 | height: 100%; 58 | width: 100%; 59 | } 60 | 61 | &.translatable .amostranslation { 62 | background-color: $amos-color-translatable; 63 | } 64 | 65 | &.translatable.missing .amostranslation { 66 | background-color: $amos-color-missing; 67 | } 68 | 69 | &.translatable.outdated .amostranslation { 70 | background-color: $amos-color-outdated; 71 | } 72 | 73 | &.translatable.staged .amostranslation { 74 | background-color: $amos-color-staged !important; 75 | } 76 | 77 | &.translatable.outdated .amostranslation { 78 | background-color: $amos-color-outdated; 79 | } 80 | 81 | &.nontranslatable .amostranslation { 82 | background-color: $amos-color-nontranslatable; 83 | font-style: italic; 84 | color: #545454; 85 | } 86 | 87 | &[data-mode="view"] .amostranslationedit { 88 | display: none !important; 89 | } 90 | 91 | &[data-mode="edit"] .amostranslationview { 92 | display: none !important; 93 | } 94 | } 95 | 96 | .amospaginator { 97 | .paginatoritem.current { 98 | font-weight: bold; 99 | background-color: #eee; 100 | } 101 | } 102 | } 103 | 104 | -------------------------------------------------------------------------------- /tests/privacy_provider_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides the {@link local_amos_privacy_provider_testcase} class. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2018 David Mudrák 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $CFG; 29 | 30 | /** 31 | * Unit tests for the {@link \local_amos\privacy\provider} class. 32 | * 33 | * @copyright 2018 David Mudrák 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class local_amos_privacy_provider_testcase extends advanced_testcase { 37 | 38 | /** 39 | * Test {@link \local_amos\privacy\provider::get_users_in_context()} implementation. 40 | */ 41 | public function test_get_users_in_context() { 42 | global $DB; 43 | $this->resetAfterTest(); 44 | 45 | $context = context_system::instance(); 46 | 47 | $userlist = new \core_privacy\local\request\userlist($context, 'local_amos'); 48 | 49 | $u1 = $this->getDataGenerator()->create_user(); 50 | $u2 = $this->getDataGenerator()->create_user(); 51 | $u3 = $this->getDataGenerator()->create_user(); 52 | 53 | $DB->insert_record('amos_commits', [ 54 | 'source' => 'test', 55 | 'timecommitted' => time(), 56 | 'commitmsg' => 'Dummy commit', 57 | 'commithash' => sha1('foo bar'), 58 | 'userid' => $u1->id, 59 | 'userinfo' => 'Test User1 ', 60 | ]); 61 | 62 | $DB->insert_record('amos_translators', [ 63 | 'userid' => $u2->id, 64 | 'lang' => 'cs', 65 | 'status' => 0, 66 | ]); 67 | 68 | \local_amos\privacy\provider::get_users_in_context($userlist); 69 | 70 | $expected = [$u1->id, $u2->id]; 71 | $actual = $userlist->get_userids(); 72 | $this->assertEqualsCanonicalizing($expected, $actual); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /admin/newlanguage_form.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * New language form 20 | * 21 | * @package local_amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | require_once($CFG->dirroot.'/lib/formslib.php'); 29 | 30 | class local_amos_newlanguage_form extends moodleform { 31 | 32 | public function definition() { 33 | $mform = $this->_form; 34 | 35 | $mform->addElement('header', 'compulsory', 'New language'); 36 | 37 | $mform->addElement('text', 'langcode', 'Language code', ['size' => 5]); 38 | $mform->setType('langcode', PARAM_SAFEDIR); 39 | $mform->addRule('langcode', get_string('err_required', 'form'), 'required', null, 'client'); 40 | 41 | $mform->addElement('text', 'langname', 'Language name in the language itself'); 42 | $mform->setType('langname', PARAM_RAW); 43 | $mform->addRule('langname', get_string('err_required', 'form'), 'required', null, 'client'); 44 | 45 | $mform->addElement('text', 'langnameint', 'International language name in English'); 46 | $mform->setType('langnameint', PARAM_RAW); 47 | $mform->addRule('langnameint', get_string('err_required', 'form'), 'required', null, 'client'); 48 | 49 | $this->add_action_buttons(false, 'Register new language'); 50 | } 51 | 52 | public function validation($data, $files) { 53 | global $CFG, $DB; 54 | 55 | $errors = parent::validation($data, $files); 56 | 57 | $tempcode = clean_param($data['langcode'], PARAM_SAFEDIR); 58 | $tempcode = strtolower($tempcode); 59 | if ($tempcode !== $data['langcode']) { 60 | $errors['langcode'] = 'Invalid language code format'; 61 | } 62 | 63 | if ($DB->record_exists('amos_translations', ['lang' => $data['langcode']])) { 64 | $errors['langcode'] = 'Already exists'; 65 | } 66 | 67 | return $errors; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /classes/contributor_selector.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * @package local_amos 19 | * @copyright 2014 David Mudrak 20 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 21 | */ 22 | 23 | defined('MOODLE_INTERNAL') || die(); 24 | 25 | require_once($CFG->dirroot.'/user/selector/lib.php'); 26 | 27 | class local_amos_contributor_selector extends user_selector_base { 28 | 29 | /** 30 | * Constructor. 31 | * 32 | * @param string $name 33 | * @param array $options 34 | */ 35 | public function __construct($name, $options = array()) { 36 | $options['accesscontext'] = context_system::instance(); 37 | $options['multiselect'] = false; 38 | $options['extrafields'] = array('email'); 39 | parent::__construct($name, $options); 40 | } 41 | 42 | public function find_users($search) { 43 | global $DB; 44 | 45 | list($searchsql, $searchparams) = $this->search_sql($search, 'u'); 46 | list($sortsql, $sortparams) = users_order_by_sql('u', $search, context_system::instance()); 47 | 48 | $fields = "SELECT ".$this->required_fields_sql('u'); 49 | $countfields = "SELECT COUNT(*)"; 50 | 51 | $sql = " FROM {user} u 52 | WHERE ${searchsql}"; 53 | 54 | $order = " ORDER BY ${sortsql}"; 55 | 56 | if (!$this->is_validating()) { 57 | $foundcount = $DB->count_records_sql($countfields.$sql, array_merge($searchparams)); 58 | if ($foundcount > $this->maxusersperpage) { 59 | return $this->too_many_results($search, $foundcount); 60 | } 61 | } 62 | 63 | $found = $DB->get_records_sql($fields.$sql.$order, array_merge($searchparams, $sortparams)); 64 | 65 | if (empty($found)) { 66 | return array(); 67 | } 68 | 69 | if ($search) { 70 | $groupname = get_string('potusersmatching', 'core_role', $search); 71 | } else { 72 | $groupname = get_string('potusers', 'core_role'); 73 | } 74 | 75 | return array($groupname => $found); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /templates/translator_root.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/translator_root 19 | 20 | Render the content of the AMOS translator. 21 | }} 22 | 23 |
24 |
25 |
26 | 33 |
34 |
35 | {{#paginator}} 36 | {{>local_amos/translator_paginator}} 37 | {{/paginator}} 38 |
39 |
40 |
41 | 42 | {{#strings}} 43 | {{>local_amos/translator_string}} 44 | {{/strings}} 45 | 46 |
47 |
48 |
49 | 55 |
56 |
57 | {{#paginator}} 58 | {{>local_amos/translator_paginator}} 59 | {{/paginator}} 60 |
61 |
62 |
63 | 64 | -------------------------------------------------------------------------------- /cli/enfix-export.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Exports strings from the en_fix language pack 20 | * 21 | * @package local_amos 22 | * @copyright 2013 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | define('CLI_SCRIPT', true); 27 | 28 | require(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->libdir . '/clilib.php'); 30 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 31 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 32 | 33 | cli_error('TODO - this needs to be updated to use the new storage system.'); 34 | 35 | // Get an information about existing strings in the en_fix 36 | $sql = "SELECT branch,lang,component,COUNT(stringid) AS numofstrings 37 | FROM {amos_repository} 38 | WHERE deleted=0 39 | AND lang='en_fix' 40 | GROUP BY branch,lang,component 41 | ORDER BY branch,lang,component"; 42 | $rs = $DB->get_recordset_sql($sql); 43 | $tree = array(); // [branch][language][component] => numofstrings 44 | foreach ($rs as $record) { 45 | $tree[$record->branch][$record->lang][$record->component] = $record->numofstrings; 46 | } 47 | $rs->close(); 48 | 49 | if (!defined('AMOS_EXPORT_ENFIX_DIR')) { 50 | cli_error('Target directory AMOS_EXPORT_ENFIX_DIR not defined!'); 51 | } 52 | 53 | remove_dir(AMOS_EXPORT_ENFIX_DIR, true); 54 | 55 | foreach ($tree as $vercode => $languages) { 56 | $version = mlang_version::by_code($vercode); 57 | foreach ($languages as $langcode => $components) { 58 | if ($langcode !== 'en_fix') { 59 | throw new coding_exception('Unexpected language'); 60 | } 61 | foreach ($components as $componentname => $unused) { 62 | $component = mlang_component::from_snapshot($componentname, $langcode, $version); 63 | if ($component->has_string()) { 64 | $file = AMOS_EXPORT_ENFIX_DIR.'/'.$version->dir.'/'.$component->name.'.php'; 65 | if (!file_exists(dirname($file))) { 66 | mkdir(dirname($file), 0755, true); 67 | } 68 | echo "$file\n"; 69 | $component->export_phpfile($file); 70 | } 71 | $component->clear(); 72 | } 73 | } 74 | } 75 | echo "DONE\n"; 76 | -------------------------------------------------------------------------------- /classes/maintainer_selector.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * @package local_amos 19 | * @copyright 2014 David Mudrak 20 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 21 | */ 22 | 23 | defined('MOODLE_INTERNAL') || die(); 24 | 25 | require_once($CFG->dirroot.'/user/selector/lib.php'); 26 | 27 | class local_amos_maintainer_selector extends user_selector_base { 28 | 29 | /** 30 | * Constructor. 31 | * 32 | * @param string $name 33 | * @param array $options 34 | */ 35 | public function __construct($name, $options = array()) { 36 | $options['accesscontext'] = context_system::instance(); 37 | $options['multiselect'] = false; 38 | $options['extrafields'] = array('email'); 39 | parent::__construct($name, $options); 40 | } 41 | 42 | public function find_users($search) { 43 | global $DB; 44 | 45 | $available = get_users_by_capability(context_system::instance(), 'local/amos:commit', 'u.id'); 46 | 47 | list($permsql, $permparams) = $DB->get_in_or_equal(array_keys($available), SQL_PARAMS_NAMED, 'permparam'); 48 | list($searchsql, $searchparams) = $this->search_sql($search, 'u'); 49 | list($sortsql, $sortparams) = users_order_by_sql('u', $search, context_system::instance()); 50 | 51 | $fields = "SELECT ".$this->required_fields_sql('u'); 52 | $countfields = "SELECT COUNT(*)"; 53 | 54 | $sql = " FROM {user} u 55 | WHERE u.id ${permsql} AND ${searchsql}"; 56 | 57 | $order = " ORDER BY ${sortsql}"; 58 | 59 | if (!$this->is_validating()) { 60 | $foundcount = $DB->count_records_sql($countfields.$sql, array_merge($permparams, $searchparams)); 61 | if ($foundcount > $this->maxusersperpage) { 62 | return $this->too_many_results($search, $foundcount); 63 | } 64 | } 65 | 66 | $found = $DB->get_records_sql($fields.$sql.$order, array_merge($permparams, $searchparams, $sortparams)); 67 | 68 | if (empty($found)) { 69 | return array(); 70 | } 71 | 72 | if ($search) { 73 | $groupname = get_string('potusersmatching', 'core_role', $search); 74 | } else { 75 | $groupname = get_string('potusers', 'core_role'); 76 | } 77 | 78 | return array($groupname => $found); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * AMOS home page 20 | * 21 | * @package local-amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 27 | require_once(dirname(__FILE__).'/locallib.php'); 28 | 29 | require_login(SITEID, false); 30 | 31 | $PAGE->set_pagelayout('standard'); 32 | $PAGE->set_url('/local/amos/index.php'); 33 | $PAGE->set_title('AMOS'); 34 | $PAGE->set_heading('AMOS'); 35 | 36 | $output = $PAGE->get_renderer('local_amos'); 37 | 38 | /// Output starts here 39 | echo $output->header(); 40 | 41 | $amosroot = $PAGE->navigation->get('amos_root'); 42 | 43 | echo html_writer::start_div('amostools'); 44 | 45 | foreach ($amosroot->get_children_key_list() as $childkey) { 46 | $child = $amosroot->get($childkey); 47 | if (!($child->action instanceof moodle_url)) { 48 | continue; 49 | } 50 | echo html_writer::div( 51 | html_writer::link( 52 | $child->action, 53 | $child->text, 54 | array('class' => 'btn btn-primary btn-large') 55 | ), 56 | 'well amostool amostool-'.$child->key 57 | ); 58 | } 59 | 60 | echo html_writer::end_div(); // .amostools 61 | 62 | echo $output->heading(get_string('privileges', 'local_amos')); 63 | $caps = array(); 64 | if (has_capability('local/amos:manage', context_system::instance())) { 65 | $caps[] = get_string('amos:manage', 'local_amos'); 66 | } 67 | if (has_capability('local/amos:stage', context_system::instance())) { 68 | $caps[] = get_string('amos:stage', 'local_amos'); 69 | } 70 | if (has_capability('local/amos:commit', context_system::instance())) { 71 | $allowed = mlang_tools::list_allowed_languages(); 72 | if (empty($allowed)) { 73 | $allowed = get_string('committablenone', 'local_amos'); 74 | } elseif (!empty($allowed['X'])) { 75 | $allowed = get_string('committableall', 'local_amos'); 76 | } else { 77 | $allowed = implode(', ', $allowed); 78 | } 79 | $caps[] = get_string('amos:commit', 'local_amos') . ' (' . $allowed . ')'; 80 | } 81 | if (empty($caps)) { 82 | get_string('privilegesnone', 'local_amos'); 83 | } else { 84 | $caps = '
  • ' . implode("
  • \n
  • ", $caps) . '
  • '; 85 | echo html_writer::tag('ul', $caps); 86 | } 87 | 88 | echo $output->footer(); 89 | -------------------------------------------------------------------------------- /tests/source_code_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides {@link local_amos_source_code_testcase} class. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2019 David Mudrák 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $CFG; 29 | 30 | /** 31 | * Test the implementation of {@link \local_amos\local\source_code} class. 32 | * 33 | * @copyright 2019 David Mudrák 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class local_amos_source_code_testcase extends advanced_testcase { 37 | 38 | /** 39 | * Test {@link \local_amos\local\source_code::parse_version_php()}. 40 | */ 41 | public function test_get_version_php() { 42 | global $CFG; 43 | 44 | $amos = new \local_amos\local\source_code($CFG->dirroot.'/local/amos'); 45 | $info = $amos->get_version_php(); 46 | 47 | $this->assertEquals('local_amos', $info['component']); 48 | $this->assertArrayHasKey('version', $info); 49 | $this->assertArrayHasKey('release', $info); 50 | $this->assertArrayHasKey('requires', $info); 51 | $this->assertArrayHasKey('maturity', $info); 52 | } 53 | 54 | /** 55 | * Test {@link \local_amos\local\source_code::get_included_string_files()}. 56 | */ 57 | public function test_get_included_string_files() { 58 | 59 | // Admin tool with the old subplugins.php file. 60 | $toolold = new \local_amos\local\source_code(__DIR__.'/fixtures/tool_old'); 61 | $files = $toolold->get_included_string_files(); 62 | 63 | $this->assertStringStartsWith('// Just a test file', $files['tool_old']['lang/en/tool_old.php']); 64 | $this->assertStringStartsWith('// Just a test file', 65 | $files['oldsubtype_subplug']['subtype/subplug/lang/en/oldsubtype_subplug.php']); 66 | 67 | // Activity module with the new subplugins.json file. 68 | $foonew = new \local_amos\local\source_code(__DIR__.'/fixtures/mod_new'); 69 | $files = $foonew->get_included_string_files(); 70 | 71 | $this->assertStringStartsWith('// Just a test file', $files['mod_new']['lang/en/new.php']); 72 | $this->assertStringStartsWith('// Just a test file', 73 | $files['newsubtype_subplug']['subtype/subplug/lang/en/newsubtype_subplug.php']); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /execute.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Executes the passed AMOScript and stages the result 20 | * 21 | * @package local 22 | * @subpackage amos 23 | * @copyright 2011 David Mudrak 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 28 | require_once(dirname(__FILE__).'/locallib.php'); 29 | require_once(dirname(__FILE__).'/mlanglib.php'); 30 | require_once(dirname(__FILE__).'/execute_form.php'); 31 | 32 | require_login(SITEID, false); 33 | require_capability('local/amos:execute', context_system::instance()); 34 | require_capability('local/amos:stage', context_system::instance()); 35 | 36 | $PAGE->set_pagelayout('standard'); 37 | $PAGE->set_url('/local/amos/execute.php'); 38 | navigation_node::override_active_url(new moodle_url('/local/amos/stage.php')); 39 | $PAGE->set_title('AMOS ' . get_string('scriptexecute', 'local_amos')); 40 | $PAGE->set_heading('AMOS ' . get_string('scriptexecute', 'local_amos')); 41 | 42 | $executeform = new local_amos_execute_form(null, local_amos_execute_options()); 43 | 44 | if ($data = $executeform->get_data()) { 45 | $version = mlang_version::by_code($data->version); 46 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 47 | $instructions = mlang_tools::extract_script_from_text($data->script); 48 | if (!empty($instructions)) { 49 | foreach ($instructions as $instruction) { 50 | $changes = mlang_tools::execute($instruction, $version); 51 | if ($changes instanceof mlang_stage) { 52 | foreach ($changes as $component) { 53 | $stage->add($component, true); 54 | } 55 | $changes->clear(); 56 | } elseif ($changes < 0) { 57 | throw new moodle_exception('error_during_amoscript_execution', 'local_amos', '', null, $changes); 58 | } 59 | unset($changes); 60 | } 61 | } 62 | $stage->rebase(); 63 | $stage->store(); 64 | 65 | if (!isset($SESSION->local_amos)) { 66 | $SESSION->local_amos = new stdClass(); 67 | } 68 | $SESSION->local_amos->presetcommitmessage = $data->script; 69 | } 70 | 71 | if (!isset($stage) or !$stage->has_component()) { 72 | notice(get_string('nothingtostage', 'local_amos'), new moodle_url('/local/amos/stage.php')); 73 | } 74 | 75 | redirect(new moodle_url('/local/amos/stage.php')); 76 | -------------------------------------------------------------------------------- /classes/task/import_app_strings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides the {@link \local_amos\task\import_app_strings} class. 19 | * 20 | * @package local_amos 21 | * @category task 22 | * @copyright 2019 Pau Ferrer Ocaña 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | namespace local_amos\task; 27 | 28 | defined('MOODLE_INTERNAL') || die(); 29 | 30 | require_once($CFG->dirroot.'/local/amos/mlanglib.php'); 31 | 32 | /** 33 | * Imports the strings used in the Moodle apps. 34 | * 35 | * @copyright 2019 Pau Ferrer Ocaña 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class import_app_strings extends \core\task\scheduled_task { 39 | 40 | /** @var \local_amos\local\git client to use */ 41 | protected $git; 42 | 43 | /** 44 | * Return the task name. 45 | * 46 | * @return string 47 | */ 48 | public function get_name() { 49 | return 'Import Moodle Apps strings'; 50 | } 51 | 52 | /** 53 | * Execute the task. 54 | */ 55 | public function execute() { 56 | global $DB; 57 | 58 | $langindexfile = get_config('local_amos', 'applangindexfile'); 59 | mtrace('Loading langindex from '.$langindexfile); 60 | $file = file_get_contents($langindexfile); 61 | $strings = json_decode($file, true); 62 | 63 | $records = array(); 64 | foreach ($strings as $key => $value) { 65 | if ($value != 'local_moodlemobileapp') { 66 | $record = new \StdClass(); 67 | $record->appid = $key; 68 | $exp = explode('/', $value, 2); 69 | $record->component = $exp[0]; 70 | if (count($exp) == 2) { 71 | $record->stringid = $exp[1]; 72 | } else { 73 | $exp = explode('.', $key, 3); 74 | 75 | if (count($exp) == 3) { 76 | $record->stringid = $exp[2]; 77 | } else { 78 | $record->stringid = $exp[1]; 79 | } 80 | } 81 | $records[$key] = $record; 82 | } 83 | } 84 | 85 | if (count($records) > 0) { 86 | $DB->delete_records('amos_app_strings'); 87 | $DB->insert_records('amos_app_strings', $records); 88 | 89 | mtrace(count($records) .' app strings inserted'); 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /classes/external/get_translator_data.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | namespace local_amos\external; 18 | 19 | defined('MOODLE_INTERNAL') || die(); 20 | 21 | global $CFG; 22 | require_once($CFG->libdir . '/externallib.php'); 23 | 24 | /** 25 | * Provide data for the AMOS translator based on the provided filter settings. 26 | * 27 | * @package local_amos 28 | * @category external 29 | * @copyright 2020 David Mudrák 30 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 31 | */ 32 | class get_translator_data extends \external_api { 33 | 34 | /** 35 | * Describe the external function parameters. 36 | * 37 | * @return \external_function_parameters 38 | */ 39 | public static function execute_parameters(): \external_function_parameters { 40 | 41 | return new \external_function_parameters([ 42 | 'filterquery' => new \external_value(PARAM_RAW, 'Query string representing the filter form data.'), 43 | ]); 44 | } 45 | 46 | /** 47 | * Execute the external function. 48 | * 49 | * @param string $filterquery 50 | * @return array 51 | */ 52 | public static function execute(string $filterquery): array { 53 | global $USER, $PAGE; 54 | 55 | $context = \context_system::instance(); 56 | self::validate_context($context); 57 | require_capability('local/amos:stage', $context); 58 | 59 | ['filterquery' => $filterquery] = self::validate_parameters(self::execute_parameters(), compact('filterquery')); 60 | 61 | // Parse the query string and inject it as if it was part of HTTP POST request which is what the filter expects. 62 | parse_str($filterquery, $_POST); 63 | 64 | $filter = new \local_amos\output\filter(new \moodle_url('/local/amos/view.php')); 65 | 66 | // Make sure that $USER contains the sesskey property. 67 | sesskey(); 68 | $translator = new \local_amos\output\translator($filter, $USER); 69 | 70 | return [ 71 | 'json' => json_encode($translator->export_for_template($PAGE->get_renderer('local_amos'))), 72 | ]; 73 | } 74 | 75 | /** 76 | * Describe the external function result value. 77 | * 78 | * @return \external_description 79 | */ 80 | public static function execute_returns(): \external_description { 81 | 82 | return new \external_single_structure([ 83 | 'json' => new \external_value(PARAM_RAW, 'JSON encoded template data'), 84 | ]); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /cli/backport.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * CLI utility to backport translations to older branches. 19 | * 20 | * @package local_amos 21 | * @copyright 2020 David Mudrák 22 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | define('CLI_SCRIPT', true); 26 | 27 | require(__DIR__ . '/../../../config.php'); 28 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 30 | require_once($CFG->libdir.'/clilib.php'); 31 | 32 | $usage = "Automatically backport translations to lower versions if they apply. 33 | 34 | A typical use case is when plugin English strings are registered on version X and translated. And then the X - 1 version 35 | is registered. Without backporting, that translations would exist since X only. But we want the translations of 36 | identical strings be backported from X to X -1 automatically. 37 | 38 | Backporting does not happen on 'en' and 'en_fix' languages. 39 | 40 | It is implicitly executed on committing in AMOS UI and whem importing English strings via external API. This script can 41 | be used to run it explicitly. 42 | 43 | Usage: 44 | php backport.php [--component=] [--languages=] 45 | 46 | Options: 47 | --component Backport translations for the given component only. Defaults to all known components. 48 | --languages Backport translations for the given languages only. Defaults to all languages. 49 | "; 50 | 51 | list($options, $unrecognised) = cli_get_params([ 52 | 'component' => '', 53 | 'languages' => '', 54 | 'help' => false, 55 | ], [ 56 | 'h' => 'help', 57 | ]); 58 | 59 | if ($options['help'] || !empty($unrecognised)) { 60 | cli_writeln($usage); 61 | exit(2); 62 | } 63 | 64 | $logger = new amos_cli_logger(); 65 | 66 | $logger->log('backport', 'Loading list of components ...', amos_cli_logger::LEVEL_DEBUG); 67 | 68 | $components = array_keys(mlang_tools::list_components()); 69 | $languages = []; 70 | 71 | if ($options['component']) { 72 | $components = array_intersect($components, [$options['component']]); 73 | } 74 | 75 | if ($options['languages']) { 76 | $languages = explode(',', $options['languages']); 77 | } 78 | 79 | $count = count($components); 80 | $i = 1; 81 | 82 | foreach ($components as $component) { 83 | $logger->log('backport', sprintf('%d%% %d/%d backporting %s translations ...', 84 | floor($i / $count * 100), $i, $count, $component)); 85 | mlang_tools::backport_translations($component, $languages); 86 | $i++; 87 | } 88 | 89 | $logger->log('backport', 'Done!'); 90 | -------------------------------------------------------------------------------- /scss/stage.scss: -------------------------------------------------------------------------------- 1 | // Styles used at the stage page. 2 | 3 | .path-local-amos #amosstagewrapper { 4 | 5 | .stagetool { 6 | margin-bottom: 10px; 7 | 8 | &.simple { 9 | margin-bottom: 20px; 10 | } 11 | 12 | .stagetool-title { 13 | font-size: larger; 14 | background: url([[pix:core|t/expanded]]) no-repeat scroll 0 center; 15 | padding-left: 20px; 16 | cursor: pointer; 17 | color: $link-color; 18 | text-decoration: $link-decoration; 19 | 20 | &:hover { 21 | color: $link-hover-color; 22 | text-decoration: $link-hover-decoration; 23 | } 24 | } 25 | 26 | .collapsed .stagetool-title { 27 | background-image: url([[pix:core|t/collapsed]]); 28 | } 29 | 30 | .stagetool-content { 31 | padding-left: 1.3rem; 32 | } 33 | 34 | &.commit { 35 | label.checkbox { 36 | margin-bottom: 5px; 37 | } 38 | label.checkbox input[type="checkbox"] { 39 | margin-top: 4px; // Undo the dodgy hack from theme/bootstrapbase/less/moodle/forms.less 40 | margin-right: 0; 41 | } 42 | } 43 | 44 | &.stageactions { 45 | .btn { 46 | margin: 2px; 47 | } 48 | } 49 | } 50 | 51 | #amosstagestrings { 52 | margin-top: 2rem; 53 | 54 | .amosoriginal, .amostranslation { 55 | flex: 1 1 auto; 56 | min-height: 3rem; 57 | border: 1px solid rgb(204,204,204); 58 | border-radius: 4px; 59 | padding: 10px; 60 | white-space: pre-wrap; 61 | word-wrap: break-word; 62 | } 63 | 64 | .amosstageitem.uncommittable .amostranslation { 65 | background-color: $amos-color-nontranslatable; 66 | } 67 | 68 | .amosstageitem.committable .amostranslation { 69 | background-color: $amos-color-committable; 70 | } 71 | 72 | .amosstageitem.committable.nodiff .amostranslation { 73 | background-color: white; 74 | } 75 | 76 | .amosstageitem.diff[data-diffmode="chunks"] .amostranslation { 77 | .translation-new, .translation-current { 78 | display: none; 79 | } 80 | } 81 | 82 | .amosstageitem.diff[data-diffmode="blocks"] .amostranslation { 83 | .translation-diff { 84 | display: none; 85 | } 86 | } 87 | 88 | .amosstageitem.diff { 89 | &[data-diffmode="chunks"] .amostranslation del, 90 | &[data-diffmode="blocks"] .amostranslation .translation-current { 91 | color: #6b6b6b; 92 | text-decoration: line-through double; 93 | } 94 | 95 | &[data-diffmode="chunks"] .amostranslation ins, 96 | &[data-diffmode="blocks"] .amostranslation .translation-new { 97 | color: #008000; 98 | font-weight: bold; 99 | text-decoration: underline dotted; 100 | } 101 | } 102 | 103 | .amosstageitem:not([class~="diff"]) [data-action="toggle-diffmode"] { 104 | display: none; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/external_plugin_translation_stats_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides class {@see local_amos_external_plugin_translation_stats_testcase}. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2019 David Mudrák 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $CFG; 29 | 30 | require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 31 | 32 | /** 33 | * Unit tests for AMOS external function 'plugin_translation_stats'. 34 | * 35 | * @copyright 2019 David Mudrák 36 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class local_amos_external_plugin_translation_stats_testcase extends externallib_advanced_testcase { 39 | 40 | /** 41 | * Test the behaviour of the plugin_translation_stats external function when unknown component is requested. 42 | */ 43 | public function test_plugin_translation_stats_unknown_component() { 44 | $this->resetAfterTest(true); 45 | 46 | // No special capability needed. 47 | $user = self::getDataGenerator()->create_user(); 48 | self::setUser($user); 49 | 50 | $this->expectException(invalid_parameter_exception::class); 51 | 52 | \local_amos\external\plugin_translation_stats::execute('muhehe'); 53 | } 54 | 55 | /** 56 | * Test the behaviour of the plugin_translation_stats external function. 57 | */ 58 | public function test_plugin_translation_stats() { 59 | global $CFG; 60 | require_once($CFG->dirroot.'/local/amos/mlanglib.php'); 61 | 62 | $this->resetAfterTest(true); 63 | 64 | // No special capability needed. 65 | $user = self::getDataGenerator()->create_user(); 66 | self::setUser($user); 67 | 68 | $stage = new mlang_stage(); 69 | $component = new mlang_component('langconfig', 'en', mlang_version::by_branch('MOODLE_36_STABLE')); 70 | $component->add_string(new mlang_string('thislanguageint', 'English')); 71 | $stage->add($component); 72 | $component->clear(); 73 | $stage->commit('Registering English language', ['source' => 'unittest']); 74 | 75 | $statsman = new local_amos_stats_manager(); 76 | $statsman->update_stats('36', 'en', 'tool_foo', 9); 77 | 78 | $raw = \local_amos\external\plugin_translation_stats::execute('tool_foo'); 79 | $clean = external_api::clean_returnvalue(\local_amos\external\plugin_translation_stats::execute_returns(), $raw); 80 | 81 | $this->assertEquals(1, count($clean['langnames'])); 82 | $this->assertEquals(1, count($clean['branches'])); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /templates/stage.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_amos/stage 19 | 20 | Render the AMOS Stage page. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Example context (json): 29 | }} 30 | 31 |
    32 | {{#strings}} 33 |
    37 |
    38 |
    39 |
    40 | {{displaysince}} | {{stringid}} | {{displaycomponent}} 41 |
    42 |
    {{{displayoriginal}}}
    43 |
    44 | 45 |
    46 |
    47 | 48 | {{displaysince}} | {{displaylanguage}} 49 | {{#committable}} 50 | 51 | {{/committable}} 52 | | {{#str}} unstage, local_amos {{/str}} 53 | | {{#str}} edit {{/str}} 54 | 55 | +/- 60 | 61 |
    62 |
    {{!-- 63 | --}}
    {{{displaytranslationdiff}}}
    {{!-- 64 | --}}
    {{{displaytranslationcurrent}}}
    {{!-- 65 | --}}
    {{{displaytranslationnew}}}
    {{!-- 66 | --}}
    67 |
    68 |
    69 |
    70 | {{/strings}} 71 |
    72 | 73 | {{#js}} 74 | require(['local_amos/stage'], function(stagejs) { 75 | stagejs.init(); 76 | }); 77 | {{/js}} 78 | 79 | -------------------------------------------------------------------------------- /db/services.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * AMOS external functions and web services are declared here. 19 | * 20 | * @package local_amos 21 | * @category webservice 22 | * @copyright 2012 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $functions = [ 29 | 'local_amos_update_strings_file' => [ 30 | 'classname' => '\local_amos\external\update_strings_file', 31 | 'methodname' => 'execute', 32 | 'description' => 'Imports strings from a string file.', 33 | 'type' => 'write', 34 | ], 35 | 'local_amos_plugin_translation_stats' => [ 36 | 'classname' => '\local_amos\external\plugin_translation_stats', 37 | 'methodname' => 'execute', 38 | 'description' => 'Get translation statistics for the given component / plugin.', 39 | 'type' => 'read', 40 | ], 41 | 'local_amos_stage_translated_string' => [ 42 | 'classname' => '\local_amos\external\stage_translated_string', 43 | 'methodname' => 'execute', 44 | 'description' => 'Add a string translation into a persistent AMOS stage.', 45 | 'capabilities' => 'local/amos:stage', 46 | 'type' => 'write', 47 | 'ajax' => true, 48 | ], 49 | 'local_amos_get_translator_data' => [ 50 | 'classname' => '\local_amos\external\get_translator_data', 51 | 'methodname' => 'execute', 52 | 'description' => 'Return data for the translator based on the filter query.', 53 | 'capabilities' => 'local/amos:stage', 54 | 'type' => 'read', 55 | 'ajax' => true, 56 | ], 57 | 'local_amos_get_string_timeline' => [ 58 | 'classname' => '\local_amos\external\get_string_timeline', 59 | 'methodname' => 'execute', 60 | 'description' => 'Return data for the string timeline modal info.', 61 | 'capabilities' => 'local/amos:stage', 62 | 'type' => 'read', 63 | 'ajax' => true, 64 | ], 65 | 'local_amos_make_translation_uptodate' => [ 66 | 'classname' => '\local_amos\external\make_translation_uptodate', 67 | 'methodname' => 'execute', 68 | 'description' => 'Mark given translation as up-to-date against the given original.', 69 | 'capabilities' => 'local/amos:commit', 70 | 'type' => 'write', 71 | 'ajax' => true, 72 | ], 73 | ]; 74 | 75 | $services = [ 76 | 'AMOS integration with the Moodle Plugins directory' => [ 77 | 'functions' => [ 78 | 'local_amos_update_strings_file', 79 | 'local_amos_plugin_translation_stats', 80 | ], 81 | 'requiredcapability' => 'local/amos:importstrings', 82 | 'restrictedusers' => 1, 83 | 'enabled' => 1, 84 | ], 85 | ]; 86 | -------------------------------------------------------------------------------- /templates/downloadpage.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_plugins/frontpage 19 | 20 | Template purpose and description. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * attributes Array of name / value pairs. 30 | 31 | Example context (json): 32 | {} 33 | }} 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {{#langpacks}} 46 | {{#haszip}} 47 | 48 | 56 | 57 | 60 | 61 | 64 | 65 | 78 | 79 | {{/haszip}} 80 | {{/langpacks}} 81 | 82 |
    LanguageDownloadSizePercentage of language strings translated
    49 | {{^parentlanguagecode}} 50 | {{langname}} 51 | {{/parentlanguagecode}} 52 | {{#parentlanguagecode}} 53 | {{langname}} 54 | {{/parentlanguagecode}} 55 | 58 | Download {{langcode}}.zip 59 | 62 | {{filesize}} 63 | 66 | {{^parentlanguagecode}} 67 |
    68 |
     
    69 |
    70 | {{ratio}}% {{totalstrings}} / {{totalenglish}} 71 |
    72 |
    73 | {{/parentlanguagecode}} 74 | {{#parentlanguagecode}} 75 | {{totalstrings}} changes from the parent language '{{parentlanguagecode}}' 76 | {{/parentlanguagecode}} 77 |
    83 | 84 |

    85 | Generated 86 |

    87 | -------------------------------------------------------------------------------- /db/access.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Capability definitions for AMOS local plugin 20 | * 21 | * @package local_amos 22 | * @copyright 2010 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | $capabilities = array( 29 | 30 | // Ability to set-up AMOS portal and assign translators of languages 31 | 'local/amos:manage' => array( 32 | 'captype' => 'write', 33 | 'contextlevel' => CONTEXT_SYSTEM, 34 | 'legacy' => array() 35 | ), 36 | 37 | // Ability to stage translations using the translation tool 38 | 'local/amos:stage' => array( 39 | 'captype' => 'write', 40 | 'contextlevel' => CONTEXT_SYSTEM, 41 | 'legacy' => array( 42 | 'user' => CAP_ALLOW, 43 | ) 44 | ), 45 | 46 | // Ability to execute a given AMOScript and get the result in the stage 47 | 'local/amos:execute' => array( 48 | 'captype' => 'write', 49 | 'contextlevel' => CONTEXT_SYSTEM, 50 | 'legacy' => array() 51 | ), 52 | 53 | // Ability to commit the stage into AMOS repository 54 | 'local/amos:commit' => array( 55 | 'captype' => 'write', 56 | 'contextlevel' => CONTEXT_SYSTEM, 57 | 'legacy' => array() 58 | ), 59 | 60 | // Ability to stash a stage and to contribute 61 | 'local/amos:stash' => array( 62 | 'captype' => 'write', 63 | 'contextlevel' => CONTEXT_SYSTEM, 64 | 'legacy' => array( 65 | 'user' => CAP_ALLOW, 66 | ) 67 | ), 68 | 69 | // Ability to import translated strings from uploaded file and stage them 70 | 'local/amos:importfile' => array( 71 | 'captype' => 'write', 72 | 'contextlevel' => CONTEXT_SYSTEM, 73 | 'legacy' => array( 74 | 'user' => CAP_ALLOW, 75 | ) 76 | ), 77 | 78 | // Ability to import strings (including the English ones) directly into the repository 79 | // (this is intended mainly for the web service users) 80 | 'local/amos:importstrings' => array( 81 | 'captype' => 'write', 82 | 'contextlevel' => CONTEXT_SYSTEM, 83 | 'legacy' => array() 84 | ), 85 | 86 | // Ability to use Google Translate services 87 | 'local/amos:usegoogle' => array( 88 | 'captype' => 'read', 89 | 'contextlevel' => CONTEXT_SYSTEM, 90 | 'legacy' => array( 91 | 'user' => CAP_ALLOW, 92 | ) 93 | ), 94 | 95 | // Ability to convert an existing contribution to a new contribution with 96 | // different language 97 | 'local/amos:changecontriblang' => array( 98 | 'captype' => 'write', 99 | 'contextlevel' => CONTEXT_SYSTEM, 100 | 'legacy' => array() 101 | ), 102 | ); 103 | -------------------------------------------------------------------------------- /cli/contrib-bulk.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * CLI script allowing to perform bulk operations over AMOS contributions. 19 | * 20 | * @package local_amos 21 | * @copyright 2020 David Mudrák 22 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | define('CLI_SCRIPT', true); 26 | 27 | require(__DIR__ . '/../../../config.php'); 28 | require_once($CFG->libdir . '/clilib.php'); 29 | require_once($CFG->dirroot . '/local/amos/locallib.php'); 30 | 31 | $usage = "Perform bulk operations over AMOS contributed translations. 32 | 33 | Usage: 34 | # php contrib-bulk.php --user= --approve 35 | 36 | Options: 37 | --author= User ID of the contributor. 38 | --approve Approve all the pending contributions by the user. 39 | 40 | "; 41 | 42 | define('AMOS_BOT_USERID', 2); 43 | 44 | [$options, $unrecognised] = cli_get_params([ 45 | 'help' => false, 46 | 'author' => null, 47 | 'approve' => false, 48 | ], [ 49 | 'h' => 'help', 50 | 'e' => 'execute', 51 | ]); 52 | 53 | if ($unrecognised) { 54 | $unrecognised = implode(PHP_EOL . ' ', $unrecognised); 55 | cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised)); 56 | } 57 | 58 | if ($options['help']) { 59 | cli_writeln($usage); 60 | exit(2); 61 | } 62 | 63 | if (empty($options['author'])) { 64 | cli_error('Missing mandatory argument: author', 2); 65 | } 66 | 67 | $sql = "SELECT c.id, c.lang, c.subject, c.stashid, c.status, c.timecreated, 68 | s.components, s.strings, " . 69 | user_picture::fields('a', null, 'authorid', 'author') . " 70 | FROM {amos_contributions} c 71 | JOIN {amos_stashes} s ON (c.stashid = s.id) 72 | JOIN {user} a ON c.authorid = a.id 73 | WHERE c.status < :rejected 74 | AND a.id = :authorid 75 | ORDER BY c.timemodified"; 76 | 77 | $contributions = $DB->get_records_sql($sql, [ 78 | 'rejected' => local_amos_contribution::STATE_REJECTED, 79 | 'authorid' => $options['author'], 80 | ]); 81 | 82 | $stage = new mlang_stage(); 83 | 84 | foreach ($contributions as $contribution) { 85 | echo sprintf("#%d\t%s\t%s\t%d\n", $contribution->id, $contribution->subject, $contribution->components, $contribution->strings); 86 | 87 | if ($options['approve']) { 88 | $stash = mlang_stash::instance_from_id($contribution->stashid); 89 | $stash->apply($stage); 90 | 91 | $stage->commit('Bulk approval - contribution #' . $contribution->id, [ 92 | 'source' => 'bot', 93 | 'userinfo' => 'AMOS-bot ' 94 | ]); 95 | 96 | $update = [ 97 | 'id' => $contribution->id, 98 | 'timemodified' => time(), 99 | 'assignee' => AMOS_BOT_USERID, 100 | 'status' => local_amos_contribution::STATE_ACCEPTED, 101 | ]; 102 | 103 | $DB->update_record('amos_contributions', $update); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /scss/styles.scss: -------------------------------------------------------------------------------- 1 | @import "../../../theme/moodleorg/scss/pre.scss"; 2 | @import "variables.scss"; 3 | @import "filter.scss"; 4 | @import "translator"; 5 | @import "stage.scss"; 6 | @import "credits.scss"; 7 | @import "index.scss"; 8 | @import "comment.scss"; 9 | 10 | #page-local-amos-view { 11 | // Avoid content overlap with side blocks. 12 | #region-main { 13 | overflow-x: auto; 14 | } 15 | 16 | h2 { 17 | text-align: center; 18 | } 19 | } 20 | 21 | .path-local-amos .logrecord { 22 | border: 1px solid #ddd; 23 | padding: 0.5em; 24 | } 25 | 26 | .path-local-amos .contributionlist { 27 | margin: 1em auto; 28 | } 29 | 30 | .path-local-amos .contributionlist .userpicture { 31 | margin-right:5px; 32 | vertical-align: middle; 33 | } 34 | 35 | .path-local-amos .contributionlist .status0 .id, 36 | .path-local-amos .contributionlist .status0 .status, 37 | .path-local-amos .contributionwrapper .details .status0 td { 38 | background-color: #d2ebff; 39 | } 40 | 41 | .path-local-amos .contributionlist .status10 .id, 42 | .path-local-amos .contributionlist .status10 .status, 43 | .path-local-amos .contributionwrapper .details .status10 td, 44 | .path-local-amos .contribactions .singlebutton.review input[type="submit"] { 45 | background-color: #f3f2aa; 46 | } 47 | 48 | .path-local-amos .contributionlist .status20 .id, 49 | .path-local-amos .contributionlist .status20 .status, 50 | .path-local-amos .contributionwrapper .details .status20 td, 51 | .path-local-amos .contribactions .singlebutton.reject input[type="submit"] { 52 | background-color: #ffd3d9; 53 | } 54 | 55 | .path-local-amos .contributionlist .status30 .id, 56 | .path-local-amos .contributionlist .status30 .status, 57 | .path-local-amos .contributionwrapper .details .status30 td, 58 | .path-local-amos .contribactions .singlebutton.accept input[type="submit"] { 59 | background-color: #e7f1c3; 60 | } 61 | 62 | .path-local-amos .contributionwrapper .source { 63 | padding-left: 60px; 64 | position: relative; 65 | } 66 | 67 | .path-local-amos .contributionwrapper .source { 68 | margin-left: auto; 69 | margin-right: auto; 70 | } 71 | 72 | .path-local-amos .contributionwrapper .details { 73 | max-width: 100%; 74 | min-width: 300px; 75 | margin-top: 1em; 76 | margin-left: auto; 77 | margin-right: auto; 78 | } 79 | 80 | .path-local-amos .contributionwrapper .source h3, 81 | .path-local-amos .contributionwrapper .source div { 82 | margin: 0px 0px 0.3em; 83 | } 84 | 85 | .path-local-amos .contributionwrapper .source .timecreated { 86 | font-style: italic; 87 | } 88 | 89 | .path-local-amos .contributionwrapper .source .userpicture { 90 | position: absolute; 91 | top: 10px; 92 | left: 10px; 93 | } 94 | 95 | .path-local-amos .contributionwrapper .details .userpicture { 96 | margin-right:5px; 97 | vertical-align: middle; 98 | } 99 | 100 | .path-local-amos .contribactions { 101 | text-align: center; 102 | margin-bottom: 1em; 103 | } 104 | 105 | .path-local-amos .contribactions .singlebutton, 106 | .path-local-amos .contribactions .singlebutton form, 107 | .path-local-amos .contribactions .singlebutton form div { 108 | display: inline; 109 | } 110 | 111 | .path-local-amos .contribactions .singlebutton { 112 | margin: 0px 2px; 113 | } 114 | 115 | .path-local-amos .commentswrapper { 116 | width: 440px; 117 | margin-left: auto; 118 | margin-right: auto; 119 | padding: 10px; 120 | border: 1px solid #eee; 121 | background-color: white; 122 | } 123 | 124 | .path-local-amos .commentswrapper .comment-link { 125 | margin-bottom: 5px; 126 | } 127 | 128 | .path-local-amos .commentswrapper .comment-area { 129 | margin: 0px auto; 130 | } 131 | -------------------------------------------------------------------------------- /tests/external_stage_translated_string_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unit tests for external function {@see \local_amos\external\stage_translated_string}. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2020 David Mudrák 23 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | class local_amos_external_stage_translated_string_testcase extends local_amos_testcase { 26 | 27 | /** 28 | * Test that permission check is performed. 29 | */ 30 | public function test_execute_without_capability() { 31 | $this->resetAfterTest(true); 32 | 33 | $user = self::getDataGenerator()->create_user(); 34 | self::setUser($user); 35 | 36 | $spammer = create_role('Spammer', 'spammer', 'Prohibits contributions to the site'); 37 | assign_capability('local/amos:stage', CAP_PROHIBIT, $spammer, SYSCONTEXTID); 38 | 39 | role_assign($spammer, $user->id, SYSCONTEXTID); 40 | accesslib_clear_all_caches_for_unit_testing(); 41 | 42 | $this->expectException(required_capability_exception::class); 43 | \local_amos\external\stage_translated_string::execute(sesskey(), 123, 'en_fix', 'Foo'); 44 | } 45 | 46 | /** 47 | * Test basic behaviour of the method. 48 | */ 49 | public function test_execute_basics() { 50 | global $DB, $USER; 51 | $this->resetAfterTest(true); 52 | 53 | self::setAdminUser(); 54 | 55 | $this->register_language('en', 20); 56 | $this->register_language('cs', 20); 57 | 58 | $stage = new mlang_stage(); 59 | 60 | $component = new mlang_component('foo_bar', 'en', mlang_version::by_code(39)); 61 | $component->add_string(new mlang_string('foobar', 'Foo bar')); 62 | $stage->add($component); 63 | $component->clear(); 64 | 65 | $stage->commit('First string', ['source' => 'unittest']); 66 | 67 | $foobarcomponent = mlang_component::from_snapshot('foo_bar', 'en', mlang_version::by_code(310), null, false, true); 68 | $foobarstring = $foobarcomponent->get_string('foobar'); 69 | 70 | $stageid = sesskey(); 71 | $originalid = $foobarstring->extra->id; 72 | $lang = 'cs'; 73 | $text = 'Fů bár'; 74 | 75 | $response = \local_amos\external\stage_translated_string::execute($stageid, $originalid, $lang, $text); 76 | $response = external_api::clean_returnvalue(\local_amos\external\stage_translated_string::execute_returns(), $response); 77 | 78 | $this->assertTrue(is_array($response)); 79 | $this->assertSame('Fů bár', $response['translation']); 80 | $this->assertSame('Fů bár', $response['displaytranslation']); 81 | $this->assertSame('3.9+', $response['displaytranslationsince']); 82 | $this->assertFalse($response['nocleaning']); 83 | $this->assertSame([], $response['warnings']); 84 | 85 | $stage = mlang_persistent_stage::instance_for_user($USER->id, $USER->sesskey); 86 | 87 | $this->assertSame('Fů bár', $stage->get_component('foo_bar', 'cs', mlang_version::by_code(39))->get_string('foobar')->text); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /jobs/install-packs-publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Publish installer language packages at gitlab. 4 | # Merges newly generated installer strings into a local clone of moodle.git and pushes changes into a remote repository. 5 | # upstream: install-packs-make 6 | 7 | if [[ -z ${AMOSDATAROOT} ]]; then 8 | echo "Error: AMOSDATAROOT not set!" 9 | exit 1 10 | fi 11 | 12 | LOCKFILE=${AMOSDATAROOT}/install-packs-publish.lock 13 | REPO=${AMOSDATAROOT}/repos/moodle-install/ 14 | SRCROOT=${AMOSDATAROOT}/export-install/ 15 | 16 | if set -C; echo $$ 2>/dev/null > ${LOCKFILE}; then 17 | trap on_exit INT TERM EXIT 18 | else 19 | echo "Lock ${LOCKFILE} exists!" 20 | exit 1 21 | fi 22 | 23 | ## 24 | # To be executed on exiting. 25 | # 26 | on_exit() { 27 | rm -f ${LOCKFILE} 28 | } 29 | 30 | ## 31 | # Make sure that all required branches exist and are up to date. 32 | # 33 | # param $1 - name of the upstream branch such as master or MOODLE_311_STABLE 34 | # param $2 - name of the install branch such as install_master or install_311_STABLE 35 | # 36 | prepare_branches() { 37 | local upstreambranch=$1 38 | local installbranch=$2 39 | 40 | cd $REPO 41 | 42 | if git show-ref --quiet --verify refs/heads/${upstreambranch}; then 43 | git checkout $upstreambranch 44 | git merge --ff-only upstream/${upstreambranch} 45 | else 46 | git checkout --no-guess -b ${upstreambranch} --track upstream/${upstreambranch} 47 | fi 48 | 49 | if git show-ref --quiet --verify refs/heads/${installbranch}; then 50 | git checkout ${installbranch} 51 | else 52 | git checkout --no-guess -b ${installbranch} --no-track upstream/${upstreambranch} 53 | fi 54 | 55 | GIT_AUTHOR_NAME="AMOS bot" GIT_AUTHOR_EMAIL="amos@moodle.org" GIT_COMMITTER_NAME="AMOS bot" GIT_COMMITTER_EMAIL="amos@moodle.org" \ 56 | git merge ${upstreambranch} 57 | } 58 | 59 | ## 60 | # Commit new install strings to the install branch. 61 | # 62 | # param $1 - name of the directory inside ${SRCROOT} to process such as master or 311_STABLE 63 | # param $2 - name of the install branch such as install_master or install_311_STABLE 64 | # 65 | commit_install_strings() { 66 | local dirname=$1 67 | local installbranch=$2 68 | 69 | cd ${REPO} 70 | git checkout ${installbranch} 71 | 72 | cd ${REPO}/install 73 | rm -rf lang 74 | cp -r ${AMOSDATAROOT}/export-install/${dirname}/install/lang . 75 | git add . 76 | GIT_AUTHOR_NAME="AMOS bot" GIT_AUTHOR_EMAIL="amos@moodle.org" GIT_COMMITTER_NAME="AMOS bot" GIT_COMMITTER_EMAIL="amos@moodle.org" \ 77 | git commit -a -m "Automatically generated installer lang files" 78 | } 79 | 80 | ## 81 | # Push branches to remotes 82 | # 83 | # param $1 - name of the upstream branch such as master or MOODLE_311_STABLE 84 | # param $2 - name of the install branch such as install_master or install_311_STABLE 85 | # 86 | publish_branches() { 87 | local upstreambranch=$1 88 | local installbranch=$2 89 | 90 | git push gitlab ${upstreambranch}:${upstreambranch} 91 | git push gitlab ${installbranch}:${installbranch} 92 | } 93 | 94 | # Execution starts here. 95 | 96 | cd $REPO 97 | git remote update --prune 98 | 99 | for DIR in ${SRCROOT}/*; do 100 | if [[ -d ${DIR} ]]; then 101 | DIRNAME=$(basename ${DIR}) 102 | if [[ ${DIRNAME} = "master" ]]; then 103 | UPSTREAMBRANCH="master" 104 | INSTALLBRANCH="install_master" 105 | else 106 | UPSTREAMBRANCH="MOODLE_${DIRNAME}" 107 | INSTALLBRANCH="install_${DIRNAME}" 108 | fi 109 | 110 | prepare_branches ${UPSTREAMBRANCH} ${INSTALLBRANCH} 111 | commit_install_strings ${DIRNAME} ${INSTALLBRANCH} 112 | publish_branches ${UPSTREAMBRANCH} ${INSTALLBRANCH} 113 | fi 114 | done 115 | 116 | cd ${REPO} 117 | git gc 118 | -------------------------------------------------------------------------------- /untranslate.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Untranslate an existing translation 20 | * 21 | * @package local_amos 22 | * @copyright 2015 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | require(__DIR__.'/../../config.php'); 27 | require_once($CFG->dirroot.'/local/amos/mlanglib.php'); 28 | 29 | $since = required_param('since', PARAM_INT); 30 | $component = required_param('component', PARAM_ALPHANUMEXT); 31 | $language = required_param('language', PARAM_ALPHANUMEXT); 32 | $stringid = required_param('stringid', PARAM_STRINGID); 33 | $confirm = optional_param('confirm', false, PARAM_BOOL); 34 | 35 | require_login(SITEID, false); 36 | require_capability('local/amos:commit', context_system::instance()); 37 | 38 | $allowedlangs = mlang_tools::list_allowed_languages(); 39 | 40 | if (empty($allowedlangs['X']) and empty($allowedlangs[$language])) { 41 | throw new moodle_exception('err_unexpected_language', 'local_amos'); 42 | } 43 | 44 | $PAGE->set_pagelayout('standard'); 45 | $PAGE->set_url(new moodle_url('/local/amos/untranslate.php', [ 46 | 'component' => $component, 47 | 'language' => $language, 48 | 'stringid' => $stringid, 49 | 'since' => $since, 50 | ])); 51 | $PAGE->set_title('AMOS '.get_string('untranslating', 'local_amos')); 52 | $PAGE->navbar->add(get_string('untranslating', 'local_amos')); 53 | 54 | navigation_node::override_active_url(new moodle_url('/local/amos/view.php')); 55 | 56 | $mversion = mlang_version::by_code($since); 57 | 58 | if (!$mversion->translatable) { 59 | throw new moodle_exception('err_nontranslatable_version', 'local_amos'); 60 | } 61 | 62 | if ($confirm) { 63 | require_sesskey(); 64 | 65 | $mstage = new mlang_stage(); 66 | $mcomponent = mlang_component::from_snapshot($component, $language, $mversion, null, false, false, [$stringid]); 67 | $cstring = $mcomponent->get_string($stringid); 68 | 69 | if ($cstring !== null) { 70 | $mstring = new mlang_string($cstring->id, $cstring->text, null, true); 71 | $mcomponent->add_string($mstring, true); 72 | $mstage->add($mcomponent); 73 | 74 | $message = 'Untranslate the string '.$stringid; 75 | $meta = [ 76 | 'source' => 'amos', 77 | 'userid' => $USER->id, 78 | 'userinfo' => fullname($USER) . ' <' . $USER->email . '>', 79 | ]; 80 | $mstage->commit($message, $meta); 81 | } 82 | 83 | redirect(new moodle_url('/local/amos/view.php')); 84 | } 85 | 86 | $output = $PAGE->get_renderer('local_amos'); 87 | 88 | echo $output->header(); 89 | echo $output->heading(get_string('untranslatetitle', 'local_amos')); 90 | 91 | $a = [ 92 | 'component' => $component, 93 | 'language' => $language, 94 | 'stringid' => $stringid, 95 | 'since' => $mversion->label, 96 | ]; 97 | 98 | echo $output->confirm( 99 | get_string('untranslateconfirm', 'local_amos', $a), 100 | new moodle_url($PAGE->url, ['sesskey' => sesskey(), 'confirm' => true]), 101 | new moodle_url('/local/amos/view.php') 102 | ); 103 | 104 | echo $output->footer(); 105 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Defines administration settings for AMOS 19 | * 20 | * @package local_amos 21 | * @category admin 22 | * @copyright 2010 David Mudrák , 2019 Pau Ferrer Ocaña 23 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | if ($hassiteconfig) { 29 | $settings = new admin_settingpage('local_amos', get_string('manageamos', 'local_amos')); 30 | 31 | if ($ADMIN->fulltree) { 32 | $settings->add(new admin_setting_configtextarea( 33 | 'local_amos/branchesall', 34 | get_string('branchesall', 'local_amos'), 35 | get_string('branchesall_desc', 'local_amos'), 36 | '20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,310,311,400', 37 | PARAM_RAW_TRIMMED, 38 | 60, 3 39 | )); 40 | 41 | $settings->add(new admin_setting_configtext( 42 | 'local_amos/branchsupported', 43 | get_string('branchsupported', 'local_amos'), 44 | get_string('branchsupported_desc', 'local_amos'), 45 | '35', 46 | PARAM_INT, 47 | 10 48 | )); 49 | 50 | $settings->add(new admin_setting_configtextarea( 51 | 'local_amos/standardcomponents', 52 | get_string('standardcomponents', 'local_amos'), 53 | get_string('standardcomponents_desc', 'local_amos'), 54 | '', 55 | PARAM_RAW_TRIMMED, 56 | 60, 10 57 | )); 58 | 59 | $settings->add(new admin_setting_configtextarea( 60 | 'local_amos/plugintypelocations', 61 | get_string('plugintypelocations', 'local_amos'), 62 | get_string('plugintypelocations_desc', 'local_amos'), 63 | implode(PHP_EOL, [ 64 | 'assignment mod/assignment/type', 65 | 'datafield mod/data/field', 66 | 'datapreset mod/data/preset', 67 | 'quiz mod/quiz/report', 68 | 'quizaccess mod/quiz/accessrule', 69 | 'scormreport mod/scorm/report', 70 | 'workshopform mod/workshop/form', 71 | 'workshopallocation mod/workshop/allocation', 72 | 'workshopeval mod/workshop/eval', 73 | 'assignsubmission mod/assign/submission', 74 | 'assignfeedback mod/assign/feedback', 75 | 'booktool mod/book/tool', 76 | 'tinymce lib/editor/tinymce/plugins', 77 | 'atto lib/editor/atto/plugins', 78 | 'logstore admin/tool/log/store', 79 | 'ltisource mod/lti/source', 80 | 'ltiservice mod/lti/service', 81 | 'forumreport mod/forum/report', 82 | ]), 83 | PARAM_RAW_TRIMMED, 84 | 60, 10 85 | )); 86 | 87 | $settings->add(new admin_setting_configtext( 88 | 'local_amos/applangindexfile', 89 | get_string('applangindexfile', 'local_amos'), 90 | get_string('applangindexfile_desc', 'local_amos'), 91 | 'https://raw.githubusercontent.com/moodlehq/moodlemobile2/integration/scripts/langindex.json', 92 | PARAM_URL 93 | )); 94 | } 95 | 96 | $ADMIN->add('root', $settings, 'registrationmoodleorg'); 97 | } 98 | -------------------------------------------------------------------------------- /tests/fixtures/parserdata002.txt: -------------------------------------------------------------------------------- 1 | 2 | diff --git a/lang/en/admin.php b/lang/en/admin.php 3 | index 7c67ea9..6271297 100644 4 | --- a/lang/en/admin.php 5 | +++ b/lang/en/admin.php 6 | @@ -183,7 +183,7 @@ $string['configdefaultallowedmodules'] = 'For the courses which fall into the ab 7 | $string['configdefaulthomepage'] = 'This determines the home page for logged in users'; 8 | $string['configdefaultrequestcategory'] = 'Courses requested by users will be automatically placed in this category.'; 9 | $string['configdefaultrequestedcategory'] = 'Default category to put courses that were requested into, if they\'re approved.'; 10 | -$string['configdefaultuserroleid'] = 'All logged in users will be given the capabilities of the role you specify here, at the site level, in ADDITION to any other roles they may have been given. The default is the Authenticated user role (or Guest role in older versions). Note that this will not conflict with other roles they have, it just ensures that all users have capabilities that are not assignable at the course level (eg post blog entries, manage own calendar, etc).'; 11 | +$string['configdefaultuserroleid'] = 'All logged in users will be given the capabilities of the role you specify here, at the site level, in ADDITION to any other roles they may have been given. The default is the Authenticated user role. Note that this will not conflict with other roles they have unless you prohibit capabilities, it just ensures that all users have capabilities that are not assignable at the course level (eg post blog entries, manage own calendar, etc).'; 12 | $string['configdeleteincompleteusers'] = 'After this period, old not fully setup accounts are deleted.'; 13 | $string['configdeleteunconfirmed'] = 'If you are using email authentication, 14 | 15 | this is the period within which a response will be accepted from users. After this period, old unconfirmed accounts are deleted.'; 16 | $string['configdenyemailaddresses'] = 'To deny email addresses from particular domains list them here in the same way. All other domains will be accepted. To deny subdomains add the domain with a preceding \'.\'. eg hotmail.com yahoo.co.uk .live.com'; 17 | @@ -276,7 +276,6 @@ $string['configmypagelocked'] = 'This setting prevents the default page from bei 18 | $string['confignavcourselimit'] = 'Limits the number of courses shown to the user when they are either not logged in or are not enrolled in any courses.'; 19 | $string['confignavshowallcourses'] = 'Setting this ensures that all courses on the site are shown in the navigation at all times.'; 20 | $string['confignavshowcategories'] = 'Show course categories in the navigation bar and navigation blocks. This does not occur with courses the user is currently enrolled in, they will still be listed under mycourses without categories.'; 21 | -$string['confignodefaultuserrolelists'] = 'This setting prevents all users from being returned from the database from deprecated calls of get_course_user, etc., for the site course if the default role provides that access. Check this, if you suffer a performance hit.'; 22 | $string['confignoreplyaddress'] = 'Emails are sometimes sent out on behalf of a user (eg forum posts). The email address you specify here will be used as the "From" address in those cases when the recipients should not be able to reply directly to the user (eg when a user chooses to keep their address private).'; 23 | $string['confignotifyloginfailures'] = 'If login failures have been recorded, email notifications can be sent out. Who should see these notifications?'; 24 | $string['confignotifyloginthreshold'] = 'If notifications about failed logins are active, how many failed login attempts by one user or one IP address is it worth notifying about?'; 25 | @@ -747,7 +746,6 @@ $string['navshowcategories'] = 'Show course categories'; 26 | $string['neverdeleteruns'] = 'Never delete runs'; 27 | $string['nobookmarksforuser'] = 'You do not have any bookmarks.'; 28 | $string['nodatabase'] = 'No database'; 29 | -$string['nodefaultuserrolelists'] = 'Don\'t return all default role users'; 30 | $string['nochanges'] = 'No changes'; 31 | +$string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed'; 32 | $string['nomissingstrings'] = 'No missing strings'; 33 | +$string['mod/something:really_nasty-like0098187.this'] ='Valid'; 34 | -------------------------------------------------------------------------------- /tests/external_get_string_timeline_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unit tests for external function {@see \local_amos\external\get_string_timeline}. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2020 David Mudrák 23 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | class local_amos_external_get_string_timeline_testcase extends local_amos_testcase { 26 | 27 | /** 28 | * Test that permission check is performed. 29 | */ 30 | public function test_execute_without_capability() { 31 | $this->resetAfterTest(true); 32 | 33 | $user = self::getDataGenerator()->create_user(); 34 | self::setUser($user); 35 | 36 | $spammer = create_role('Spammer', 'spammer', 'Prohibits contributions to the site'); 37 | assign_capability('local/amos:stage', CAP_PROHIBIT, $spammer, SYSCONTEXTID); 38 | 39 | role_assign($spammer, $user->id, SYSCONTEXTID); 40 | accesslib_clear_all_caches_for_unit_testing(); 41 | 42 | $this->expectException(required_capability_exception::class); 43 | \local_amos\external\get_string_timeline::execute('', '', ''); 44 | } 45 | 46 | /** 47 | * Test basic behaviour of the method. 48 | */ 49 | public function test_execute_basics() { 50 | global $DB, $USER; 51 | $this->resetAfterTest(true); 52 | 53 | self::setAdminUser(); 54 | 55 | $this->register_language('en', 20); 56 | $this->register_language('cs', 20); 57 | 58 | $stage = new mlang_stage(); 59 | $component = new mlang_component('foo_bar', 'en', mlang_version::by_code(39)); 60 | $component->add_string(new mlang_string('foobar', 'Foo bar')); 61 | $stage->add($component); 62 | $stage->commit('First string', ['source' => 'unittest']); 63 | 64 | $stage = new mlang_stage(); 65 | $component = new mlang_component('foo_bar', 'cs', mlang_version::by_code(39)); 66 | $component->add_string(new mlang_string('foobar', 'Fů bár')); 67 | $stage->add($component); 68 | $stage->commit('První překlad', ['source' => 'unittest']); 69 | 70 | $stage = new mlang_stage(); 71 | $component = new mlang_component('foo_bar', 'en', mlang_version::by_code(310)); 72 | $component->add_string(new mlang_string('foobar', 'Foo bar!')); 73 | $stage->add($component); 74 | $stage->commit('Add exclamation mark in Moodle 3.10', ['source' => 'unittest']); 75 | 76 | $response = \local_amos\external\get_string_timeline::execute('foo_bar', 'foobar', 'cs'); 77 | $response = external_api::clean_returnvalue(\local_amos\external\get_string_timeline::execute_returns(), $response); 78 | 79 | $this->assertEquals('foo_bar', $response['component']); 80 | $this->assertEquals('foobar', $response['strname']); 81 | $this->assertEquals('cs', $response['language']); 82 | $this->assertEquals('Foo bar!', $response['changes'][0]['english']['displaytext']); 83 | $this->assertEquals('en', $response['changes'][0]['english']['langcode']); 84 | $this->assertEquals('3.10+', $response['changes'][0]['english']['displaysince']); 85 | $this->assertFalse($response['changes'][0]['translation']['hascontent']); 86 | 87 | $this->assertFalse($response['changes'][1]['english']['hascontent']); 88 | $this->assertEquals('3.9+', $response['changes'][1]['translation']['displaysince']); 89 | $this->assertEquals('Fů bár', $response['changes'][1]['translation']['displaytext']); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /cli/compare-packs.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Cmpare the contents of two unzipped language packs 19 | * 20 | * @package local_amos 21 | * @subpackage cli 22 | * @copyright 2013 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | define('CLI_SCRIPT', 1); 27 | 28 | require(__DIR__ . '/../../../config.php'); 29 | require_once($CFG->libdir . '/clilib.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | 32 | $usage = "Use this script to compare the contents of two unzipped language packs 33 | 34 | $ php compare-packs.php --old=/tmp/old/cs --new=/tmp/new/cs 35 | "; 36 | 37 | list($options, $unrecognised) = cli_get_params([ 38 | 'help' => false, 39 | 'old' => '', 40 | 'new' => '', 41 | ], [ 42 | 'h' => 'help', 43 | ]); 44 | 45 | if ($unrecognised) { 46 | $unrecognised = implode(PHP_EOL . ' ', $unrecognised); 47 | cli_error(get_string('cliunknowoption', 'core_admin', $unrecognised)); 48 | } 49 | 50 | if ($options['help']) { 51 | cli_writeln($usage); 52 | exit(2); 53 | } 54 | 55 | $old = $options['old']; 56 | $new = $options['new']; 57 | 58 | if (empty($old) || empty($new)) { 59 | cli_error($usage); 60 | } 61 | 62 | if (!is_dir($old)) { 63 | cli_error($old . ' is not a directory'); 64 | } 65 | 66 | if (!is_dir($new)) { 67 | cli_error($new . ' is not a directory'); 68 | } 69 | 70 | $oldpack = load_language_pack($old); 71 | $newpack = load_language_pack($new); 72 | 73 | if ($diff = array_diff(array_keys($oldpack), array_keys($newpack))) { 74 | cli_writeln('(+ ) component only in old: ' . implode(', ', $diff)); 75 | } 76 | 77 | if ($diff = array_diff(array_keys($newpack), array_keys($oldpack))) { 78 | cli_writeln('( +) components only in new: ' . implode(', ', $diff)); 79 | } 80 | 81 | foreach (array_intersect(array_keys($newpack), array_keys($oldpack)) as $component) { 82 | if ($diff = array_diff(array_keys($oldpack[$component]), array_keys($newpack[$component]))) { 83 | cli_writeln('[ +] strings only in old ' . $component . ': ' . implode(', ', $diff)); 84 | } 85 | 86 | if ($diff = array_diff(array_keys($newpack[$component]), array_keys($oldpack[$component]))) { 87 | cli_writeln('[+ ] strings only in new ' . $component . ': ' . implode(', ', $diff)); 88 | } 89 | 90 | foreach (array_intersect(array_keys($newpack[$component]), array_keys($oldpack[$component])) as $strname) { 91 | if ($newpack[$component][$strname] !== $oldpack[$component][$strname]) { 92 | cli_writeln('[!!] string mismatch in component ' . $component . ': ' . $strname); 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Load all strings from all files in the given language pack path. 99 | * 100 | * @param string $path 101 | * @return array (string)componentname => (string)strname => (string)strtext 102 | */ 103 | function load_language_pack(string $path): array { 104 | 105 | $pack = []; 106 | 107 | foreach (new DirectoryIterator($path) as $file) { 108 | if ($file->isDot() or $file->isDir()) { 109 | continue; 110 | } 111 | if (substr($file->getFilename(), -4) !== '.php') { 112 | fputs(STDERR, 'Unexpected file ' . $file->getPathname()); 113 | exit(1); 114 | } 115 | $component = mlang_component::name_from_filename($file->getFilename()); 116 | $string = []; 117 | require($file->getPathname()); 118 | $pack[$component] = $string; 119 | unset($string); 120 | } 121 | 122 | return $pack; 123 | } 124 | -------------------------------------------------------------------------------- /classes/external/plugin_translation_stats.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides class {@see \local_amos\external\plugin_translation_stats}. 19 | * 20 | * @package local_amos 21 | * @category external 22 | * @copyright 2019 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | namespace local_amos\external; 27 | 28 | defined('MOODLE_INTERNAL') || die(); 29 | 30 | /** 31 | * Implements external function plugin_translation_stats used e.g. by plugins directory. 32 | * 33 | * @package local_amos 34 | * @category external 35 | * @copyright 2019, 2020 David Mudrák 36 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 | */ 38 | class plugin_translation_stats extends \external_api { 39 | 40 | /** 41 | * Describes parameters of the {@link execute()} method 42 | */ 43 | public static function execute_parameters() { 44 | return new \external_function_parameters([ 45 | 'component' => new \external_value(PARAM_COMPONENT, 'Name of the component to obtain stats for'), 46 | ]); 47 | } 48 | 49 | /** 50 | * Returns stats about the given plugin / component translations. 51 | * 52 | * @param string $component 53 | * @return stdClass 54 | */ 55 | public static function execute($component) { 56 | 57 | // Validate parameters. 58 | $params = self::validate_parameters(self::execute_parameters(), ['component' => $component]); 59 | $component = $params['component']; 60 | 61 | // Validate the context. 62 | $context = \context_system::instance(); 63 | self::validate_context($context); 64 | 65 | $statsman = new \local_amos_stats_manager(); 66 | 67 | $data = $statsman->get_component_stats($component); 68 | 69 | if ($data === false) { 70 | throw new \invalid_parameter_exception('Stats requested for an unknown component.'); 71 | } 72 | 73 | return $data; 74 | } 75 | 76 | /** 77 | * Describes the return value of the {@link execute()} method. 78 | * 79 | * @return external_description 80 | */ 81 | public static function execute_returns() { 82 | return new \external_single_structure([ 83 | 'lastmodified' => new \external_value(PARAM_INT, 'Timestamp of when the data was last modified'), 84 | 'langnames' => new \external_multiple_structure( 85 | new \external_single_structure([ 86 | 'lang' => new \external_value(PARAM_SAFEDIR, 'Language code'), 87 | 'name' => new \external_value(PARAM_TEXT, 'International name of the language followed by its code') 88 | ]) 89 | ), 90 | 'branches' => new \external_multiple_structure( 91 | new \external_single_structure([ 92 | 'branch' => new \external_value(PARAM_FILE, 'Moodle branch (eg. 3.6)'), 93 | 'languages' => new \external_multiple_structure( 94 | new \external_single_structure([ 95 | 'lang' => new \external_value(PARAM_SAFEDIR, 'Language code'), 96 | 'numofstrings' => new \external_value(PARAM_INT, 'Number of strings in the language pack'), 97 | 'ratio' => new \external_value(PARAM_INT, 'Completeness of the translation'), 98 | ]) 99 | ), 100 | ]) 101 | ) 102 | ]); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /cli/enfix-merge.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package local_amos 20 | * @subpackage enfix 21 | * @copyright 2013 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $help = 26 | "Merges en_fix strings into the Git working directory. 27 | 28 | Options: 29 | 30 | --symlinksdir Full path to the directory with symbolic links generated 31 | by the enfix-makesymlinks.sh script 32 | --enfixdir Full path to the directory containing en_fix string files 33 | generated by enfix-export.php script 34 | --help, -h Print out this help 35 | 36 | Example: 37 | \$ php enfix-merge.php --symlinksdir=/home/mudrd8mz/public_html/moodle25/amos --enfixdir=/home/mudrd8mz/moodledata/moodle24amos/amos/export-enfix/2.5 38 | "; 39 | 40 | define('CLI_SCRIPT', true); 41 | 42 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 43 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 44 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 45 | 46 | list($options, $unrecognized) = cli_get_params( 47 | array( 48 | 'symlinksdir' => '', 49 | 'enfixdir' => '', 50 | 'help' => false), 51 | array('h' => 'help')); 52 | 53 | 54 | if ($options['help'] or empty($options['symlinksdir']) or empty($options['enfixdir'])) { 55 | cli_error($help, 2); 56 | } 57 | 58 | // Make sure that all target files exist, are symlinks and are writable. 59 | 60 | foreach (new DirectoryIterator($options['enfixdir']) as $enfixfileinfo) { 61 | if ($enfixfileinfo->isDot()) { 62 | continue; 63 | } 64 | $filename = $enfixfileinfo->getFilename(); 65 | 66 | if (!is_link($options['symlinksdir'].'/'.$filename)) { 67 | cli_error('File not symlink: '.$options['symlinksdir'].'/'.$filename); 68 | } 69 | 70 | if (!is_writable($options['symlinksdir'].'/'.$filename)) { 71 | cli_error('File not writable: '.$options['symlinksdir'].'/'.$filename); 72 | } 73 | } 74 | 75 | // Let's rock! 76 | 77 | $logger = new amos_cli_logger(); 78 | $helper = new amos_merge_string_files($logger); 79 | $total = 0; 80 | 81 | foreach (new DirectoryIterator($options['enfixdir']) as $enfixfileinfo) { 82 | if ($enfixfileinfo->isDot()) { 83 | continue; 84 | } 85 | $filename = $enfixfileinfo->getFilename(); 86 | 87 | $logger->log('enfix-merge', 'Processing file '.$filename.' ...'); 88 | 89 | if ($filename === 'langconfig.php') { 90 | $logger->log('enfix-merge', 'Skipping file '.$filename, amos_cli_logger::LEVEL_DEBUG); 91 | continue; 92 | } 93 | 94 | $filecontents = file_get_contents($options['symlinksdir'].'/'.$filename); 95 | 96 | $fromstrings = $helper->load_strings_from_file($options['symlinksdir'].'/'.$filename); 97 | $logger->log('enfix-merge', count($fromstrings).' string(s) found in '.$options['symlinksdir'].'/'.$filename, amos_cli_logger::LEVEL_DEBUG); 98 | 99 | $tostrings = $helper->load_strings_from_file($options['enfixdir'].'/'.$filename); 100 | $logger->log('enfix-merge', count($tostrings).' string(s) found in '.$options['enfixdir'].'/'.$filename, amos_cli_logger::LEVEL_DEBUG); 101 | 102 | $changes = $helper->replace_strings_in_file($filecontents, $fromstrings, $tostrings); 103 | 104 | if ($changes) { 105 | $total += $changes; 106 | file_put_contents($options['symlinksdir'].'/'.$filename, $filecontents); 107 | $logger->log('enfix-merge', $changes.' string(s) fixed in '.$filename); 108 | } else if ($changes === 0) { 109 | $logger->log('enfix-merge', 'No changes in '.$filename); 110 | } else { 111 | $logger->log('enfix-merge', 'Error while processing file '.$filename, amos_cli_logger::LEVEL_ERROR); 112 | } 113 | } 114 | 115 | $logger->log('enfix-merge', 'Finished! Total of '.$total.' string(s) merged.'); 116 | -------------------------------------------------------------------------------- /tests/git_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides {@link local_amos_git_testcase} class. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2019 David Mudrák 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | defined('MOODLE_INTERNAL') || die(); 27 | 28 | global $CFG; 29 | 30 | /** 31 | * Test the implementation of {@link \local_amos\local\git} class. 32 | * 33 | * @copyright 2019 David Mudrák 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | class local_amos_git_testcase extends basic_testcase { 37 | 38 | /** 39 | * Test {@link \local_amos\local\git::exec()}. 40 | */ 41 | public function test_exec() { 42 | 43 | $repo = make_request_directory(); 44 | $git = new \local_amos\local\git($repo); 45 | 46 | $out = $git->exec('init'); 47 | $this->assertEquals('Initialized empty Git repository in '.$repo.'/.git/', $out[0]); 48 | 49 | file_put_contents($repo.'/README.txt', 'Hello world'); 50 | $git->exec('add README.txt'); 51 | $git->exec('commit -m "Adding a first file"'); 52 | 53 | $out = $git->exec('log -n 1 --oneline'); 54 | $this->assertTrue(strpos($out[0], 'Adding a first file') > 0); 55 | 56 | $this->expectException(Exception::class); 57 | $git->exec('add FOO.exe 2>/dev/null'); 58 | } 59 | 60 | /** 61 | * Test {@link \local_amos\local\git::is_success()}. 62 | */ 63 | public function test_is_success() { 64 | 65 | $repo = make_request_directory(); 66 | $git = new \local_amos\local\git($repo); 67 | 68 | $git->exec('init'); 69 | file_put_contents($repo.'/index.php', ''); 70 | $git->exec('add .'); 71 | $git->exec('commit -m "Initial commit"'); 72 | $git->exec('checkout -b slave 2>/dev/null'); 73 | 74 | $this->assertTrue($git->is_success('show-ref --verify --quiet refs/heads/master')); 75 | $this->assertTrue($git->is_success('show-ref --verify --quiet refs/heads/slave')); 76 | $this->assertFalse($git->is_success('show-ref --verify --quiet refs/heads/justice_exists')); 77 | } 78 | 79 | /** 80 | * Test {@link \local_amos\local\git::list_local_branches()}. 81 | */ 82 | public function test_list_local_branches() { 83 | 84 | $repo = make_request_directory(); 85 | $git = new \local_amos\local\git($repo); 86 | 87 | $git->exec('init'); 88 | $this->assertSame([], $git->list_local_branches()); 89 | 90 | file_put_contents($repo.'/index.php', ''); 91 | $git->exec('add .'); 92 | $git->exec('commit -m "Initial commit"'); 93 | $this->assertEquals(['master'], $git->list_local_branches()); 94 | 95 | $git->exec('checkout -b slave 2>/dev/null'); 96 | $this->assertEquals(2, count($git->list_local_branches())); 97 | $this->assertContains('master', $git->list_local_branches()); 98 | $this->assertContains('slave', $git->list_local_branches()); 99 | } 100 | 101 | /** 102 | * Test {@link \local_amos\local\git::has_local_branch()}. 103 | */ 104 | public function test_has_local_branch() { 105 | 106 | $repo = make_request_directory(); 107 | $git = new \local_amos\local\git($repo); 108 | 109 | $git->exec('init'); 110 | file_put_contents($repo.'/index.php', ''); 111 | $git->exec('add .'); 112 | $git->exec('commit -m "Initial commit"'); 113 | $git->exec('checkout -b slave 2>/dev/null'); 114 | 115 | $this->assertTrue($git->has_local_branch('master')); 116 | $this->assertTrue($git->has_local_branch('slave')); 117 | $this->assertFalse($git->has_local_branch('justice_exists')); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /cli/import-strings.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Imports English strings for a given component from PHP file 19 | * 20 | * This can be used to manually import contributed plugins, for example. Note there is an external function for this 21 | * feature, too. 22 | * 23 | * @package local_amos 24 | * @copyright 2011 David Mudrak 25 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | 28 | define('CLI_SCRIPT', true); 29 | 30 | require(__DIR__ . '/../../../config.php'); 31 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 32 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 33 | require_once($CFG->libdir . '/clilib.php'); 34 | 35 | list($options, $unrecognised) = cli_get_params([ 36 | 'lang' => 'en', 37 | 'versioncode' => null, 38 | 'timemodified' => null, 39 | 'name' => null, 40 | 'format' => 2, 41 | 'message' => '', 42 | 'userinfo' => 'David Mudrak ', 43 | 'commithash' => null, 44 | 'yes' => false, 45 | 'help' => false 46 | ], [ 47 | 'h' => 'help', 48 | 'y' => 'yes', 49 | ]); 50 | 51 | $usage = " 52 | Imports strings from a file into the AMOS repository. 53 | 54 | Usage: 55 | php import-strings.php --message='Commit message' [--other-options] /path/to/file.php 56 | 57 | Options: 58 | --message Commit message 59 | --lang Language code, defaults to 'en', 60 | --versioncode Code of the branch to commit to, defaults to most recent one 61 | --timemodified Timestamp of the commit, defaults to the file last modification time 62 | --name Name of the component, defaults to the filename 63 | --format Format of the file, defaults to 2 (Moodle 2.0+) 64 | --userinfo Committer information, defaults to 'David Mudrak ' 65 | --commithash Allows to specify the git commit hash 66 | --yes | -y It won't ask to continue 67 | --help | -h Show this usage 68 | 69 | The file is directly included into the PHP processor. Make sure to review the file 70 | for a malicious contents before you import it via this script. 71 | "; 72 | 73 | if ($options['help'] || empty($options['message']) || empty($unrecognised)) { 74 | echo $usage . PHP_EOL; 75 | exit(1); 76 | } 77 | 78 | $filepath = $unrecognised[0]; 79 | if (!is_readable($filepath)) { 80 | echo 'File "'.$filepath.'" not readable' . PHP_EOL; 81 | echo $usage . PHP_EOL; 82 | exit(2); 83 | } 84 | 85 | if ($options['versioncode']) { 86 | $version = mlang_version::by_code($options['versioncode']); 87 | } else { 88 | $version = mlang_version::latest_version(); 89 | } 90 | 91 | $component = mlang_component::from_phpfile($filepath, $options['lang'], $version, 92 | $options['timemodified'], $options['name'], (int)$options['format']); 93 | 94 | fputs(STDOUT, "{$component->name} {$component->version->label} {$component->lang}" . PHP_EOL); 95 | 96 | $stage = new mlang_stage(); 97 | $stage->add($component); 98 | $stage->rebase(null, true, $options['timemodified']); 99 | 100 | if (!$stage->has_component()) { 101 | echo 'No strings found (after rebase)' . PHP_EOL; 102 | exit(4); 103 | } 104 | 105 | foreach ($stage as $component) { 106 | foreach ($component as $string) { 107 | if ($string->deleted) { 108 | $sign = '-'; 109 | } else { 110 | $sign = '+'; 111 | } 112 | echo $sign . ' ' . $string->id . PHP_EOL; 113 | } 114 | } 115 | 116 | echo PHP_EOL; 117 | if (!$options['yes']) { 118 | $continue = cli_input('Continue? [y/n]', 'n', array('y', 'n')); 119 | if ($continue !== 'y') { 120 | echo 'Import aborted' . PHP_EOL; 121 | exit(5); 122 | } 123 | } 124 | 125 | $meta = array('source' => 'import', 'userinfo' => $options['userinfo']); 126 | if ($options['commithash']) { 127 | $meta['commithash'] = $commithash; 128 | } 129 | 130 | $stage->commit($options['message'], $meta, true); 131 | -------------------------------------------------------------------------------- /cli/enfix-cleanup.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Deletes all en_fix strings that are merged into the en pack. 20 | * 21 | * @package local_amos 22 | * @copyright 2013 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | define('CLI_SCRIPT', true); 27 | 28 | require(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->libdir . '/clilib.php'); 30 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 31 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 32 | 33 | cli_error('TODO - this needs to be updated to use the new storage system.'); 34 | 35 | list($options, $unrecognized) = cli_get_params(array('execute' => false, 'aggresive' => false)); 36 | 37 | fputs(STDOUT, "*****************************************\n"); 38 | fputs(STDOUT, date('Y-m-d H:i', time())); 39 | fputs(STDOUT, " ENFIX CLEANUP JOB STARTED\n"); 40 | 41 | // Get an information about existing strings in the en_fix 42 | $sql = "SELECT branch,lang,component,COUNT(stringid) AS numofstrings 43 | FROM {amos_repository} 44 | WHERE deleted=0 45 | AND lang='en_fix' 46 | GROUP BY branch,lang,component 47 | ORDER BY branch,lang,component"; 48 | $rs = $DB->get_recordset_sql($sql); 49 | $tree = array(); // [branch][language][component] => numofstrings 50 | foreach ($rs as $record) { 51 | $tree[$record->branch][$record->lang][$record->component] = $record->numofstrings; 52 | } 53 | $rs->close(); 54 | 55 | $stage = new mlang_stage(); 56 | 57 | foreach ($tree as $vercode => $languages) { 58 | $version = mlang_version::by_code($vercode); 59 | foreach ($languages as $langcode => $components) { 60 | if ($langcode !== 'en_fix') { 61 | throw new coding_exception('Unexpected language'); 62 | } 63 | foreach ($components as $componentname => $unused) { 64 | $en = mlang_component::from_snapshot($componentname, 'en', $version); 65 | $enfix = mlang_component::from_snapshot($componentname, $langcode, $version); 66 | $enfix->intersect($en); 67 | $removed = $enfix->complement($en); 68 | 69 | if ($options['aggresive']) { 70 | foreach ($enfix as $enfixstring) { 71 | $enstring = $en->get_string($enfixstring->id); 72 | if ($enstring === null) { 73 | fputs(STDERR, 'orphaned string '.$enfixstring->id.' in '.$componentname.' '.$version->label.PHP_EOL); 74 | continue; 75 | } 76 | if ($enstring->timemodified > $enfixstring->timemodified) { 77 | fputs(STDERR, 'string '.$enfixstring->id.' outdated in '.$componentname.' '.$version->label.PHP_EOL); 78 | $enfix->unlink_string($enfixstring->id); 79 | $removed++; 80 | } 81 | } 82 | } 83 | 84 | if ($removed) { 85 | 86 | if ($options['execute']) { 87 | $action = 'removing'; 88 | } else { 89 | $action = 'would remove'; 90 | } 91 | 92 | fputs(STDERR, $action.' '.$removed.' string(s) from '.$componentname.' '.$version->label.PHP_EOL); 93 | 94 | if ($options['execute']) { 95 | $stage->add($enfix); 96 | $stage->rebase(null, true); 97 | $msg = 'Clean-up strings that were merged into the English pack'; 98 | $stage->commit($msg, array('source' => 'bot', 'userinfo' => 'AMOS-bot '), true); 99 | } else { 100 | $stage->clear(); 101 | } 102 | } else { 103 | fputs(STDERR, 'nothing to do in '.$componentname.' '.$version->label.PHP_EOL); 104 | } 105 | $en->clear(); 106 | $enfix->clear(); 107 | } 108 | } 109 | } 110 | 111 | fputs(STDOUT, date('Y-m-d H:i', time())); 112 | fputs(STDOUT, " ENFIX CLEANUP JOB DONE\n"); 113 | -------------------------------------------------------------------------------- /cli/export-installer.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Exports the strings needed by the installer 19 | * 20 | * Moodle core contains a subset of strings needed for the start of the installation. 21 | * The list of required strings is maintained in install/stringnames.txt. This 22 | * script parses that file and exports the translations into a configured destination. 23 | * 24 | * @package local_amos 25 | * @copyright 2010 David Mudrak 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | 29 | define('CLI_SCRIPT', true); 30 | 31 | require(__DIR__ . '/../../../config.php'); 32 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 33 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 34 | 35 | $versions = array_reverse(mlang_version::list_supported(), true); 36 | 37 | $git = new \local_amos\local\git(AMOS_REPO_MOODLE); 38 | $git->exec('remote update --prune'); 39 | 40 | fputs(STDOUT, "*****************************************\n"); 41 | fputs(STDOUT, date('Y-m-d H:i', time())); 42 | fputs(STDOUT, " EXPORT INSTALLER JOB STARTED\n"); 43 | 44 | remove_dir(AMOS_EXPORT_INSTALLER_DIR, true); 45 | 46 | $exitstatus = 0; 47 | 48 | foreach ($versions as $version) { 49 | if ($git->has_remote_branch($version->branch)) { 50 | $gitbranch = 'origin/' . $version->branch; 51 | $exportdir = $version->code . '_STABLE'; 52 | 53 | } else if ($version->code == mlang_version::latest_version()->code) { 54 | $gitbranch = 'origin/master'; 55 | $exportdir = 'master'; 56 | 57 | } else { 58 | fputs(STDERR, "GIT BRANCH NOT FOUND FOR MOODLE VERSION {$version->label}\n"); 59 | exit(3); 60 | } 61 | 62 | fputs(STDOUT, "PROCESSING VERSION {$version->code} ({$gitbranch})\n"); 63 | 64 | // Read the contents of stringnames.txt on the given branch. 65 | $gitout = $git->exec('show ' . escapeshellarg($gitbranch . ':install/stringnames.txt')); 66 | 67 | $list = []; 68 | 69 | foreach ($gitout as $string) { 70 | [$stringid, $component] = array_map('trim', explode(',', $string)); 71 | $list[$component][$stringid] = true; 72 | } 73 | unset($gitout); 74 | 75 | $langs = array_keys(mlang_tools::list_languages()); 76 | 77 | $phpdoc = << $stringids) { 95 | foreach ($langs as $lang) { 96 | if ($lang === 'en_fix') { 97 | continue; 98 | } 99 | $component = mlang_component::from_snapshot($componentname, $lang, $version, null, false, false, 100 | array_keys($stringids)); 101 | if ($component->has_string()) { 102 | $file = AMOS_EXPORT_INSTALLER_DIR . '/' . $exportdir . '/install/lang/' . $lang . '/' . $component->name . '.php'; 103 | if (!file_exists(dirname($file))) { 104 | mkdir(dirname($file), 0755, true); 105 | } 106 | $component->export_phpfile($file, $phpdoc); 107 | } 108 | if ($lang === 'en') { 109 | // Check that all string were exported. 110 | foreach (array_keys($stringids) as $stringid) { 111 | if (!$component->has_string($stringid)) { 112 | fputs(STDERR, "ERROR Unknown $stringid,$componentname\n"); 113 | $exitstatus = 1; 114 | } 115 | } 116 | } 117 | $component->clear(); 118 | } 119 | } 120 | } 121 | 122 | fputs(STDOUT, date('Y-m-d H:i', time())); 123 | fputs(STDOUT, " EXPORT INSTALLER JOB DONE\n"); 124 | 125 | exit($exitstatus); 126 | -------------------------------------------------------------------------------- /tests/external_get_translator_data_test.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Unit tests for external function {@see \local_amos\external\get_translator_data}. 19 | * 20 | * @package local_amos 21 | * @category test 22 | * @copyright 2020 David Mudrák 23 | * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | class local_amos_external_get_translator_data_testcase extends local_amos_testcase { 26 | 27 | /** 28 | * Test that permission check is performed. 29 | */ 30 | public function test_execute_without_capability() { 31 | $this->resetAfterTest(true); 32 | 33 | $user = self::getDataGenerator()->create_user(); 34 | self::setUser($user); 35 | 36 | $spammer = create_role('Spammer', 'spammer', 'Prohibits contributions to the site'); 37 | assign_capability('local/amos:stage', CAP_PROHIBIT, $spammer, SYSCONTEXTID); 38 | 39 | role_assign($spammer, $user->id, SYSCONTEXTID); 40 | accesslib_clear_all_caches_for_unit_testing(); 41 | 42 | $this->expectException(required_capability_exception::class); 43 | \local_amos\external\get_translator_data::execute(''); 44 | } 45 | 46 | /** 47 | * Test basic behaviour of the method. 48 | */ 49 | public function test_execute_basics() { 50 | global $DB, $USER; 51 | $this->resetAfterTest(true); 52 | 53 | self::setAdminUser(); 54 | 55 | $this->register_language('en', 20); 56 | $this->register_language('cs', 20); 57 | 58 | $stage = new mlang_stage(); 59 | 60 | $component = new mlang_component('foo_bar', 'en', mlang_version::by_code(39)); 61 | $component->add_string(new mlang_string('foobar', 'Foo bar')); 62 | $stage->add($component); 63 | $component->clear(); 64 | 65 | $stage->commit('First string', ['source' => 'unittest']); 66 | 67 | // Emulate what URLSearchParams.toString() does on the client side. 68 | $filterquery = http_build_query([ 69 | '__lazyform_amosfilter' => 1, 70 | 'sesskey' => sesskey(), 71 | 'fcmp' => ['foo_bar', 'baz_quix'], 72 | 'flng' => ['cs'], 73 | 'flast' => 1, 74 | 'ftxt' => 'Foo bar', 75 | ], '', '&'); 76 | 77 | $response = \local_amos\external\get_translator_data::execute($filterquery); 78 | $response = external_api::clean_returnvalue(\local_amos\external\get_translator_data::execute_returns(), $response); 79 | 80 | $this->assertTrue(isset($response['json'])); 81 | 82 | $data = json_decode($response['json'], true); 83 | 84 | $this->assertArrayHasKey('permalink', $data); 85 | $this->assertEquals(1, $data['found']); 86 | $this->assertEquals(1, $data['missing']); 87 | $this->assertEquals(1, $data['missingcurrentpage']); 88 | $this->assertEquals(1, count($data['strings'])); 89 | $this->assertEquals('Foo bar', $data['strings'][0]['original']); 90 | $this->assertFalse($data['paginator']['hasmultiplepages']); 91 | $this->assertEquals(1, $data['paginator']['currentpage']); 92 | $this->assertEquals(1, $data['paginator']['navigation'][0]['label']); 93 | 94 | // Emulate what URLSearchParams.toString() does on the client side. 95 | $filterquery = http_build_query([ 96 | '__lazyform_amosfilter' => 1, 97 | 'sesskey' => sesskey(), 98 | 'fcmp' => ['foo_bar', 'baz_quix'], 99 | 'flng' => ['cs'], 100 | 'flast' => 1, 101 | 'fpg' => 3, 102 | ], '', '&'); 103 | 104 | $response = \local_amos\external\get_translator_data::execute($filterquery); 105 | $response = external_api::clean_returnvalue(\local_amos\external\get_translator_data::execute_returns(), $response); 106 | 107 | $this->assertTrue(isset($response['json'])); 108 | 109 | $data = json_decode($response['json'], true); 110 | 111 | $this->assertArrayHasKey('permalink', $data); 112 | $this->assertEquals(1, $data['found']); 113 | $this->assertEquals(1, $data['missing']); 114 | $this->assertEquals(0, $data['missingcurrentpage']); 115 | $this->assertEquals(0, count($data['strings'])); 116 | $this->assertFalse($data['paginator']['hasmultiplepages']); 117 | $this->assertEquals(3, $data['paginator']['currentpage']); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /templates/frontpage.mustache: -------------------------------------------------------------------------------- 1 | {{! 2 | This file is part of Moodle - https://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 . 16 | }} 17 | {{! 18 | @template local_plugins/frontpage 19 | 20 | Template purpose and description. 21 | 22 | Classes required for JS: 23 | * none 24 | 25 | Data attributes required for JS: 26 | * none 27 | 28 | Context variables required for this template: 29 | * attributes Array of name / value pairs. 30 | 31 | Example context (json): 32 | {} 33 | }} 34 | 35 |
    36 |
    37 |
    {{#str}} amos, local_amos {{/str}}
    38 |
    39 |
    {{#str}} about, local_amos {{/str}}
    40 |
    41 |
    42 |
    43 |
    {{#str}} contribute, local_amos {{/str}}
    44 |
    45 |

    {{#str}} contributestats, local_amos, {"count": {{#quote}} {{{contributedstrings}}} {{/quote}} } {{/str}}

    46 |

    {{#str}} contributethanks, local_amos, { "listcontributors": {{#quote}} {{{listcontributors}}} {{/quote}} } {{/str}}

    47 |

    48 | {{#str}} contributenow, local_amos {{/str}} 49 | {{#str}} creditstitleshort, local_amos {{/str}} 50 |

    51 |
    52 |
    53 | 80 |
    81 | 82 |
    83 |
    {{#str}} availablelangs, core_install {{/str}}
    84 |
    85 |

    {{#str}} languagepacks, local_amos {{/str}}

    86 |
      87 | {{#langpackstats}} 88 |
    • 89 |
      90 |
      91 | {{langname}} 92 |
      93 |
        94 | {{#childpacks}} 95 |
      • {{langname}} {{totalstrings}}
      • 96 | {{/childpacks}} 97 |
      98 |
      99 |
      100 |
      101 |
      102 |
      103 |
      104 |
      105 | {{ratio}}%{{totalstrings}} / {{totalenglish}} 106 |
      107 |
      108 |
      109 |
    • 110 | {{/langpackstats}} 111 |
    112 |
    113 |
    114 | -------------------------------------------------------------------------------- /credits.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Displays maintainers and contributors per language 20 | * 21 | * @package local_amos 22 | * @copyright 2013 David Mudrak 23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 | */ 25 | 26 | require_once(__DIR__.'/../../config.php'); 27 | require_once($CFG->dirroot.'/local/amos/locallib.php'); 28 | require_once($CFG->dirroot.'/local/amos/mlanglib.php'); 29 | require_once($CFG->dirroot.'/local/amos/renderer.php'); 30 | 31 | $editmode = optional_param('editmode', false, PARAM_BOOL); 32 | $canedit = has_capability('local/amos:manage', context_system::instance()); 33 | 34 | if (!$canedit) { 35 | $editmode = false; 36 | } else if (!$editmode) { 37 | $editmode = null; 38 | } else { 39 | $editmode = true; 40 | } 41 | 42 | $PAGE->set_context(context_system::instance()); 43 | $PAGE->set_pagelayout('standard'); 44 | $PAGE->set_url('/local/amos/credits.php'); 45 | $PAGE->set_title(get_string('creditstitleshort', 'local_amos')); 46 | $PAGE->set_heading(get_string('creditstitlelong', 'local_amos')); 47 | 48 | if ($canedit and $editmode) { 49 | $PAGE->set_button($OUTPUT->single_button(new moodle_url($PAGE->url, array('editmode' => 0)), get_string('turneditingoff'), 'get')); 50 | $PAGE->set_url(new moodle_url($PAGE->url, array('editmode' => 1))); 51 | } else if ($canedit and !$editmode) { 52 | $PAGE->set_button($OUTPUT->single_button(new moodle_url($PAGE->url, array('editmode' => 1)), get_string('turneditingon'), 'get')); 53 | } 54 | 55 | $languages = mlang_tools::list_languages(false, true, false); 56 | 57 | // Get the list of known languages. 58 | 59 | foreach ($languages as $langcode => $langname) { 60 | $list[$langcode] = (object)array('langname' => $langname, 'maintainers' => array(), 'contributors' => array()); 61 | } 62 | 63 | // Get the list of maintainers, explicitly assigned contributors and 64 | // other contributors based on submitted contributions. 65 | 66 | $userfields = user_picture::fields('u'); 67 | list($sortsql, $sortparams) = users_order_by_sql(); 68 | 69 | $sql = "SELECT t.lang AS amoslang, t.status AS contribstatus, 1 AS iseditable, {$userfields} 70 | FROM {amos_translators} t 71 | JOIN {user} u ON t.userid = u.id 72 | WHERE t.lang <> 'X' AND t.lang <> 'en' 73 | 74 | UNION 75 | 76 | SELECT c.lang AS amoslang, ".AMOS_USER_CONTRIBUTOR." AS contribstatus, 0 AS iseditable, {$userfields} 77 | FROM {amos_contributions} c 78 | JOIN {user} u ON c.authorid = u.id 79 | WHERE c.status = :status 80 | GROUP BY c.lang, {$userfields} 81 | HAVING COUNT(*) >= 3 82 | 83 | ORDER BY amoslang, contribstatus, {$sortsql}, iseditable"; 84 | 85 | $rs = $DB->get_recordset_sql($sql, array_merge($sortparams, 86 | array('status' => local_amos_contribution::STATE_ACCEPTED))); 87 | 88 | // Track credits with unexpected data. 89 | $issues = array(); 90 | 91 | foreach ($rs as $user) { 92 | 93 | $lang = $user->amoslang; 94 | if (empty($lang)) { 95 | $issues[] = (object)array( 96 | 'problem' => 'Empty contribution language', 97 | 'record' => $user, 98 | ); 99 | continue; 100 | } 101 | unset($user->amoslang); 102 | 103 | $status = $user->contribstatus; 104 | unset($user->contribstatus); 105 | 106 | if (empty($list[$lang])) { 107 | $issues[] = (object)array( 108 | 'problem' => 'Unknown language', 109 | 'record' => $user, 110 | ); 111 | continue; 112 | } 113 | 114 | if ($status == AMOS_USER_MAINTAINER) { 115 | if (!isset($list[$lang]->maintainers[$user->id])) { 116 | $list[$lang]->maintainers[$user->id] = $user; 117 | } 118 | 119 | } else if ($status == AMOS_USER_CONTRIBUTOR) { 120 | if (!isset($list[$lang]->maintainers[$user->id]) and !isset($list[$lang]->contributors[$user->id])) { 121 | $list[$lang]->contributors[$user->id] = $user; 122 | } 123 | 124 | } else { 125 | $issues[] = (object)array( 126 | 'problem' => 'Unknown credit status', 127 | 'record' => $user, 128 | ); 129 | continue; 130 | } 131 | } 132 | 133 | $rs->close(); 134 | 135 | // Output starts here 136 | echo $OUTPUT->header(); 137 | 138 | $output = $PAGE->get_renderer('local_amos'); 139 | echo $output->page_credits($list, current_language(), $editmode); 140 | 141 | if (!empty($issues) and has_capability('local/amos:manage', $PAGE->context)) { 142 | echo $output->page_credits_issues($issues); 143 | } 144 | 145 | echo $OUTPUT->footer(); 146 | -------------------------------------------------------------------------------- /importfile.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Import strings from uploaded file and stage them 20 | * 21 | * @package local 22 | * @subpackage amos 23 | * @copyright 2010 David Mudrak 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 28 | require_once(dirname(__FILE__).'/locallib.php'); 29 | require_once(dirname(__FILE__).'/mlanglib.php'); 30 | require_once(dirname(__FILE__).'/mlangparser.php'); 31 | require_once(dirname(__FILE__).'/importfile_form.php'); 32 | 33 | require_login(SITEID, false); 34 | require_capability('local/amos:importfile', context_system::instance()); 35 | 36 | $PAGE->set_pagelayout('standard'); 37 | $PAGE->set_url('/local/amos/importfile.php'); 38 | navigation_node::override_active_url(new moodle_url('/local/amos/stage.php')); 39 | $PAGE->set_title('AMOS ' . get_string('importfile', 'local_amos')); 40 | $PAGE->set_heading('AMOS ' . get_string('importfile', 'local_amos')); 41 | 42 | $importform = new local_amos_importfile_form(null, local_amos_importfile_options()); 43 | 44 | if (($data = $importform->get_data()) and has_capability('local/amos:stage', context_system::instance())) { 45 | $tmpdir = $CFG->dataroot . '/amos/temp/import-uploads/' . $USER->id; 46 | check_dir_exists($tmpdir); 47 | $filenameorig = basename($importform->get_new_filename('importfile')); 48 | $filename = $filenameorig . '-' . md5(time() . '-' . $USER->id . '-'. random_string(20)); 49 | $pathname = $tmpdir . '/' . $filename; 50 | 51 | if ($importform->save_file('importfile', $pathname)) { 52 | 53 | // Prepare the list of files to import. 54 | $stringfiles = array(); 55 | 56 | if (strtolower(substr($filenameorig, -4)) === '.zip') { 57 | $tmpzipdir = $pathname . '-content'; 58 | check_dir_exists($tmpzipdir); 59 | $fp = get_file_packer('application/zip'); 60 | $zipcontents = $fp->extract_to_pathname($pathname, $tmpzipdir); 61 | if (!$zipcontents) { 62 | notice(get_string('novalidzip', 'local_amos'), new moodle_url('/local/amos/stage.php')); 63 | @remove_dir($tmpzipdir); 64 | } else { 65 | foreach ($zipcontents as $zipfilename => $zipfilestatus) { 66 | // We want PHP files in the root of the ZIP only. 67 | if ($zipfilestatus === true and basename($zipfilename) === $zipfilename and strtolower(substr($zipfilename, -4)) === '.php') { 68 | $stringfiles[$zipfilename] = $tmpzipdir . '/' . $zipfilename; 69 | } 70 | } 71 | } 72 | 73 | } else if (strtolower(substr($filenameorig, -4)) === '.php') { 74 | $stringfiles = array($filenameorig => $pathname); 75 | } 76 | 77 | if (empty($stringfiles)) { 78 | notice(get_string('nostringtoimport', 'local_amos'), new moodle_url('/local/amos/stage.php')); 79 | 80 | } else { 81 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 82 | $version = mlang_version::by_code($data->version); 83 | $parser = mlang_parser_factory::get_parser('php'); 84 | 85 | foreach ($stringfiles as $filenameorig => $pathname) { 86 | $name = mlang_component::name_from_filename($filenameorig); 87 | $component = new mlang_component($name, $data->language, $version); 88 | 89 | try { 90 | $parser->parse(file_get_contents($pathname), $component); 91 | 92 | } catch (mlang_parser_exception $e) { 93 | notice($e->getMessage(), new moodle_url('/local/amos/stage.php')); 94 | } 95 | 96 | $encomponent = mlang_component::from_snapshot($component->name, 'en', $version); 97 | $component->intersect($encomponent); 98 | 99 | if ($component->has_string()) { 100 | $stage->add($component, true); 101 | $component->clear(); 102 | $stage->store(); 103 | } 104 | } 105 | mlang_stash::autosave($stage); 106 | } 107 | 108 | if (!empty($tmpzipdir)) { 109 | @remove_dir($tmpzipdir); 110 | } 111 | 112 | } else { 113 | notice(get_string('nofiletoimport', 'local_amos'), new moodle_url('/local/amos/stage.php')); 114 | } 115 | } 116 | 117 | if (!isset($stage) or !$stage->has_component()) { 118 | notice(get_string('nostringtoimport', 'local_amos'), new moodle_url('/local/amos/stage.php')); 119 | } 120 | 121 | redirect(new moodle_url('/local/amos/stage.php')); 122 | --------------------------------------------------------------------------------