├── bin ├── .htaccess └── detect.sh ├── .htaccess ├── .gitignore ├── tests ├── fixtures │ ├── merge002.php │ ├── parserdata003.txt │ ├── parserdata004.txt │ ├── parserdata001.txt │ ├── merge001.php │ ├── merge004.php │ ├── merge003.php │ └── parserdata002.txt ├── privacy_provider_test.php ├── source_code_test.php └── git_test.php ├── .mdtconfig ├── jobs ├── checker-separators ├── checker-pending-contributions ├── en-mergefix ├── zip-packs-make ├── en-orphan ├── zip-packs-pull ├── install-packs-make ├── zip-packs-publish ├── en-track ├── zip-packs-publish-s3 ├── run ├── en-fixdrift ├── en-revclean ├── en-pull ├── zip-packs-snapshot └── install-packs-publish ├── scss ├── sitefrontpage.scss ├── variables.scss ├── index.scss ├── strings.scss ├── comment.scss ├── filter.scss └── stage.scss ├── package.json ├── settings.php ├── README.md ├── CHANGES.md ├── cli ├── install.sh ├── enfix-symlinks.sh ├── export-zip.php ├── automerge.php ├── fix-revclean-time.php ├── init-texts.php ├── config-dist.php ├── import-greylist.php ├── dump-stringids.php ├── export-php.php ├── clean-texts.php ├── enfix-export.php ├── make-branch.php ├── intersect.php ├── compare-packs.php ├── enfix-merge.php ├── import-strings.php ├── enfix-cleanup.php ├── export-installer.php └── rev-clean.php ├── Gruntfile.js ├── version.php ├── HOWTO-BOOTSTRAP.txt ├── db ├── log.php ├── tasks.php ├── messages.php ├── services.php └── access.php ├── classes ├── external │ ├── api.php │ └── plugin_translation_stats.php ├── contributor_selector.php ├── maintainer_selector.php └── task │ └── import_app_strings.php ├── local_amos.yml ├── execute_form.php ├── HOWTO-ENFIX.txt ├── yui ├── stash │ └── stash.js └── timeline │ └── timeline.js ├── stash_form.php ├── log.php ├── importfile_form.php ├── merge_form.php ├── unstage.ajax.php ├── admin ├── newlanguage.php └── newlanguage_form.php ├── uptodate.ajax.php ├── rest ├── view.php ├── diff_form.php ├── saveajax.php ├── index.php ├── execute.php ├── HOWTO-BRANCHING.txt ├── untranslate.php ├── frontpage.php ├── merge.php ├── timeline.php ├── translate.ajax.php ├── credits.php └── importfile.php /bin/.htaccess: -------------------------------------------------------------------------------- 1 | order deny,allow 2 | deny from all 3 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | 2 | ForceType application/x-httpd-php 3 | 4 | -------------------------------------------------------------------------------- /.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 | and should be parsed'; 6 | $string['valid2'] = 'Multiline 7 | string'; 8 | $string['valid3'] = 'What \$a\'Pe%%\\"be'; // comment 9 | 10 | ?> 11 | -------------------------------------------------------------------------------- /jobs/zip-packs-pull: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Update language packs used at lang.moodle.org 4 | # Once they are available, use the newly generated language packs at 5 | # http://lang.moodle.org itself. 6 | # upstream: zip-packs-commit 7 | 8 | cd $CFGDATAROOT/lang/ 9 | git pull 10 | cd $CFGDIRROOT 11 | php admin/cli/purge_caches.php 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | $amos-color-red: #ffd3d9; 6 | 7 | $amos-color-missing: $amos-color-red; 8 | $amos-color-committable: #e7f1c3; 9 | $amos-color-outdated: #f3f2aa; 10 | $amos-color-greylisted: #f7f7f7; 11 | $amos-color-staged: #d2ebff; 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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-revclean 10 | 11 | php $AMOSCLIROOT/fix-drift.php --execute 12 | -------------------------------------------------------------------------------- /jobs/en-revclean: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Reverse cleanup 4 | # Propagates deletions of English strings into other lang packs 5 | # The procedure is known as reverse cleanup. This script takes the most recent 6 | # snapshot of every component in the English lang pack. If a string removal is 7 | # part of the snapshot, the script propagates such removal into all other 8 | # languages. If the string is already removed in the other language, the 9 | # removing commit is not recorded. 10 | # upstream: en-fixdrift 11 | # downstream: en-orphan 12 | 13 | php $AMOSCLIROOT/rev-clean.php 14 | -------------------------------------------------------------------------------- /settings.php: -------------------------------------------------------------------------------- 1 | dirroot . '/mod/assign/adminlib.php'); 5 | $settings = new admin_settingpage('local_amos', get_string('pluginname', 'local_amos')); 6 | $ADMIN->add('localplugins', $settings); 7 | 8 | if ($ADMIN->fulltree) { 9 | $settings->add(new admin_setting_configtext('local_amos/applangindexfile', get_string('applangindexfile', 'local_amos'), get_string('applangindexfile_desc', 'local_amos'), 'https://raw.githubusercontent.com/moodlehq/moodlemobile2/integration/scripts/langindex.json', PARAM_TEXT)); 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cli/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 1. Edit DIRECTORY variable where the language packs are installed. Be sure you are on the branch you want to be. 4 | DIRECTORY='/Users/user/git/moodle-langpacks' 5 | 6 | # 2. Edit the branch name and USERINFO. 7 | USERINFO='Initial install ' 8 | 9 | # 3. Run the script from local/amos directory. 10 | # ./cli/install.sh 11 | 12 | # 4. Execute this SQL (number 2 is the userid to make amos manager). 13 | # Also an AMOS manager role should be added and assigned to the user. 14 | # INSERT INTO mdl_amos_translators ( userid, lang, status ) VALUES (2, 'X', 0); 15 | 16 | pushd "$DIRECTORY" > /dev/null 17 | BRANCH=`git branch 2>/dev/null | grep '*' | sed 's/* \(.*\)/\1/'` 18 | popd > /dev/null 19 | 20 | filelist=`ls $DIRECTORY/en/*.php` 21 | for langfile in $filelist 22 | do 23 | php cli/import-strings.php --message='First import' --version=$BRANCH --userinfo="$USERINFO" --yes $langfile 24 | done -------------------------------------------------------------------------------- /jobs/zip-packs-snapshot: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Commit the snapshot of language packs 4 | # Everytime new ZIP language packages are generated, their content is pushed into 5 | # a custom Git repository at http://git.moodle.org/ 6 | # upstream: zip-packs-publish 7 | # downstream: zip-packs-pull 8 | 9 | CLONESROOT=${AMOSDATAROOT}/repos/moodle-langpacks-clones 10 | 11 | rsync -a --exclude 'README*' --exclude '*.zip' --exclude '.git' $AMOSDATAROOT/temp/export-zip/ $CLONESROOT 12 | 13 | for VER in 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 ; do 14 | cd $CLONESROOT/$VER 15 | set +e 16 | git add . 17 | GIT_AUTHOR_NAME="AMOS bot" GIT_AUTHOR_EMAIL="amos@moodle.org" GIT_COMMITTER_NAME="AMOS bot" GIT_COMMITTER_EMAIL="amos@moodle.org" git commit -m 'Moodle language packs snapshot' . 18 | set -e 19 | git push 20 | done 21 | 22 | cd ${AMOSDATAROOT}/repos/moodle-langpacks 23 | git push git@git.in.moodle.com:moodle/moodle-langpacks.git refs/heads/*:refs/heads/* 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /version.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Defines the version of AMOS 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 | $plugin->component = 'local_amos'; 29 | $plugin->version = 2019052900; 30 | $plugin->release = '3.7.0'; 31 | $plugin->maturity = MATURITY_STABLE; 32 | $plugin->requires = 2019052000; 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cli/export-zip.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Prepares language packages for Moodle 2.0 in ZIP format to be published 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 | define('CLI_SCRIPT', true); 27 | 28 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 30 | 31 | $logger = new amos_cli_logger(); 32 | 33 | $exporter = new amos_export_zip($logger); 34 | $exporter->init(); 35 | $exporter->rebuild_zip_packages(); 36 | $exporter->rebuild_output_folders(); 37 | $exporter->finalize(); 38 | -------------------------------------------------------------------------------- /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/log.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package local_amos 20 | * @category log 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 | global $DB; 28 | 29 | $logs = array( 30 | array( 31 | 'module' => 'amos', 32 | 'action' => 'usegoogle', 33 | 'mtable' => 'amos_repository', 34 | 'field' => $DB->sql_concat("'['", "stringid", "', '", "component", "']'") 35 | ), 36 | 37 | // A string translation is staged via AJAX call from the translator 38 | array( 39 | 'module' => 'amos', 40 | 'action' => 'stage', 41 | 'mtable' => '', 42 | 'field' => '', 43 | ), 44 | ); 45 | -------------------------------------------------------------------------------- /scss/strings.scss: -------------------------------------------------------------------------------- 1 | // The table like overview of strings used at the translator and the stage 2 | 3 | .path-local-amos { 4 | 5 | #amostranslator, #amosstage { 6 | 7 | margin: 1.5em auto; 8 | 9 | .string-control-group { 10 | margin-bottom: 1em; 11 | overflow: hidden; 12 | } 13 | 14 | .string-control-label { 15 | font-size: 66%; 16 | color: #7f7f7f; 17 | 18 | .span6 { 19 | min-height: 0; // overwrite bootstrap's defaults here 20 | } 21 | 22 | img.iconhelp { 23 | width: 10px; 24 | height: 10px; 25 | padding: 0 2px; 26 | vertical-align: baseline; 27 | } 28 | } 29 | 30 | .string-text { 31 | border: 1px solid rgb(204,204,204); 32 | @include border-radius(4px); 33 | margin-bottom: 5px; 34 | 35 | .preformatted { 36 | margin: 10px; 37 | min-height: 3em; 38 | white-space: pre-wrap; /* css-3 should we be so lucky... */ 39 | white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ 40 | white-space: -pre-wrap; /* Opera 4-6 ?? */ 41 | white-space: -o-pre-wrap; /* Opera 7 ?? */ 42 | word-wrap: break-word; /* Internet Explorer 5.5+ */ 43 | _white-space: pre; /* IE only hack to re-specify in addition to word-wrap */ 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /classes/external/api.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides the {\local_amos\external\api} class. 19 | * 20 | * @package local_amos 21 | * @category external 22 | * @copyright 2019 David Mudrák 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 | require_once($CFG->libdir.'/externallib.php'); 31 | 32 | /** 33 | * Provides the AMOS external API. 34 | * 35 | * Each external function is implemented in its own trait. This class aggregates them all. 36 | * 37 | * @copyright 2019 David Mudrák 38 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 | */ 40 | class api extends \external_api { 41 | 42 | use plugin_translation_stats; 43 | use update_strings_file; 44 | } 45 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | amos_filter_usage: 38 | - timesubmitted 39 | - userlang 40 | - currentlang 41 | - usercountry 42 | - ismaintainer 43 | - usesdefaultversion 44 | - usesdefaultlang 45 | - numofversions 46 | - numoflanguages 47 | - numofcomponents 48 | - showmissingonly 49 | - showoutdatedonly 50 | - showexistingonly 51 | - showhelpsonly 52 | - withsubstring 53 | - substringregex 54 | - substringcasesensitive 55 | - withstringid 56 | - stringidpartial 57 | - showstagedonly 58 | - showgreylistedonly 59 | - showwithoutgreylisted 60 | subsystems: 61 | - comment 62 | external: 63 | - languagepacks: 64 | - firstname 65 | - lastname 66 | - email 67 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bin/detect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script is used to populate AMOS greylist 4 | # 5 | # @author David Mudrak 6 | # 7 | # To use this script, get a vanilla Moodle source code in moodle/ 8 | # subdirectory and amos-all-stringids.txt generated by dump-stringids.php 9 | # Be ware it takes several hours to populate the new greylist.txt 10 | 11 | # dump of all strings generated by AMOS CLI dump-stringids.php 12 | STRINGS=amos-all-stringids.txt 13 | 14 | # the file to append greylisted strings into 15 | GREYLIST=greylist.txt 16 | 17 | # the file to append weirdlisted strings into 18 | WEIRDLIST=weirdlist.txt 19 | 20 | ################################################################################ 21 | 22 | total=$(cat $STRINGS|wc -l) 23 | let processed=0 24 | let greylisted=0 25 | 26 | while read string; do 27 | # extract the string identifer from the [stringid,component] format 28 | stringid=$(echo $string|sed 's/^\[\(.\+\),.*$/\1/') 29 | # search for stringid pattern in the moodle source code 30 | # stop searching after first two occurrences and return the number of occurrences 31 | # the string should be greylisted if it is found just once in the language pack 32 | # zero occurrance should be reviewed, too as they are not removed from AMOS repository 33 | numoflines=$(grep -w -r -i $stringid moodle/* | head -n 2 | wc -l) 34 | if [ $numoflines -eq 1 ]; then 35 | (( greylisted++ )) 36 | echo $string >> $GREYLIST 37 | fi 38 | if [ $numoflines -eq 0 ]; then 39 | echo $string >> $WEIRDLIST 40 | fi 41 | (( processed++ )) 42 | 43 | if [ -n $processed ]; then 44 | ratio=$(echo $processed/$total*100|bc -l) 45 | else 46 | ratio=0 47 | fi 48 | 49 | echo -n -e "\rprocessed: $processed/$total " $(printf "%0.1f%%" $ratio) "greylisted: $greylisted" 50 | done < $STRINGS 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 | -------------------------------------------------------------------------------- /cli/automerge.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Automerge components 20 | * 21 | * @package local_amos 22 | * @subpackage cli 23 | * @copyright 2013 David Mudrak 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | define('CLI_SCRIPT', true); 28 | 29 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 32 | 33 | $logger = new amos_cli_logger(); 34 | 35 | $logger->log('automerge', 'Loading list of components ...', amos_cli_logger::LEVEL_DEBUG); 36 | 37 | $components = $DB->get_fieldset_sql("SELECT DISTINCT component 38 | FROM {amos_snapshot} 39 | WHERE lang='en'"); 40 | $count = count($components); 41 | $i = 1; 42 | foreach ($components as $component) { 43 | $logger->log('automerge', $i.'/'.$count.' automerging '.$component.' ...'); 44 | mlang_tools::auto_merge($component); 45 | $i++; 46 | } 47 | 48 | $logger->log('automerge', 'Done!'); 49 | -------------------------------------------------------------------------------- /yui/stash/stash.js: -------------------------------------------------------------------------------- 1 | /** 2 | * YUI module for AMOS stash page 3 | * 4 | * @author David Mudrak 5 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6 | */ 7 | YUI.add('moodle-local_amos-stash', function(Y) { 8 | 9 | var STASH = function() { 10 | STASH.superclass.constructor.apply(this, arguments); 11 | } 12 | 13 | Y.extend(STASH, Y.Base, { 14 | initializer : function(config) { 15 | this.setUpActionBar(); 16 | this.protectDropStashButtons(); 17 | }, 18 | 19 | setUpActionBar : function() { 20 | Y.all('.stashwrapper .actions').setStyle('visibility', 'hidden'); 21 | Y.all('.stashwrapper').on('mouseover', function(e) { 22 | Y.log(e.currentTarget); 23 | e.currentTarget.addClass('mousein'); 24 | e.currentTarget.one('.actions').setStyle('visibility', 'visible'); 25 | }); 26 | Y.all('.stashwrapper').on('mouseout', function(e) { 27 | e.currentTarget.removeClass('mousein'); 28 | e.currentTarget.one('.actions').setStyle('visibility', 'hidden'); 29 | }); 30 | }, 31 | 32 | protectDropStashButtons : function() { 33 | Y.all('.stashwrapper .actions .drop form').on('submit', function(e) { 34 | if (!confirm(M.util.get_string('confirmaction', 'local_amos'))) { 35 | e.halt(); 36 | } 37 | }); 38 | } 39 | 40 | }, { 41 | NAME : 'amos_stash', 42 | ATTRS : { 43 | aparam : {} 44 | } 45 | }); 46 | 47 | M.local_amos = M.local_amos || {}; 48 | 49 | M.local_amos.init_stash = function(config) { 50 | return new STASH(config); 51 | } 52 | 53 | }, '@VERSION@', { requires:['base', 'event-mouseenter'] }); 54 | -------------------------------------------------------------------------------- /cli/fix-revclean-time.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Fix the timemodified timestamp of the strings removed by reverse cleanup 19 | * 20 | * This should be executed just once to fix the database records. 21 | * 22 | * @package local 23 | * @subpackage amos 24 | * @copyright 2011 David Mudrak 25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 26 | */ 27 | 28 | define('CLI_SCRIPT', true); 29 | 30 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 31 | 32 | $sqls = "SELECT r.id, c.timecommitted"; 33 | $sqlc = "SELECT COUNT(*)"; 34 | $sql = " FROM {amos_repository} r 35 | JOIN {amos_commits} c ON r.commitid = c.id 36 | WHERE c.source = 'revclean' 37 | AND r.timemodified <> c.timecommitted"; 38 | 39 | $count = $DB->count_records_sql($sqlc . $sql); 40 | $rs = $DB->get_recordset_sql($sqls . $sql); 41 | 42 | $i = 0; 43 | foreach ($rs as $record) { 44 | $DB->set_field('amos_repository', 'timemodified', $record->timecommitted, array('id' => $record->id)); 45 | $i++; 46 | echo "\r$i/$count"; 47 | } 48 | $rs->close(); 49 | 50 | echo "\n"; 51 | -------------------------------------------------------------------------------- /jobs/install-packs-publish: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 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 | REPO=${AMOSDATAROOT}/repos/moodle-install/ 8 | 9 | # 10 | # STEP 1 - update upstream tracking branches 11 | # 12 | 13 | cd $REPO 14 | git fetch upstream 15 | for BRANCH in master MOODLE_37_STABLE MOODLE_36_STABLE MOODLE_35_STABLE; do 16 | git checkout $BRANCH 17 | git merge upstream/$BRANCH 18 | done 19 | 20 | # 21 | # STEP 2 - merge install strings 22 | # 23 | 24 | # $1 the install branch in git 25 | # $2 the upstream branch 26 | # $3 the directory in export-install 27 | function update_branch { 28 | cd $REPO 29 | git checkout $1 30 | GIT_AUTHOR_NAME="AMOS bot" GIT_AUTHOR_EMAIL="amos@moodle.org" GIT_COMMITTER_NAME="AMOS bot" GIT_COMMITTER_EMAIL="amos@moodle.org" git merge $2 31 | 32 | cd $REPO/install 33 | rm -rf lang 34 | cp -r $AMOSDATAROOT/export-install/$3/install/lang . 35 | set +e 36 | # Commit changed and deleted files first 37 | GIT_AUTHOR_NAME="AMOS bot" GIT_AUTHOR_EMAIL="amos@moodle.org" GIT_COMMITTER_NAME="AMOS bot" GIT_COMMITTER_EMAIL="amos@moodle.org" git commit -a -m "Automatically generated installer lang files" 38 | # Add and commit new files 39 | git add . 40 | GIT_AUTHOR_NAME="AMOS bot" GIT_AUTHOR_EMAIL="amos@moodle.org" GIT_COMMITTER_NAME="AMOS bot" GIT_COMMITTER_EMAIL="amos@moodle.org" git commit -a -m "Automatically generated installer lang files" 41 | set -e 42 | 43 | cd $REPO 44 | git push git@git.in.moodle.com:amosbot/moodle-install.git $1:$1 45 | git push git@git.in.moodle.com:amosbot/moodle-install.git $2:$2 46 | } 47 | 48 | update_branch install_master master 3.8 49 | update_branch install_37_STABLE MOODLE_37_STABLE 3.7 50 | update_branch install_36_STABLE MOODLE_36_STABLE 3.6 51 | update_branch install_35_STABLE MOODLE_35_STABLE 3.5 52 | 53 | cd $REPO 54 | git gc 55 | -------------------------------------------------------------------------------- /cli/init-texts.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Populate the amos_texts table 20 | * 21 | * @package local_amos 22 | * @subpackage cli 23 | * @copyright 2013 David Mudrak 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | define('CLI_SCRIPT', true); 28 | 29 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 32 | 33 | $logger = new amos_cli_logger(); 34 | 35 | $rs = $DB->get_recordset('amos_repository', array('textid' => null), '', 'id, text'); 36 | 37 | foreach ($rs as $record) { 38 | $texthash = sha1($record->text); 39 | $known = $DB->get_record('amos_texts', array('texthash' => $texthash), 'id', IGNORE_MISSING); 40 | if ($known === false) { 41 | $action = 'N'; // New 42 | $textid = $DB->insert_record('amos_texts', array('texthash' => $texthash, 'text' => $record->text), true, true); 43 | } else { 44 | $action = 'R'; // Reused 45 | $textid = $known->id; 46 | } 47 | $DB->set_field('amos_repository', 'textid', $textid, array('id' => $record->id)); 48 | $logger->log('init-texts', $action.' '.$texthash.' '.$record->id); 49 | } 50 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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\api', 31 | 'methodname' => 'update_strings_file', 32 | 'classpath' => '', 33 | 'description' => 'Imports strings from a string file.', 34 | 'type' => 'write', 35 | ], 36 | 'local_amos_plugin_translation_stats' => [ 37 | 'classname' => '\local_amos\external\api', 38 | 'methodname' => 'plugin_translation_stats', 39 | 'classpath' => '', 40 | 'description' => 'Get translation statistics for the given component / plugin.', 41 | 'type' => 'read', 42 | ], 43 | ]; 44 | 45 | $services = [ 46 | 'AMOS integration with the Moodle Plugins directory' => [ 47 | 'functions' => [ 48 | 'local_amos_update_strings_file', 49 | 'local_amos_plugin_translation_stats', 50 | ], 51 | 'requiredcapability' => 'local/amos:importstrings', 52 | 'restrictedusers' => 1, 53 | 'enabled' => 1, 54 | ], 55 | ]; 56 | -------------------------------------------------------------------------------- /log.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * AMOS log 20 | * 21 | * @todo use the same filter as translator has to allow fine grained log output 22 | * @package local-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($CFG->dirroot . '/local/amos/locallib.php'); 29 | require_once($CFG->dirroot . '/local/amos/log_form.php'); 30 | 31 | require_login(SITEID); 32 | 33 | $PAGE->set_pagelayout('standard'); 34 | $PAGE->set_url('/local/amos/log.php'); 35 | $PAGE->set_title('AMOS ' . get_string('log', 'local_amos')); 36 | $PAGE->set_heading('AMOS ' . get_string('log', 'local_amos')); 37 | 38 | $output = $PAGE->get_renderer('local_amos'); 39 | 40 | $filterform = new local_amos_log_form(); 41 | if ($formdata = $filterform->get_data()) { 42 | $filter = (array)$formdata; 43 | if (empty($formdata->langenabled)) { 44 | unset($filter['lang']); 45 | } 46 | if (empty($formdata->componentenabled)) { 47 | unset($filter['component']); 48 | } 49 | $records = new local_amos_log($filter); 50 | } 51 | 52 | /// Output starts here 53 | echo $output->header(); 54 | $filterform->display(); 55 | if (isset($records)) { 56 | echo $output->render($records); 57 | } 58 | echo $output->footer(); 59 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /merge_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_merge_form extends moodleform { 30 | function definition() { 31 | $mform =& $this->_form; 32 | 33 | $mform->addElement('select', 'sourceversion', get_string('sourceversion', 'local_amos'), $this->_customdata['sourceversions']); 34 | $mform->setDefault('sourceversion', $this->_customdata['defaultsourceversion']); 35 | $mform->addRule('sourceversion', null, 'required', null, 'client'); 36 | 37 | $mform->addElement('select', 'targetversion', get_string('targetversion', 'local_amos'), $this->_customdata['targetversions']); 38 | $mform->setDefault('targetversion', $this->_customdata['defaulttargetversion']); 39 | $mform->addRule('targetversion', null, 'required', null, 'client'); 40 | 41 | $mform->addElement('select', 'language', get_string('language', 'local_amos'), $this->_customdata['languages']); 42 | $mform->setDefault('language', $this->_customdata['languagecurrent']); 43 | $mform->addRule('language', null, 'required', null, 'client'); 44 | 45 | $this->add_action_buttons(false, get_string('merge', 'local_amos')); 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/import-greylist.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Imports greylisted strings from text file 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 | define('CLI_SCRIPT', true); 27 | 28 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->libdir.'/clilib.php'); 32 | 33 | list($options, $unrecognized) = cli_get_params(array('greylist'=>false, 'help'=>false), array('h'=>'help')); 34 | 35 | if ($options['help'] or !$options['greylist']) { 36 | echo 'Usage: '.basename(__FILE__).' --greylist=greylist.txt' . PHP_EOL; 37 | exit(0); 38 | } 39 | 40 | $DB->delete_records('amos_greylist'); // truncate the table 41 | $greylist = fopen($options['greylist'], 'r'); 42 | while (($string = fgets($greylist, 514)) !== false) { 43 | $matches = array(); 44 | if (preg_match('/^\[(.+),(.+)\]$/', $string, $matches)) { 45 | $item = new stdClass(); 46 | $item->branch = mlang_version::MOODLE_20; 47 | $item->stringid = $matches[1]; 48 | $item->component = $matches[2]; 49 | try { 50 | $DB->insert_record('amos_greylist', $item, false, true); 51 | } 52 | catch (dml_write_exception $e) { 53 | echo $e->getMessage() . ' ' . $string . PHP_EOL; 54 | } 55 | } 56 | } 57 | fclose($greylist); 58 | -------------------------------------------------------------------------------- /unstage.ajax.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Unstage a single string via AJAX request from the stage page 20 | * 21 | * @package local 22 | * @supackage amos 23 | * @copyright 2011 David Mudrak 24 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 | */ 26 | 27 | define('AJAX_SCRIPT', true); 28 | 29 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 30 | //require_once(dirname(__FILE__).'/locallib.php'); 31 | require_once(dirname(__FILE__).'/mlanglib.php'); 32 | 33 | require_login(SITEID, false); 34 | if (!has_capability('local/amos:stage', context_system::instance())) { 35 | header('HTTP/1.1 403 Forbidden'); 36 | die(); 37 | } 38 | if (!confirm_sesskey()) { 39 | header('HTTP/1.1 403 Forbidden'); 40 | die(); 41 | } 42 | 43 | $name = required_param('component', PARAM_ALPHANUMEXT); 44 | $branch = required_param('branch', PARAM_INT); 45 | $lang = required_param('lang', PARAM_ALPHANUMEXT); 46 | $unstage = required_param('unstage', PARAM_STRINGID); 47 | 48 | $response = new stdClass(); 49 | $response->success = true; 50 | $response->error = ''; 51 | 52 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 53 | $component = $stage->get_component($name, $lang, mlang_version::by_code($branch)); 54 | if ($component) { 55 | $component->unlink_string($unstage); 56 | $stage->store(); 57 | } else { 58 | $response->success = false; 59 | $response->error = 'Unable to get load component'; 60 | } 61 | 62 | header('Content-Type: application/json; charset: utf-8'); 63 | echo json_encode($response); 64 | -------------------------------------------------------------------------------- /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 | $PAGE->set_pagelayout('standard'); 34 | $PAGE->set_url('/local/amos/admin/newlanguage.php'); 35 | $PAGE->set_title('AMOS ' . get_string('newlanguage', 'local_amos')); 36 | $PAGE->set_heading('AMOS ' . get_string('newlanguage', 'local_amos')); 37 | 38 | $form = new local_amos_newlanguage_form(); 39 | 40 | if ($data = $form->get_data()) { 41 | $component = new mlang_component('langconfig', $data->langcode, mlang_version::by_code(mlang_version::MOODLE_38)); 42 | $data->langname = mlang_string::fix_syntax($data->langname); 43 | $data->langnameint = mlang_string::fix_syntax($data->langnameint); 44 | $component->add_string(new mlang_string('thislanguage', $data->langname)); 45 | $component->add_string(new mlang_string('thislanguageint', $data->langnameint)); 46 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 47 | $stage->add($component); 48 | $stage->store(); 49 | redirect(new moodle_url('/local/amos/stage.php')); 50 | } 51 | 52 | /// Output starts here 53 | echo $OUTPUT->header(); 54 | $form->display(); 55 | echo $OUTPUT->footer(); 56 | -------------------------------------------------------------------------------- /uptodate.ajax.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Mark a single string as up-to-date using AJAX 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 | define('AJAX_SCRIPT', true); 27 | 28 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 29 | //require_once($CFG->dirroot . '/local/amos/locallib.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | 32 | require_login(SITEID, false); 33 | if (!has_capability('local/amos:commit', context_system::instance())) { 34 | header('HTTP/1.1 403 Forbidden'); 35 | die(); 36 | } 37 | if (!confirm_sesskey()) { 38 | header('HTTP/1.1 403 Forbidden'); 39 | die(); 40 | } 41 | 42 | $amosid = optional_param('amosid', null, PARAM_INT); 43 | 44 | if (is_null($amosid)) { 45 | header('HTTP/1.1 400 Bad Request'); 46 | die(); 47 | } 48 | 49 | $allowedlangs = mlang_tools::list_allowed_languages($USER->id); 50 | $allowed = false; 51 | if (!empty($allowedlangs['X'])) { 52 | // can commit into all languages 53 | $allowed = true; 54 | } else { 55 | $string = $DB->get_record('amos_repository', array('id' => $amosid), 'lang'); 56 | $allowed = !empty($allowedlangs[$string->lang]); 57 | } 58 | 59 | if (!$allowed) { 60 | header('HTTP/1.1 403 Forbidden'); 61 | die(); 62 | } 63 | 64 | $timeupdated = time(); 65 | $DB->set_field('amos_repository', 'timeupdated', $timeupdated, array('id' => $amosid)); 66 | 67 | header('Content-Type: application/json; charset: utf-8'); 68 | $response = new stdclass(); 69 | $response->timeupdated = $timeupdated; 70 | echo json_encode($response); 71 | -------------------------------------------------------------------------------- /rest: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Simple REST services provider 20 | * 21 | * At the moment, the only supported REST method is /greylist returning 22 | * the list of greylisted strings. 23 | * 24 | * @package local_amos 25 | * @copyright 2012 David Mudrak 26 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 | */ 28 | 29 | define('NO_DEBUG_DISPLAY', true); 30 | define('NO_MOODLE_COOKIES', true); 31 | 32 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 33 | 34 | $request = false; 35 | if (isset($_SERVER['PATH_INFO'])) { 36 | if (isset($_SERVER['SCRIPT_NAME']) and strpos($_SERVER['PATH_INFO'], $_SERVER['SCRIPT_NAME']) === 0) { 37 | $request = substr($_SERVER['PATH_INFO'], strlen($_SERVER['SCRIPT_NAME'])); 38 | } else { 39 | $request = $_SERVER['PATH_INFO']; 40 | } 41 | $request = clean_param($request, PARAM_PATH); 42 | } 43 | 44 | if ($request === '/greylist') { 45 | $format = optional_param('format', 'csv', PARAM_ALPHA); 46 | if ($format !== 'csv') { 47 | header($_SERVER["SERVER_PROTOCOL"]." 400 Bad Request (unsupported format)"); 48 | die(); 49 | } 50 | 51 | $rs = $DB->get_recordset('amos_greylist', null, 'branch,component,stringid', 'branch,component,stringid'); 52 | 53 | if ($format === 'csv') { 54 | header('Content-type: text/csv'); 55 | header('Content-disposition: attachment;filename=amos-greylist-'.time().'.csv'); 56 | } 57 | 58 | foreach ($rs as $string) { 59 | printf("%s,%s,%s\n", $string->branch, $string->component, $string->stringid); 60 | } 61 | $rs->close(); 62 | 63 | } else { 64 | header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found"); 65 | } 66 | -------------------------------------------------------------------------------- /cli/dump-stringids.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Prints the list of all currently known strings 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 | define('CLI_SCRIPT', true); 27 | 28 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->libdir.'/clilib.php'); 32 | 33 | list($options, $unrecognized) = cli_get_params(array('branch'=>false, 'help'=>false), array('h'=>'help')); 34 | 35 | if ($options['help'] or !$options['branch']) { 36 | echo 'Usage: '.basename(__FILE__).' --branch=MOODLE_XX_STABLE' . PHP_EOL; 37 | exit(0); 38 | } 39 | 40 | $version = mlang_version::by_branch($options['branch']); 41 | 42 | if (is_null($version)) { 43 | echo 'Unknown branch' . PHP_EOL; 44 | exit(1); 45 | } 46 | 47 | // Let us get an information about existing components on the given branch 48 | $sql = "SELECT DISTINCT component 49 | FROM {amos_repository} 50 | WHERE deleted=0 AND lang='en' AND branch=? 51 | ORDER BY component"; 52 | 53 | $rs = $DB->get_recordset_sql($sql, array($version->code)); 54 | $components = array(); 55 | foreach ($rs as $record) { 56 | $components[$record->component] = true; 57 | } 58 | $rs->close(); 59 | 60 | foreach (array_keys($components) as $componentname) { 61 | $component = mlang_component::from_snapshot($componentname, 'en', $version); 62 | foreach ($component->get_iterator() as $string) { 63 | echo '['.$string->id.','.$component->name.']'.PHP_EOL; 64 | } 65 | $component->clear(); 66 | } 67 | -------------------------------------------------------------------------------- /cli/export-php.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Exports the most recent version of Moodle strings into Moodle PHP format 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 | define('CLI_SCRIPT', true); 27 | 28 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | 32 | // Let us get an information about existing components 33 | $sql = "SELECT branch,lang,component,COUNT(stringid) AS numofstrings 34 | FROM {amos_repository} 35 | WHERE deleted=0 36 | GROUP BY branch,lang,component 37 | ORDER BY branch,lang,component"; 38 | $rs = $DB->get_recordset_sql($sql); 39 | $tree = array(); // [branch][language][component] => numofstrings 40 | foreach ($rs as $record) { 41 | $tree[$record->branch][$record->lang][$record->component] = $record->numofstrings; 42 | } 43 | $rs->close(); 44 | 45 | remove_dir(AMOS_EXPORT_DIR, true); 46 | foreach ($tree as $vercode => $languages) { 47 | $version = mlang_version::by_code($vercode); 48 | foreach ($languages as $langcode => $components) { 49 | if ($langcode == 'en') { 50 | continue; 51 | } 52 | foreach ($components as $componentname => $unused) { 53 | $component = mlang_component::from_snapshot($componentname, $langcode, $version); 54 | if ($component->has_string()) { 55 | $file = AMOS_EXPORT_DIR . '/' . $version->dir . '/' . $langcode . '/' . $component->get_phpfile_location(false); 56 | if (!file_exists(dirname($file))) { 57 | mkdir(dirname($file), 0755, true); 58 | } 59 | echo "$file\n"; 60 | $component->export_phpfile($file); 61 | } 62 | $component->clear(); 63 | } 64 | } 65 | } 66 | echo "DONE\n"; 67 | -------------------------------------------------------------------------------- /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->assertEquals($expected, $actual, '', 0, 10, true); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /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 | $PAGE->requires->strings_for_js(array('processing', 'googletranslate'), 'local_amos'); 39 | $PAGE->requires->yui_module('moodle-local_amos-filter', 'M.local_amos.init_filter', null, null, true); 40 | $PAGE->requires->yui_module('moodle-local_amos-translator', 'M.local_amos.init_translator', null, null, true); 41 | $PAGE->requires->yui_module('moodle-local_amos-timeline', 'M.local_amos.init_timeline', null, null, true); 42 | 43 | $output = $PAGE->get_renderer('local_amos'); 44 | 45 | // create a renderable object that represents the filter form 46 | $filter = new local_amos_filter($PAGE->url); 47 | // save the filter settings into the sesssion 48 | $fdata = $filter->get_data(); 49 | foreach ($fdata as $setting => $value) { 50 | $USER->{'local_amos_' . $setting} = serialize($value); 51 | } 52 | $filter->set_permalink($PAGE->url, $fdata); 53 | 54 | // just make sure that USER contains sesskey 55 | $sesskey = sesskey(); 56 | // create a renderable object that represent the translation table 57 | $translator = new local_amos_translator($filter, $USER); 58 | 59 | /// Output starts here 60 | echo $output->header(); 61 | echo $output->render($filter); 62 | echo $output->render($translator); 63 | echo $output->box('', 'googlebranding', 'googlebranding'); 64 | echo $output->footer(); 65 | -------------------------------------------------------------------------------- /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', array('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_repository', array('lang' => $data['langcode']))) { 64 | $errors['langcode'] = 'Already exists'; 65 | } 66 | 67 | return $errors; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /cli/clean-texts.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Applies {@link mlang_component::clean_texts()} to all components 20 | * 21 | * @package local_amos 22 | * @copyright 2012 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_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->libdir . '/clilib.php'); 32 | 33 | cli_separator(); 34 | fputs(STDOUT, date('Y-m-d H:i', time())); 35 | fputs(STDOUT, " CLEAN TEXTS JOB STARTED\n"); 36 | 37 | $stage = new mlang_stage(); 38 | 39 | $tree = mlang_tools::components_tree(); 40 | foreach ($tree as $vercode => $languages) { 41 | if ($vercode <= mlang_version::MOODLE_19) { 42 | continue; 43 | } 44 | $version = mlang_version::by_code($vercode); 45 | foreach ($languages as $langcode => $components) { 46 | if ($langcode == 'en') { 47 | continue; 48 | } 49 | foreach ($components as $componentname => $ignored) { 50 | $component = mlang_component::from_snapshot($componentname, $langcode, $version); 51 | $component->clean_texts(); 52 | $stage->add($component); 53 | $stage->rebase(); 54 | if ($stage->has_component()) { 55 | $stage->commit('Cleaning strings debris and format', array('source' => 'bot', 'userinfo' => 'AMOS-bot '), true); 56 | fputs(STDOUT, $version->label.' '.$langcode.' '.$componentname.' committed '.date('Y-m-d H:i', time()).PHP_EOL); 57 | } else { 58 | fputs(STDOUT, $version->label.' '.$langcode.' '.$componentname.' no change '.date('Y-m-d H:i', time()).PHP_EOL); 59 | } 60 | $stage->clear(); 61 | $component->clear(); 62 | unset($component); 63 | } 64 | } 65 | } 66 | 67 | fputs(STDOUT, date('Y-m-d H:i', time())); 68 | fputs(STDOUT, " CLEAN TEXTS JOB DONE\n"); 69 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /diff_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_diff_form extends moodleform { 30 | 31 | public function definition() { 32 | $mform = $this->_form; 33 | 34 | $mform->addGroup(array( 35 | $mform->createElement('select', 'versiona', '', $this->_customdata['versions']), 36 | $mform->createElement('select', 'versionb', '', $this->_customdata['versions']), 37 | ), 'versionsgroup', get_string('diffversions', 'local_amos'), ' - ', false); 38 | $mform->setDefault('versiona', $this->_customdata['defaultversion']); 39 | reset($this->_customdata['versions']); // this is a trick so that we can use key() below 40 | $mform->setDefault('versionb', key($this->_customdata['versions'])); 41 | $mform->addRule('versionsgroup', null, 'required', null, 'client'); 42 | 43 | $mform->addElement('select', 'language', get_string('language', 'local_amos'), $this->_customdata['languages']); 44 | $mform->setDefault('language', $this->_customdata['languagecurrent']); 45 | $mform->addRule('language', null, 'required', null, 'client'); 46 | 47 | $options = array(); 48 | for ($i = 1; $i <=4; $i++) { 49 | $options[$i] = get_string('diffmode'.$i, 'local_amos'); 50 | } 51 | $mform->addElement('select', 'mode', get_string('diffmode', 'local_amos'), $options); 52 | 53 | $options = array( 54 | 1 => get_string('diffaction1', 'local_amos'), 55 | 2 => get_string('diffaction2', 'local_amos') 56 | ); 57 | $mform->addElement('select', 'action', get_string('diffaction', 'local_amos'), $options); 58 | $mform->setDefault('action', 1); 59 | $mform->disabledIf('action', 'mode', 'eq', 1); 60 | 61 | $this->add_action_buttons(false, get_string('diff', 'local_amos')); 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 | // Get an information about existing strings in the en_fix 34 | $sql = "SELECT branch,lang,component,COUNT(stringid) AS numofstrings 35 | FROM {amos_repository} 36 | WHERE deleted=0 37 | AND lang='en_fix' 38 | GROUP BY branch,lang,component 39 | ORDER BY branch,lang,component"; 40 | $rs = $DB->get_recordset_sql($sql); 41 | $tree = array(); // [branch][language][component] => numofstrings 42 | foreach ($rs as $record) { 43 | $tree[$record->branch][$record->lang][$record->component] = $record->numofstrings; 44 | } 45 | $rs->close(); 46 | 47 | if (!defined('AMOS_EXPORT_ENFIX_DIR')) { 48 | cli_error('Target directory AMOS_EXPORT_ENFIX_DIR not defined!'); 49 | } 50 | 51 | remove_dir(AMOS_EXPORT_ENFIX_DIR, true); 52 | 53 | foreach ($tree as $vercode => $languages) { 54 | $version = mlang_version::by_code($vercode); 55 | foreach ($languages as $langcode => $components) { 56 | if ($langcode !== 'en_fix') { 57 | throw new coding_exception('Unexpected language'); 58 | } 59 | foreach ($components as $componentname => $unused) { 60 | $component = mlang_component::from_snapshot($componentname, $langcode, $version); 61 | if ($component->has_string()) { 62 | $file = AMOS_EXPORT_ENFIX_DIR.'/'.$version->dir.'/'.$component->name.'.php'; 63 | if (!file_exists(dirname($file))) { 64 | mkdir(dirname($file), 0755, true); 65 | } 66 | echo "$file\n"; 67 | $component->export_phpfile($file); 68 | } 69 | $component->clear(); 70 | } 71 | } 72 | } 73 | echo "DONE\n"; 74 | -------------------------------------------------------------------------------- /yui/timeline/timeline.js: -------------------------------------------------------------------------------- 1 | /** 2 | * YUI module for AMOS string timeline overlay 3 | * 4 | * @author David Mudrak 5 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 6 | */ 7 | YUI.add('moodle-local_amos-timeline', function(Y) { 8 | 9 | var Timeline = function() { 10 | Timeline.superclass.constructor.apply(this, arguments); 11 | } 12 | 13 | Y.extend(Timeline, Y.Base, { 14 | initializer : function(config) { 15 | var translator = Y.one('#amostranslator'); 16 | if (translator) { 17 | translator.delegate('click', this.display, '.info-timeline a', this); 18 | } 19 | }, 20 | 21 | display : function(e, args) { 22 | e.preventDefault(); 23 | var a = e.currentTarget; 24 | var ajaxurl = a.get('href') + '&ajax=1'; 25 | var closebtn = Y.Node.create(''); 26 | Y.use('overlay', 'io', function(Y) { 27 | var overlay = new Y.Overlay({ 28 | headerContent: closebtn, 29 | bodyContent: Y.Node.create(''), 30 | id: 'timelinebox', 31 | constrain: true, 32 | visible: true, 33 | centered: true, 34 | width: '60%' 35 | }); 36 | overlay.render(Y.one('body')); 37 | closebtn.on('click', function (e) { 38 | e.preventDefault(); 39 | a.focus(); 40 | this.destroy(); 41 | }, overlay); 42 | closebtn.focus(); 43 | 44 | var ajaxcfg = { 45 | method: 'get', 46 | context : this, 47 | on: { 48 | success: function(id, o, node) { 49 | overlay.set('bodyContent', o.responseText); 50 | }, 51 | failure: function(id, o, node) { 52 | var debuginfo = o.statusText; 53 | if (M.cfg.developerdebug) { 54 | debuginfo += ' (' + ajaxurl + ')'; 55 | } 56 | overlay.set('bodyContent', debuginfo); 57 | } 58 | } 59 | }; 60 | 61 | Y.io(ajaxurl, ajaxcfg); 62 | }); 63 | } 64 | 65 | }, { 66 | NAME : 'amos_timeline', 67 | ATTRS : { 68 | aparam : {} 69 | } 70 | }); 71 | 72 | M.local_amos = M.local_amos || {}; 73 | 74 | M.local_amos.init_timeline = function(config) { 75 | M.local_amos.Timeline = new Timeline(config); 76 | } 77 | 78 | }, '@VERSION@', { requires:['overlay'] }); 79 | -------------------------------------------------------------------------------- /saveajax.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Stage a single string using AJAX 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 | define('AJAX_SCRIPT', true); 27 | 28 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/locallib.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->dirroot . '/local/amos/renderer.php'); 32 | 33 | require_login(SITEID, false); 34 | if (!has_capability('local/amos:stage', context_system::instance())) { 35 | header('HTTP/1.1 403 Forbidden'); 36 | die(); 37 | } 38 | if (!confirm_sesskey()) { 39 | header('HTTP/1.1 403 Forbidden'); 40 | die(); 41 | } 42 | 43 | $lang = optional_param('lang', null, PARAM_SAFEDIR); 44 | $originalid = optional_param('originalid', null, PARAM_INT); 45 | $text = optional_param('text', null, PARAM_RAW); 46 | $nocleaning = optional_param('nocleaning', false, PARAM_BOOL); 47 | 48 | if (is_null($lang) or is_null($originalid) or is_null($text)) { 49 | header('HTTP/1.1 400 Bad Request'); 50 | die(); 51 | } 52 | 53 | $record = $DB->get_record('amos_repository', array('id' => $originalid), 'id,stringid,component,branch', MUST_EXIST); 54 | $version = mlang_version::by_code($record->branch); 55 | $component = new mlang_component($record->component, $lang, $version); 56 | if ($version->code < mlang_version::MOODLE_20) { 57 | header('HTTP/1.1 400 Bad Request'); 58 | die(); 59 | } 60 | $string = new mlang_string($record->stringid, $text); 61 | $string->nocleaning = $nocleaning; 62 | $string->clean_text(); 63 | $component->add_string($string); 64 | 65 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 66 | $stage->add($component, true); 67 | $stage->store(); 68 | mlang_stash::autosave($stage); 69 | 70 | header('Content-Type: application/json; charset: utf-8'); 71 | $response = new stdclass(); 72 | $response->text = local_amos_renderer::add_breaks(s($string->text)); 73 | $response->nocleaning = $string->nocleaning; 74 | 75 | add_to_log(SITEID, 'amos', 'stage', '', $lang.' '.$version->label.' ['.$string->id.','.$component->name.']', 0, $USER->id); 76 | 77 | echo json_encode($response); 78 | -------------------------------------------------------------------------------- /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 | global $CFG; 59 | 60 | $workshop = new \local_amos\local\source_code($CFG->dirroot.'/mod/workshop'); 61 | $stringfiles = $workshop->get_included_string_files(); 62 | 63 | // Because we use a standard module here, debugging is expected once for each subplugin type. 64 | $msg = 'get_list_of_plugins() should not be used to list real plugins, use core_component::get_plugin_list() instead!'; 65 | $this->assertDebuggingCalledCount(3, array_fill(0, 3, $msg), array_fill(0, 3, DEBUG_DEVELOPER)); 66 | 67 | // Check that we get both workshop's and its subplugins' files. 68 | $this->assertStringStartsWith('assertStringStartsWith('. 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 | echo $output->heading(get_string('amos', 'local_amos'), 1); 41 | 42 | $amosroot = $PAGE->navigation->get('amos_root'); 43 | 44 | echo html_writer::start_div('amostools'); 45 | 46 | foreach ($amosroot->get_children_key_list() as $childkey) { 47 | $child = $amosroot->get($childkey); 48 | if (!($child->action instanceof moodle_url)) { 49 | continue; 50 | } 51 | echo html_writer::div( 52 | html_writer::link( 53 | $child->action, 54 | $child->text, 55 | array('class' => 'btn btn-large') 56 | ), 57 | 'well amostool amostool-'.$child->key 58 | ); 59 | } 60 | 61 | echo html_writer::end_div(); // .amostools 62 | 63 | echo $output->heading(get_string('privileges', 'local_amos')); 64 | $caps = array(); 65 | if (has_capability('local/amos:manage', context_system::instance())) { 66 | $caps[] = get_string('amos:manage', 'local_amos'); 67 | } 68 | if (has_capability('local/amos:stage', context_system::instance())) { 69 | $caps[] = get_string('amos:stage', 'local_amos'); 70 | } 71 | if (has_capability('local/amos:commit', context_system::instance())) { 72 | $allowed = mlang_tools::list_allowed_languages($USER->id); 73 | if (empty($allowed)) { 74 | $allowed = get_string('committablenone', 'local_amos'); 75 | } elseif (!empty($allowed['X'])) { 76 | $allowed = get_string('committableall', 'local_amos'); 77 | } else { 78 | $allowed = implode(', ', $allowed); 79 | } 80 | $caps[] = get_string('amos:commit', 'local_amos') . ' (' . $allowed . ')'; 81 | } 82 | if (empty($caps)) { 83 | get_string('privilegesnone', 'local_amos'); 84 | } else { 85 | $caps = '
  • ' . implode("
  • \n
  • ", $caps) . '
  • '; 86 | echo html_writer::tag('ul', $caps); 87 | } 88 | 89 | echo $output->footer(); 90 | -------------------------------------------------------------------------------- /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->get_iterator() 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 | -------------------------------------------------------------------------------- /scss/filter.scss: -------------------------------------------------------------------------------- 1 | // Strings filter at the translator page 2 | 3 | #page-local-amos-view #region-main { 4 | overflow-x: auto; 5 | } 6 | 7 | .path-local-amos { 8 | 9 | .string-control-label .fa-mobile { 10 | font-size: 1.7em; 11 | } 12 | 13 | .form-check-inline { 14 | display: inline; 15 | margin-right: 4px; 16 | input[type="checkbox"] { 17 | margin-top: 0; 18 | } 19 | } 20 | .form-actions { 21 | button { 22 | margin-right: 4px; 23 | } 24 | } 25 | 26 | input[type="radio"], 27 | input[type="checkbox"] { 28 | margin-top: 4px; 29 | margin-right: 0; 30 | } 31 | 32 | #amosfilter_form { 33 | .help-block { 34 | font-size: 66%; 35 | } 36 | .jsactions { 37 | display: block; 38 | margin-top: 2px; 39 | min-height: 20px; 40 | input[type="radio"] { 41 | display: none; 42 | } 43 | } 44 | .col-form-label { 45 | text-align: right; 46 | } 47 | } 48 | 49 | #amosfilter_fmis .form-check, 50 | #amosfilter_ftxt .form-check, 51 | #amosfilter_fsid .form-check { 52 | padding-left: 0; 53 | } 54 | 55 | #amosfilter_fver .unsupported label { 56 | color: gray; 57 | } 58 | 59 | #amosfilter_fver label { 60 | padding-right: 0.3em; 61 | } 62 | 63 | #amosfilter_submitted_icon { 64 | display: inline-block; 65 | min-width: 20px; 66 | } 67 | 68 | #amosfilter_fcmp { 69 | border: 1px solid rgb(204,204,204); 70 | @include border-radius(4px); 71 | padding: 0px 5px; 72 | height: 100px; 73 | overflow: auto; 74 | overflow-y: scroll; 75 | background-color: white; 76 | } 77 | 78 | #amosfilter_fcmp.hiddenversions .version { 79 | display: none; 80 | } 81 | 82 | .error #amosfilter_fcmp { 83 | border-color: #b94a48; 84 | color: #b94a48; 85 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); 86 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); 87 | box-shadow: inset 0 1px 1px rgba(0,0,0,0.075); 88 | } 89 | 90 | .error #amosfilter_fcmp_actions_search { 91 | border-color: inherit; 92 | -webkit-box-shadow: inherit; 93 | -moz-box-shadow: inherit; 94 | box-shadow: inherit; 95 | color: inherit; 96 | } 97 | 98 | #amosfilter_fcmp table tr th, #amosfilter_fcmp table tr td { 99 | border: none; 100 | padding: 0px 1px; 101 | margin: 0px; 102 | text-align: left; 103 | vertical-align: top; 104 | } 105 | 106 | #amosfilter_fcmp table tr th { 107 | font-style: italic; 108 | font-weight: bold; 109 | } 110 | 111 | #amosfilter_fcmp table tr td.version span { 112 | background-color: #ccc; 113 | font-size: 70%; 114 | color: white; 115 | padding: 2px; 116 | } 117 | 118 | #amosfilter_fcmp table tr td.version.supported span { 119 | background-color: theme-color("success"); 120 | } 121 | 122 | #amosfilter_fcmp input[type=checkbox]:checked + label { 123 | font-weight: bold; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /HOWTO-BRANCHING.txt: -------------------------------------------------------------------------------- 1 | Registering a new Moodle branch in AMOS 2 | ======================================= 3 | 4 | Whenever the new major Moodle version is released, several steps must be done 5 | in AMOS. For the purpose of this document, let us expect that Moodle X.X was 6 | just released and the new branch MOODLE_XX_STABLE was created in moodle.git. 7 | So now we must tell AMOS that the master branch is Y.Y. 8 | 9 | Note that in AMOS, the mlang_version already calls the new git branch as 10 | MOODLE_YY_STABLE even if it is actually master at the moment. 11 | 12 | * Disable new logins at the portal and announce the maintenance via 13 | @moodlesites twitter. 14 | * Make sure that origin/MOODLE_XX_STABLE is fetched into the moodle.git clone 15 | that AMOS tracks. 16 | * Turn Jenkins off. 17 | * Switch the portal to the climaintenance mode. 18 | * Backup AMOS database. 19 | * Define new mlang_version for MOODLE_YY_STABLE in mlanglib.php. 20 | * Grep for the current usage of MOODLE_XX and update it with MOODLE_YY where 21 | needed. The affected files should probably be: 22 | 23 | admin/newlanguage.php 24 | cli/export-installer.php 25 | cli/fix-drift.php 26 | cli/parse-core.php 27 | cli/utilslib.php 28 | jobs/install-packs-publish 29 | 30 | * Update jobs/zip-packs-snapshot and make it ready for the to-be-created branch. 31 | * Make sure that plugins.xml contains declaration for Y.Y. 32 | * Deploy the code at the production site. 33 | * Make sure to perform the following tasks in tmux or similar to avoid 34 | accidental cancellation. 35 | * Fork the new branch in the repository (took cca 60 minutes last time) 36 | 37 | php cli/make-branch.php --from=MOODLE_XX_STABLE --to=MOODLE_YY_STABLE 38 | 39 | and do not forget to reinitilize the snapshot table (took cca 2 hours) 40 | 41 | php cli/init-snapshot.php --branch=YY00 42 | 43 | * Copy moodledata/amos/var/MOODLE_XX_STABLE.startat to 44 | MOODLE_YY_STABLE.startat. Fix the content of MOODLE_XX_STABLE.startat so 45 | that it points to the HEAD of the real MOODLE_XX_STABLE (and not the master). 46 | * Make moodledata/amos/var/export-zip/Y.Y/ 47 | * Force AMOS to regenerate all ZIPs including those for Y.Y: 48 | 49 | # sudo -u www-data php admin/cli/cfg.php --component=local_amos --name=lastexportzip --set=1 50 | 51 | * Create new MOODLE_XX_STABLE and install_XX_STABLE branches in moodle-install 52 | 53 | # cd /opt/data/amos/repos/moodle-install 54 | # sudo -u www-data git checkout -b MOODLE_XX_STABLE upstream/MOODLE_XX_STABLE 55 | # sudo -u www-data git checkout -b install_XX_STABLE upstream/MOODLE_XX_STABLE 56 | 57 | * Create MOODLE_YY_STABLE branch in moodle-langpacks 58 | 59 | # cd /opt/data/amos/repos/moodle-langpacks 60 | # sudo -u www-data git checkout master 61 | # sudo -u www-data git branch MOODLE_YY_STABLE 62 | 63 | and make a clone for it at moodle-langpacks-clones 64 | 65 | # cd /opt/data/amos/repos/moodle-langpacks-clones 66 | # sudo -u www-data git clone ../moodle-langpacks Y.Y 67 | # cd Y.Y 68 | # sudo -u www-data git checkout -b MOODLE_YY_STABLE origin/MOODLE_YY_STABLE 69 | # sudo -u www-data git branch -d master 70 | 71 | Check that jobs/zip-packs-snapshot will process the new version. 72 | 73 | * Turn Jenkins on and let it re-generate all the ZIP packages (took cca 80 minutes) 74 | * Check http://download.moodle.org/langpack/ 75 | * Clone greylisted strings 76 | 77 | INSERT INTO mdl_amos_greylist (branch, component, stringid) 78 | SELECT YY00 AS branch, component, stringid FROM mdl_amos_greylist WHERE branch = XX00; 79 | 80 | * Disable the site maintenance mode. 81 | * Have a beer! 82 | -------------------------------------------------------------------------------- /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 | $component = required_param('component', PARAM_ALPHANUMEXT); 30 | $language = required_param('language', PARAM_ALPHANUMEXT); 31 | $stringid = required_param('stringid', PARAM_STRINGID); 32 | $confirm = optional_param('confirm', false, PARAM_BOOL); 33 | 34 | require_login(SITEID, false); 35 | require_capability('local/amos:commit', context_system::instance()); 36 | 37 | $allowedlangs = mlang_tools::list_allowed_languages($USER->id); 38 | 39 | if (empty($allowedlangs['X']) and empty($allowedlangs[$language])) { 40 | throw new moodle_exception('err_unexpected_language', 'local_amos'); 41 | } 42 | 43 | $PAGE->set_pagelayout('standard'); 44 | $PAGE->set_url(new moodle_url('/local/amos/untranslate.php', array( 45 | 'component' => $component, 'language' => $language, 'stringid' => $stringid 46 | ))); 47 | $PAGE->set_title('AMOS '.get_string('untranslating', 'local_amos')); 48 | $PAGE->navbar->add(get_string('untranslating', 'local_amos')); 49 | 50 | navigation_node::override_active_url(new moodle_url('/local/amos/view.php')); 51 | 52 | if ($confirm) { 53 | require_sesskey(); 54 | $mstage = new mlang_stage(); 55 | $mversions = mlang_version::list_all(); 56 | foreach ($mversions as $mversion) { 57 | if (!$mversion->translatable) { 58 | continue; 59 | } 60 | $mcomponent = mlang_component::from_snapshot($component, $language, $mversion, null, false, false, array($stringid)); 61 | $cstring = $mcomponent->get_string($stringid); 62 | if ($cstring === null) { 63 | continue; 64 | } 65 | $mstring = new mlang_string($cstring->id, $cstring->text, null, true); 66 | $mcomponent->add_string($mstring, true); 67 | $mstage->add($mcomponent); 68 | } 69 | $message = 'Untranslate the string '.$stringid; 70 | $meta = array( 71 | 'source' => 'amos', 72 | 'userid' => $USER->id, 73 | 'userinfo' => fullname($USER) . ' <' . $USER->email . '>', 74 | ); 75 | $mstage->commit($message, $meta); 76 | redirect(new moodle_url('/local/amos/view.php')); 77 | die(); 78 | } 79 | 80 | $output = $PAGE->get_renderer('local_amos'); 81 | 82 | echo $output->header(); 83 | echo $output->heading(get_string('untranslatetitle', 'local_amos')); 84 | 85 | $a = array( 86 | 'component' => $component, 87 | 'language' => $language, 88 | 'stringid' => $stringid, 89 | ); 90 | 91 | echo $output->confirm( 92 | get_string('untranslateconfirm', 'local_amos', $a), 93 | new moodle_url($PAGE->url, array('sesskey' => sesskey(), 'confirm' => true)), 94 | new moodle_url('/local/amos/view.php') 95 | ); 96 | 97 | echo $output->footer(); 98 | -------------------------------------------------------------------------------- /scss/stage.scss: -------------------------------------------------------------------------------- 1 | // Styles used at the stage page 2 | 3 | $collapsible-padding: 20px; 4 | 5 | .path-local-amos { 6 | 7 | .stagewrapper { 8 | 9 | .stagetool { 10 | border: 1px solid #ddd; 11 | @include border-radius(10px); 12 | padding: 10px; 13 | margin-bottom: 10px; 14 | } 15 | 16 | .stagetool.simple { 17 | text-align: center; 18 | border: none; 19 | margin-bottom: 20px; 20 | } 21 | 22 | .stagetool { 23 | 24 | .stagetool-title { 25 | font-size: larger; 26 | background: url([[pix:core|t/expanded]]) no-repeat scroll 0 center; 27 | padding-left: $collapsible-padding; 28 | cursor: pointer; 29 | color: $link-color; 30 | text-decoration: $link-decoration; 31 | 32 | &:hover { 33 | color: $link-hover-color; 34 | text-decoration: $link-hover-decoration; 35 | } 36 | } 37 | 38 | .collapsed .stagetool-title { 39 | background-image: url([[pix:core|t/collapsed]]); 40 | } 41 | 42 | .stagetool-content { 43 | margin-top: 15px; 44 | 45 | .form-control { 46 | width: 100vh; 47 | } 48 | } 49 | } 50 | 51 | .stagetool.commit, .stagetool.propagate, .stagetool.stageactions, .stagetool.stashactions { 52 | .stagetool-content { 53 | text-align: center; 54 | } 55 | } 56 | 57 | .stagetool.propagate { 58 | 59 | input[type="submit"] { 60 | margin: 5px 20px; 61 | } 62 | 63 | label.checkbox input[type="checkbox"] { 64 | margin-top: 4px; // Undo the dodgy hack from theme/bootstrapbase/less/moodle/forms.less 65 | margin-right: 0; 66 | } 67 | } 68 | 69 | .stagetool.commit { 70 | label.checkbox { 71 | margin-bottom: 5px; 72 | } 73 | label.checkbox input[type="checkbox"] { 74 | margin-top: 4px; // Undo the dodgy hack from theme/bootstrapbase/less/moodle/forms.less 75 | margin-right: 0; 76 | } 77 | } 78 | 79 | .stagetool.stageactions { 80 | .btn { 81 | margin: 2px; 82 | } 83 | } 84 | 85 | } 86 | 87 | #amosstage { 88 | 89 | .translation.uncommittable { 90 | background-color: $amos-color-staged; 91 | } 92 | 93 | .translation.uncommittable.nodiff { 94 | background-color: white; 95 | color: #999; 96 | } 97 | 98 | .translation.committable.new { 99 | background-color: $amos-color-committable; 100 | } 101 | 102 | .translation.committable.diff { 103 | background-color: $amos-color-outdated; 104 | } 105 | 106 | .translation.committable.removal { 107 | background-color: $amos-color-red; 108 | } 109 | 110 | .translation.nocleaning { 111 | border-style: solid; 112 | border-color: red; 113 | } 114 | 115 | .translation .translationactions { 116 | margin: 3px; 117 | .btn { 118 | margin: 2px; 119 | } 120 | } 121 | 122 | .translation.diff { 123 | del { 124 | color: #999; 125 | } 126 | ins { 127 | color: green; 128 | text-decoration: none; 129 | font-weight: bold; 130 | } 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /cli/make-branch.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Create a new branch of strings in the AMOS repository 19 | * 20 | * @package local 21 | * @subpackage amos 22 | * @copyright 2011 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_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 29 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 30 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 31 | require_once($CFG->dirroot . '/local/amos/locallib.php'); 32 | require_once($CFG->libdir.'/clilib.php'); 33 | 34 | list($options, $unrecognized) = cli_get_params(array('from'=>false, 'to'=>false, 'help'=>false), array('h'=>'help')); 35 | 36 | if ($options['help'] or !$options['from'] or !$options['to']) { 37 | echo 'Usage: '.basename(__FILE__).' --from=MOODLE_XX_STABLE --to=MOODLE_YY_STABLE' . PHP_EOL; 38 | exit(1); 39 | } 40 | 41 | $fromversion = mlang_version::by_branch($options['from']); 42 | $toversion = mlang_version::by_branch($options['to']); 43 | 44 | if (is_null($fromversion) or is_null($toversion)) { 45 | echo 'Unknown branch' . PHP_EOL; 46 | exit(2); 47 | } 48 | 49 | // Make sure that this is not executed by mistake 50 | if ($DB->record_exists('amos_repository', array('branch' => $toversion->code))) { 51 | echo 'The target branch already exists' . PHP_EOL; 52 | exit(3); 53 | } 54 | 55 | // Get the list of standard components on the source branch. 56 | $components = array(); 57 | foreach (local_amos_standard_plugins() as $moodleversion => $frankenstylenames) { 58 | if ($moodleversion === $fromversion->dir) { 59 | foreach (array_keys($frankenstylenames) as $componentname) { 60 | $components[$componentname] = true; 61 | } 62 | } 63 | } 64 | 65 | list($subsql, $subparams) = $DB->get_in_or_equal(array_keys($components)); 66 | 67 | // Let us get the list of commits that change strings on the given branch 68 | $sql = "SELECT DISTINCT commitid 69 | FROM {amos_repository} 70 | WHERE branch = ? 71 | AND component $subsql 72 | ORDER BY commitid"; 73 | 74 | $rs = $DB->get_recordset_sql($sql, array_merge(array($fromversion->code), $subparams)); 75 | 76 | foreach ($rs as $record) { 77 | $commitid = $record->commitid; 78 | echo 'Cloning changes introduced by commit ' . $commitid . ' ... '; 79 | 80 | // all changes introduced by the commit at the given branch 81 | $changes = $DB->get_records('amos_repository', array('commitid' => $commitid, 'branch' => $fromversion->code), 'id'); 82 | 83 | // clone every string change onto the new branch 84 | $transaction = $DB->start_delegated_transaction(); 85 | foreach ($changes as $change) { 86 | unset($change->id); 87 | $change->branch = $toversion->code; 88 | 89 | if (isset($components[$change->component])) { 90 | $DB->insert_record('amos_repository', $change); 91 | } 92 | } 93 | $transaction->allow_commit(); 94 | 95 | echo 'Done' . PHP_EOL; 96 | } 97 | 98 | echo '!!! Do not forget to reinitilize the snapshot table !!!' . PHP_EOL; 99 | 100 | $rs->close(); 101 | -------------------------------------------------------------------------------- /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 | /** 26 | * Populates the contribution front page block contents. 27 | * 28 | * @return string 29 | */ 30 | function local_amos_frontpage_contribution_stats() { 31 | global $CFG, $DB; 32 | 33 | $total = (int)$DB->get_field_sql(" 34 | SELECT SUM(strings) 35 | FROM {amos_contributions} c 36 | JOIN mdl_amos_stashes s ON c.stashid = s.id 37 | WHERE c.status = 30"); 38 | 39 | $namefields = get_all_user_name_fields(true, "u"); 40 | $recent = $DB->get_records_sql(" 41 | SELECT c.authorid AS id, $namefields, MAX(c.timecreated) AS mostrecent 42 | FROM {amos_contributions} c 43 | JOIN {user} u ON u.id = c.authorid 44 | GROUP BY c.authorid, $namefields 45 | ORDER BY mostrecent DESC", null, 0, 4); 46 | 47 | $links = array(); 48 | foreach ($recent as $contributor) { 49 | $links[] = ''.s(fullname($contributor)).''; 50 | } 51 | 52 | $last = array_pop($links); 53 | 54 | $links = implode(', ', $links) . ' and ' . $last; 55 | 56 | return '

    ' . get_string('contributestats', 'local_amos', array('count' => $total)) . '

    57 | 60 |

    ' . get_string('contributethanks', 'local_amos', array('listcontributors' => $links)) . '

    '; 61 | } 62 | 63 | ?> 64 | 65 |
    66 |
    67 |
    68 |
    69 |

    70 |
    71 |
    72 |
    73 |
    74 |
    75 |

    76 | 77 |
    78 |
    79 |
    80 |
    81 |

    82 |
      83 |
    • 84 |
    • 85 |
    • 86 |
    • 87 |
    88 |
    89 |
    90 |
    91 |
    92 | -------------------------------------------------------------------------------- /cli/intersect.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Removes translation of strings that are not present in the English pack 20 | * 21 | * In other words, it calls mlang_component::intersect() against the English 22 | * original for all components in the repository. 23 | * 24 | * Usage: 25 | * 26 | * php intersect.php (for dry-run) 27 | * php intersect.php --execute (to actually commit the removal) 28 | * 29 | * @package local_amos 30 | * @copyright 2012 David Mudrak 31 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 32 | */ 33 | 34 | define('CLI_SCRIPT', true); 35 | 36 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 37 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 38 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 39 | require_once($CFG->dirroot . '/local/amos/renderer.php'); 40 | require_once($CFG->libdir.'/clilib.php'); 41 | 42 | list($options, $unrecognized) = cli_get_params(array('execute' => false)); 43 | 44 | fputs(STDOUT, "*****************************************\n"); 45 | fputs(STDOUT, date('Y-m-d H:i', time())); 46 | fputs(STDOUT, " INTERSECT JOB STARTED\n"); 47 | 48 | $cliresult = 0; 49 | 50 | $tree = mlang_tools::components_tree(); 51 | foreach ($tree as $vercode => $languages) { 52 | if ($vercode < mlang_version::MOODLE_20) { 53 | continue; 54 | } 55 | $version = mlang_version::by_code($vercode); 56 | foreach ($languages['en'] as $componentname => $unused) { 57 | $english = mlang_component::from_snapshot($componentname, 'en', $version); 58 | foreach (array_keys($tree[$vercode]) as $otherlang) { 59 | if ($otherlang == 'en') { 60 | continue; 61 | } 62 | $stage = new mlang_stage(); 63 | $other = mlang_component::from_snapshot($componentname, $otherlang, $version); 64 | $removed = $other->intersect($english); 65 | if ($removed) { 66 | $stage->add($other); 67 | } 68 | $other->clear(); 69 | unset($other); 70 | 71 | $stage->rebase(null, true); 72 | 73 | if ($options['execute']) { 74 | $action = 'removing'; 75 | } else { 76 | $action = 'would remove'; 77 | } 78 | 79 | foreach ($stage->get_iterator() as $xcomp) { 80 | foreach ($xcomp->get_iterator() as $xstr) { 81 | fputs(STDERR, $action.' '.$xstr->id.' from '.$componentname.' '.$otherlang.' '.$version->label.PHP_EOL); 82 | if (!$options['execute']) { 83 | $cliresult = 1; 84 | } 85 | } 86 | } 87 | 88 | if ($options['execute']) { 89 | $msg = 'Reverse clean-up of strings that do not exist in the English pack'; 90 | $stage->commit($msg, array('source' => 'bot', 'userinfo' => 'AMOS-bot '), true); 91 | } else { 92 | $stage->clear(); 93 | } 94 | } 95 | $english->clear(); 96 | } 97 | } 98 | 99 | fputs(STDOUT, date('Y-m-d H:i', time())); 100 | fputs(STDOUT, " INTERSECT CLEANUP JOB DONE\n"); 101 | 102 | exit($cliresult); 103 | -------------------------------------------------------------------------------- /cli/compare-packs.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package local_amos 20 | * @subpackage cli 21 | * @copyright 2013 David Mudrak 22 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 | */ 24 | 25 | $usage = " 26 | Use this script to compare the contents of two unzipped language packs 27 | 28 | $ php compare-packs.php --version=2.6 --master=/tmp/en --slave=/tmp/fr 29 | "; 30 | 31 | define('CLI_SCRIPT', 1); 32 | 33 | require(__DIR__.'/../../../config.php'); 34 | require_once($CFG->libdir.'/pluginlib.php'); 35 | require_once($CFG->libdir.'/clilib.php'); 36 | require_once($CFG->dirroot.'/local/amos/mlanglib.php'); 37 | require_once($CFG->dirroot.'/local/amos/locallib.php'); 38 | 39 | list($options, $unrecognized) = cli_get_params(array('version' => '', 'master' => '', 'slave' => '')); 40 | 41 | $version = $options['version']; 42 | $master = $options['master']; 43 | $slave = $options['slave']; 44 | 45 | if (empty($version) or empty($master) or empty($slave)) { 46 | cli_error($usage); 47 | } 48 | 49 | $standard = local_amos_standard_plugins(); 50 | 51 | if (!isset($standard[$version])) { 52 | cli_error($version.' not a known version'); 53 | } 54 | 55 | if (!is_dir($master)) { 56 | cli_error($master.' is not a directory'); 57 | } 58 | 59 | if (!is_dir($slave)) { 60 | cli_error($slave.' is not a directory'); 61 | } 62 | 63 | $mpack = array(); 64 | $spack = array(); 65 | 66 | foreach (new DirectoryIterator($master) as $file) { 67 | if ($file->isDot() or $file->isDir()) { 68 | continue; 69 | } 70 | if (substr($file->getFilename(), -4) !== '.php') { 71 | fputs(STDERR, 'Unexpected file '.$file->getPathname()); 72 | exit(1); 73 | } 74 | $component = mlang_component::name_from_filename($file->getFilename()); 75 | $string = array(); 76 | require($file->getPathname()); 77 | $mpack[$component] = array_flip(array_keys($string)); 78 | unset($string); 79 | } 80 | 81 | foreach (new DirectoryIterator($slave) as $file) { 82 | if ($file->isDot() or $file->isDir()) { 83 | continue; 84 | } 85 | if (substr($file->getFilename(), -4) !== '.php') { 86 | fputs(STDERR, 'Unexpected file '.$file->getPathname()); 87 | exit(1); 88 | } 89 | $component = mlang_component::name_from_filename($file->getFilename()); 90 | $string = array(); 91 | require($file->getPathname()); 92 | $spack[$component] = array_flip(array_keys($string)); 93 | unset($string); 94 | } 95 | 96 | // Report all slave strings not present in the master pack. 97 | foreach ($spack as $component => $strings) { 98 | foreach (array_keys($strings) as $string) { 99 | if (!isset($mpack[$component][$string])) { 100 | fputs(STDERR, '['.$component.','.$string.'] defined in the slave only'.PHP_EOL); 101 | } 102 | } 103 | } 104 | 105 | // Report all missing slave strings if the master pack is a standard one. 106 | foreach ($mpack as $component => $strings) { 107 | if (!isset($standard[$version][$component])) { 108 | continue; 109 | } 110 | foreach (array_keys($strings) as $string) { 111 | if (substr($string, -5) === '_link') { 112 | continue; 113 | } 114 | if (!isset($spack[$component][$string])) { 115 | fputs(STDERR, '['.$component.','.$string.'] defined in the master only'.PHP_EOL); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /classes/external/plugin_translation_stats.php: -------------------------------------------------------------------------------- 1 | . 16 | 17 | /** 18 | * Provides the {@link \local_amos\external\plugin_translation_stats} trait. 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 | * Trait implementing the plugin_translation_stats external function. 32 | */ 33 | trait plugin_translation_stats { 34 | 35 | /** 36 | * Describes parameters of the {@link plugin_translation_stats()} method 37 | */ 38 | public static function plugin_translation_stats_parameters() { 39 | return new \external_function_parameters([ 40 | 'component' => new \external_value(PARAM_COMPONENT, 'Name of the component to obtain stats for'), 41 | ]); 42 | } 43 | 44 | /** 45 | * Returns stats about the given plugin / component translations. 46 | * 47 | * @param string $component 48 | * @return stdClass 49 | */ 50 | public static function plugin_translation_stats($component) { 51 | 52 | // Validate parameters. 53 | $params = self::validate_parameters(self::plugin_translation_stats_parameters(), ['component' => $component]); 54 | $component = $params['component']; 55 | 56 | // Validate the context. 57 | $context = \context_system::instance(); 58 | self::validate_context($context); 59 | 60 | $statsman = new \local_amos_stats_manager(); 61 | 62 | $data = $statsman->get_component_stats($component); 63 | 64 | if ($data === false) { 65 | throw new \invalid_parameter_exception('Stats requested for an unknown component.'); 66 | } 67 | 68 | return $data; 69 | } 70 | 71 | /** 72 | * Describes the return value of the {@link plugin_translation_stats()} method. 73 | * 74 | * @return external_description 75 | */ 76 | public static function plugin_translation_stats_returns() { 77 | return new \external_single_structure([ 78 | 'lastmodified' => new \external_value(PARAM_INT, 'Timestamp of when the data was last modified'), 79 | 'langnames' => new \external_multiple_structure( 80 | new \external_single_structure([ 81 | 'lang' => new \external_value(PARAM_SAFEDIR, 'Language code'), 82 | 'name' => new \external_value(PARAM_TEXT, 'International name of the language followed by its code') 83 | ]) 84 | ), 85 | 'branches' => new \external_multiple_structure( 86 | new \external_single_structure([ 87 | 'branch' => new \external_value(PARAM_FILE, 'Moodle branch (eg. 3.6)'), 88 | 'languages' => new \external_multiple_structure( 89 | new \external_single_structure([ 90 | 'lang' => new \external_value(PARAM_SAFEDIR, 'Language code'), 91 | 'numofstrings' => new \external_value(PARAM_INT, 'Number of strings in the language pack'), 92 | 'ratio' => new \external_value(PARAM_INT, 'Completeness of the translation'), 93 | ]) 94 | ), 95 | ]) 96 | ) 97 | ]); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /merge.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Merge all missing strings from branch to another 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__).'/merge_form.php'); 31 | 32 | require_login(SITEID, false); 33 | require_capability('local/amos:commit', context_system::instance()); // for langpack maintainers only 34 | 35 | $PAGE->set_pagelayout('standard'); 36 | $PAGE->set_url('/local/amos/merge.php'); 37 | navigation_node::override_active_url(new moodle_url('/local/amos/stage.php')); 38 | $PAGE->set_title('AMOS ' . get_string('merge', 'local_amos')); 39 | $PAGE->set_heading('AMOS ' . get_string('merge', 'local_amos')); 40 | 41 | $mergeform = new local_amos_merge_form(null, local_amos_merge_options()); 42 | 43 | if ($data = $mergeform->get_data()) { 44 | $stage = mlang_persistent_stage::instance_for_user($USER->id, sesskey()); 45 | 46 | $sourceversion = mlang_version::by_code($data->sourceversion); 47 | $targetversion = mlang_version::by_code($data->targetversion); 48 | if (is_null($sourceversion) or is_null($targetversion)) { 49 | notice('Invalid version selected', new moodle_url('/local/amos/stage.php')); 50 | } 51 | 52 | $tree = mlang_tools::components_tree(array('branch' => $sourceversion->code, 'lang' => $data->language)); 53 | $tree = reset($tree); 54 | $sourcecomponentnames = array_keys(reset($tree)); 55 | unset($tree); 56 | 57 | foreach ($sourcecomponentnames as $sourcecomponentname) { 58 | // get a snapshot of both components and merge source into target 59 | $sourcecomponent = mlang_component::from_snapshot($sourcecomponentname, $data->language, $sourceversion); 60 | $targetcomponent = mlang_component::from_snapshot($sourcecomponent->name, $sourcecomponent->lang, $targetversion); 61 | mlang_tools::merge($sourcecomponent, $targetcomponent); 62 | $sourcecomponent->clear(); 63 | // keep just strings that are defined in english 64 | $englishcomponent = mlang_component::from_snapshot($sourcecomponent->name, 'en', $targetversion); 65 | $targetcomponent->intersect($englishcomponent); 66 | $englishcomponent->clear(); 67 | // stage the target 68 | $stage->add($targetcomponent); 69 | $targetcomponent->clear(); 70 | } 71 | 72 | // prune the stage so that only committable strings are staged 73 | $allowed = mlang_tools::list_allowed_languages($USER->id); 74 | $stage->prune($allowed); 75 | // keep just really modified (that is new in this case) strings 76 | $stage->rebase(); 77 | // and store the persistant stage 78 | $stage->store(); 79 | 80 | // if no new strings are merged, inform the user 81 | if (!$stage->has_component()) { 82 | notice(get_string('nothingtomerge', 'local_amos'), new moodle_url('/local/amos/stage.php')); 83 | } 84 | 85 | if (!isset($SESSION->local_amos)) { 86 | $SESSION->local_amos = new stdClass(); 87 | } 88 | $a = new stdClass(); 89 | $a->source = $sourceversion->label; 90 | $a->target = $targetversion->label; 91 | $SESSION->local_amos->presetcommitmessage = get_string('presetcommitmessage2', 'local_amos', $a); 92 | } 93 | 94 | redirect(new moodle_url('/local/amos/stage.php')); 95 | -------------------------------------------------------------------------------- /timeline.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Displays a timeline history of a given string 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($CFG->dirroot . '/local/amos/locallib.php'); 29 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 30 | require_once($CFG->dirroot . '/local/amos/renderer.php'); 31 | 32 | require_login(SITEID, false); 33 | require_capability('local/amos:stage', context_system::instance()); 34 | 35 | $component = required_param('component', PARAM_ALPHANUMEXT); 36 | $language = required_param('language', PARAM_ALPHANUMEXT); 37 | $branch = required_param('branch', PARAM_INT); 38 | $stringid = required_param('stringid', PARAM_STRINGID); 39 | $ajax = optional_param('ajax', 0, PARAM_BOOL); 40 | 41 | $PAGE->set_url('/local/amos/timeline.ajax.php'); 42 | $PAGE->set_pagelayout('popup'); 43 | $PAGE->set_context(context_system::instance()); 44 | 45 | if ($ajax) { 46 | @header('Content-Type: text/plain; charset=utf-8'); 47 | } else { 48 | echo $OUTPUT->header(); 49 | } 50 | 51 | $sql = "SELECT s.id, s.lang, t.text, s.timemodified, s.deleted, 52 | c.userinfo, c.commitmsg, c.commithash 53 | FROM {amos_repository} s 54 | JOIN {amos_texts} t ON t.id = s.textid 55 | JOIN {amos_commits} c ON c.id = s.commitid 56 | WHERE branch = ? AND (lang = 'en' OR lang = ?) AND component = ? AND stringid = ? 57 | ORDER BY s.timemodified DESC, s.id DESC"; 58 | 59 | $params = array($branch, $language, $component, $stringid); 60 | 61 | $results = $DB->get_records_sql($sql, $params); 62 | 63 | if (!$results) { 64 | print_error('invalidtimelineparams', 'local_amos'); 65 | } 66 | 67 | $table = new html_table(); 68 | $table->attributes['class'] = 'timelinetable'; 69 | 70 | foreach ($results as $result) { 71 | 72 | $encell = new html_table_cell(); 73 | $langcell = new html_table_cell(); 74 | if ($result->lang == 'en') { 75 | $cell = $encell; 76 | $none = $langcell; 77 | } else { 78 | $cell = $langcell; 79 | $none = $encell; 80 | } 81 | 82 | $date = html_writer::tag('div', local_amos_renderer::commit_datetime($result->timemodified), array('class' => 'timemodified')); 83 | $userinfo = html_writer::tag('span', s($result->userinfo), array('class' => 'userinfo')); 84 | $commitmsg = html_writer::tag('span', s($result->commitmsg), array('class' => 'commitmsg')); 85 | if ($result->deleted) { 86 | $text = html_writer::tag('del', s($result->text)); 87 | } else { 88 | $text = s($result->text); 89 | } 90 | $text = local_amos_renderer::add_breaks($text); 91 | if ($result->commithash) { 92 | if ($result->lang == 'en') { 93 | $url = 'https://github.com/moodle/moodle/commit/'.$result->commithash; 94 | } else { 95 | $url = 'https://github.com/mudrd8mz/moodle-lang/commit/'.$result->commithash; 96 | } 97 | $hashlink = html_writer::link($url, $result->commithash); 98 | $commithash = html_writer::tag('div', $hashlink, array('class' => 'commithash')); 99 | } else { 100 | $commithash = ''; 101 | } 102 | $text = html_writer::tag('div', $text, array('class' => 'text preformatted')); 103 | 104 | $cell->text = $date . html_writer::tag('div', $userinfo . ' ' . $commitmsg . $commithash, array('class' => 'usermessage')) . $text; 105 | $none->text = ' '; 106 | 107 | $row = new html_table_row(array($encell, $langcell)); 108 | $table->data[] = $row; 109 | } 110 | 111 | echo html_writer::table($table); 112 | 113 | if (!$ajax) { 114 | echo $OUTPUT->footer(); 115 | } 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/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/cli/config.php'); 44 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 45 | require_once($CFG->dirroot . '/local/amos/cli/utilslib.php'); 46 | 47 | list($options, $unrecognized) = cli_get_params( 48 | array( 49 | 'symlinksdir' => '', 50 | 'enfixdir' => '', 51 | 'help' => false), 52 | array('h' => 'help')); 53 | 54 | 55 | if ($options['help'] or empty($options['symlinksdir']) or empty($options['enfixdir'])) { 56 | cli_error($help, 2); 57 | } 58 | 59 | // Make sure that all target files exist, are symlinks and are writable. 60 | 61 | foreach (new DirectoryIterator($options['enfixdir']) as $enfixfileinfo) { 62 | if ($enfixfileinfo->isDot()) { 63 | continue; 64 | } 65 | $filename = $enfixfileinfo->getFilename(); 66 | 67 | if (!is_link($options['symlinksdir'].'/'.$filename)) { 68 | cli_error('File not symlink: '.$options['symlinksdir'].'/'.$filename); 69 | } 70 | 71 | if (!is_writable($options['symlinksdir'].'/'.$filename)) { 72 | cli_error('File not writable: '.$options['symlinksdir'].'/'.$filename); 73 | } 74 | } 75 | 76 | // Let's rock! 77 | 78 | $logger = new amos_cli_logger(); 79 | $helper = new amos_merge_string_files($logger); 80 | $total = 0; 81 | 82 | foreach (new DirectoryIterator($options['enfixdir']) as $enfixfileinfo) { 83 | if ($enfixfileinfo->isDot()) { 84 | continue; 85 | } 86 | $filename = $enfixfileinfo->getFilename(); 87 | 88 | $logger->log('enfix-merge', 'Processing file '.$filename.' ...'); 89 | 90 | if ($filename === 'langconfig.php') { 91 | $logger->log('enfix-merge', 'Skipping file '.$filename, amos_cli_logger::LEVEL_DEBUG); 92 | continue; 93 | } 94 | 95 | $filecontents = file_get_contents($options['symlinksdir'].'/'.$filename); 96 | 97 | $fromstrings = $helper->load_strings_from_file($options['symlinksdir'].'/'.$filename); 98 | $logger->log('enfix-merge', count($fromstrings).' string(s) found in '.$options['symlinksdir'].'/'.$filename, amos_cli_logger::LEVEL_DEBUG); 99 | 100 | $tostrings = $helper->load_strings_from_file($options['enfixdir'].'/'.$filename); 101 | $logger->log('enfix-merge', count($tostrings).' string(s) found in '.$options['enfixdir'].'/'.$filename, amos_cli_logger::LEVEL_DEBUG); 102 | 103 | $changes = $helper->replace_strings_in_file($filecontents, $fromstrings, $tostrings); 104 | 105 | if ($changes) { 106 | $total += $changes; 107 | file_put_contents($options['symlinksdir'].'/'.$filename, $filecontents); 108 | $logger->log('enfix-merge', $changes.' string(s) fixed in '.$filename); 109 | } else if ($changes === 0) { 110 | $logger->log('enfix-merge', 'No changes in '.$filename); 111 | } else { 112 | $logger->log('enfix-merge', 'Error while processing file '.$filename, amos_cli_logger::LEVEL_ERROR); 113 | } 114 | } 115 | 116 | $logger->log('enfix-merge', 'Finished! Total of '.$total.' string(s) merged.'); 117 | -------------------------------------------------------------------------------- /cli/import-strings.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Imports English strings for a given component from PHP file 20 | * 21 | * This is used typically for contributed plugins. 22 | * 23 | * @package local 24 | * @subpackage amos 25 | * @copyright 2011 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_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 32 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 33 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 34 | require_once($CFG->libdir.'/clilib.php'); 35 | 36 | list($options, $unrecognized) = cli_get_params(array( 37 | 'lang' => 'en', 38 | 'version' => 'MOODLE_21_STABLE', 39 | 'timemodified' => null, 40 | 'name' => null, 41 | 'format' => 2, 42 | 'message' => '', 43 | 'userinfo' => 'David Mudrak ', 44 | 'commithash' => null, 45 | 'yes' => false, 46 | 'help' => false 47 | 48 | ), array('h' => 'help')); 49 | 50 | $usage = <<' 64 | --commithash Allows to specify the git commit hash 65 | --yes It won't ask to continue 66 | --help Show this usage 67 | 68 | The file is directly included into the PHP processor. Make sure to review the file 69 | for a malicious contents before you import it via this script. 70 | 71 | EOF; 72 | 73 | if ($options['help'] or empty($options['message']) or empty($unrecognized)) { 74 | echo $usage . PHP_EOL; 75 | exit(1); 76 | } 77 | 78 | $filepath = $unrecognized[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 | $version = mlang_version::by_branch($options['version']); 86 | if (is_null($version)) { 87 | echo 'Invalid version' . PHP_EOL; 88 | exit(3); 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->get_iterator() as $component) { 106 | foreach ($component->get_iterator() 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 | list($options, $unrecognized) = cli_get_params(array('execute' => false, 'aggresive' => false)); 34 | 35 | fputs(STDOUT, "*****************************************\n"); 36 | fputs(STDOUT, date('Y-m-d H:i', time())); 37 | fputs(STDOUT, " ENFIX CLEANUP JOB STARTED\n"); 38 | 39 | // Get an information about existing strings in the en_fix 40 | $sql = "SELECT branch,lang,component,COUNT(stringid) AS numofstrings 41 | FROM {amos_repository} 42 | WHERE deleted=0 43 | AND lang='en_fix' 44 | GROUP BY branch,lang,component 45 | ORDER BY branch,lang,component"; 46 | $rs = $DB->get_recordset_sql($sql); 47 | $tree = array(); // [branch][language][component] => numofstrings 48 | foreach ($rs as $record) { 49 | $tree[$record->branch][$record->lang][$record->component] = $record->numofstrings; 50 | } 51 | $rs->close(); 52 | 53 | $stage = new mlang_stage(); 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 | $en = mlang_component::from_snapshot($componentname, 'en', $version); 63 | $enfix = mlang_component::from_snapshot($componentname, $langcode, $version); 64 | $enfix->intersect($en); 65 | $removed = $enfix->complement($en); 66 | 67 | if ($options['aggresive']) { 68 | foreach ($enfix->get_iterator() as $enfixstring) { 69 | $enstring = $en->get_string($enfixstring->id); 70 | if ($enstring === null) { 71 | fputs(STDERR, 'orphaned string '.$enfixstring->id.' in '.$componentname.' '.$version->label.PHP_EOL); 72 | continue; 73 | } 74 | if ($enstring->timemodified > $enfixstring->timemodified) { 75 | fputs(STDERR, 'string '.$enfixstring->id.' outdated in '.$componentname.' '.$version->label.PHP_EOL); 76 | $enfix->unlink_string($enfixstring->id); 77 | $removed++; 78 | } 79 | } 80 | } 81 | 82 | if ($removed) { 83 | 84 | if ($options['execute']) { 85 | $action = 'removing'; 86 | } else { 87 | $action = 'would remove'; 88 | } 89 | 90 | fputs(STDERR, $action.' '.$removed.' string(s) from '.$componentname.' '.$version->label.PHP_EOL); 91 | 92 | if ($options['execute']) { 93 | $stage->add($enfix); 94 | $stage->rebase(null, true); 95 | $msg = 'Clean-up strings that were merged into the English pack'; 96 | $stage->commit($msg, array('source' => 'bot', 'userinfo' => 'AMOS-bot '), true); 97 | } else { 98 | $stage->clear(); 99 | } 100 | } else { 101 | fputs(STDERR, 'nothing to do in '.$componentname.' '.$version->label.PHP_EOL); 102 | } 103 | $en->clear(); 104 | $enfix->clear(); 105 | } 106 | } 107 | } 108 | 109 | fputs(STDOUT, date('Y-m-d H:i', time())); 110 | fputs(STDOUT, " ENFIX CLEANUP JOB DONE\n"); 111 | -------------------------------------------------------------------------------- /translate.ajax.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * @package local_amos 20 | * @copyright 2012 David Mudrak 21 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 22 | */ 23 | 24 | define('AJAX_SCRIPT', true); 25 | 26 | require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); 27 | require_once($CFG->libdir.'/filelib.php'); 28 | 29 | require_login(SITEID, false); 30 | 31 | if (!has_capability('local/amos:usegoogle', context_system::instance())) { 32 | error_log('AMOS No capability to fetching Google translation'); 33 | header('HTTP/1.1 403 Forbidden'); 34 | die(); 35 | } 36 | 37 | if (!confirm_sesskey(optional_param('sesskey', -1, PARAM_RAW))) { 38 | error_log('AMOS Invalid sesskey'); 39 | header('HTTP/1.1 403 Forbidden'); 40 | die(); 41 | } 42 | 43 | $enid = optional_param('enid', null, PARAM_INT); 44 | $lang = optional_param('lng', null, PARAM_SAFEDIR); 45 | if (is_null($enid) or empty($lang)) { 46 | error_log('AMOS Invalid parameters provided'); 47 | header('HTTP/1.1 400 Bad Request'); 48 | die(); 49 | } 50 | 51 | if (empty($CFG->amosgoogleapi)) { 52 | header('HTTP/1.1 500 Internal Server Error - Google API key not defined'); 53 | die(); 54 | } 55 | 56 | try { 57 | $string = $DB->get_record_sql("SELECT t.text AS text 58 | FROM {amos_repository} r 59 | JOIN {amos_texts} t ON t.id = r.textid 60 | WHERE r.id = :enid AND r.lang = :lang AND r.deleted = 0", 61 | array('enid' => $enid, 'lang' => 'en'), MUST_EXIST); 62 | 63 | } catch (Exception $e) { 64 | error_log('AMOS Unable to find the string to translate'); 65 | header('HTTP/1.1 400 Bad Request'); 66 | die(); 67 | } 68 | 69 | add_to_log(SITEID, 'amos', 'usegoogle', '', $enid, 0, $USER->id); 70 | 71 | $entext = str_replace('{$a}', 'PLACEHOLDERA__', $string->text); 72 | $entext = preg_replace('/\{\$a->(.+?)\}/', 'PLACEHOLDER__$1__', $entext); 73 | 74 | // map Moodle language codes to Google language codes 75 | switch ($lang) { 76 | case 'zh_cn': 77 | $lang = 'zh-CN'; 78 | break; 79 | case 'zh_tw': 80 | $lang = 'zh-TW'; 81 | break; 82 | case 'pt_br': 83 | $lang = 'pt'; 84 | break; 85 | case 'he': 86 | $lang = 'iw'; 87 | break; 88 | } 89 | 90 | $curl = new curl(array('cache' => false, 'proxy' => true)); 91 | $params = array( 92 | 'key' => $CFG->amosgoogleapi, 93 | 'format' => 'html', 94 | 'prettyprint' => 'false', 95 | 'source' => 'en', 96 | 'target' => $lang, 97 | 'userIp' => getremoteaddr(), 98 | 'q' => $entext, 99 | ); 100 | 101 | $response = $curl->get('https://www.googleapis.com/language/translate/v2', $params); 102 | $curlinfo = $curl->get_info(); 103 | 104 | $translation = null; 105 | 106 | if (empty($curlinfo)) { 107 | error_log('AMOS Unable to fetch Google translation - empty cURL info'); 108 | 109 | } else if ($curlinfo['http_code'] != 200) { 110 | error_log('AMOS Fetching Google translation - got HTTP response '.$curlinfo['http_code']); 111 | 112 | } else if ($response) { 113 | $response = json_decode($response); 114 | if (!empty($response->data->translations) and is_array($response->data->translations)) { 115 | $first = reset($response->data->translations); 116 | if (isset($first->translatedText)) { 117 | $translation = s($first->translatedText); 118 | } 119 | } 120 | } 121 | 122 | if (is_null($translation)) { 123 | $response = array( 124 | 'error' => array( 125 | 'message' => 'Unable to translate this at the moment' 126 | ) 127 | ); 128 | 129 | } else { 130 | $translation = preg_replace('/PLACEHOLDER__(.+?)__/', '{$a->$1}', $translation); 131 | $translation = str_replace('PLACEHOLDERA__', '{$a}', $translation); 132 | 133 | $response = array( 134 | 'data' => array( 135 | 'translation' => $translation, 136 | ) 137 | ); 138 | } 139 | 140 | header('Content-Type: application/json; charset: utf-8'); 141 | echo json_encode($response); 142 | -------------------------------------------------------------------------------- /cli/export-installer.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Exports the strings needed by the installer 20 | * 21 | * Moodle core contains a subset of strings needed for the start of the installation. 22 | * The list of required strings is maintained in install/stringnames.txt. This 23 | * script parses that file and exports the translations into a configured destination. 24 | * 25 | * @package local_amos 26 | * @copyright 2010 David Mudrak 27 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 28 | */ 29 | 30 | define('CLI_SCRIPT', true); 31 | 32 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 33 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 34 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 35 | 36 | // list of branches to process 37 | $branches = array( 38 | 'MOODLE_35_STABLE', 39 | 'MOODLE_36_STABLE', 40 | 'MOODLE_37_STABLE', 41 | 'MOODLE_38_STABLE', 42 | ); 43 | 44 | fputs(STDOUT, "*****************************************\n"); 45 | fputs(STDOUT, date('Y-m-d H:i', time())); 46 | fputs(STDOUT, " EXPORT INSTALLER JOB STARTED\n"); 47 | 48 | remove_dir(AMOS_EXPORT_INSTALLER_DIR, true); 49 | 50 | foreach ($branches as $branch) { 51 | fputs(STDOUT, "BRANCH {$branch}\n"); 52 | if ($branch == 'MOODLE_38_STABLE') { 53 | $gitbranch = 'origin/master'; 54 | } else { 55 | $gitbranch = 'origin/' . $branch; 56 | } 57 | $version = mlang_version::by_branch($branch); 58 | 59 | // read the contents of stringnames.txt at the given branch 60 | chdir(AMOS_REPO_MOODLE); 61 | $gitout = array(); 62 | $gitstatus = 0; 63 | $gitcmd = AMOS_PATH_GIT . " show {$gitbranch}:install/stringnames.txt"; 64 | exec($gitcmd, $gitout, $gitstatus); 65 | 66 | if ($gitstatus <> 0) { 67 | fputs(STDERR, "ERROR EXECUTING {$gitcmd}\n"); 68 | exit($gitstatus); 69 | } 70 | 71 | $list = array(); // [component][stringid] => true 72 | foreach ($gitout as $string) { 73 | list($stringid, $component) = array_map('trim', explode(',', $string)); 74 | $list[$component][$stringid] = true; 75 | } 76 | unset($gitout); 77 | 78 | $tree = mlang_tools::components_tree(array('branch' => $version->code)); 79 | $langs = array_keys($tree[$version->code]); 80 | unset($tree); 81 | 82 | $phpdoc = << $stringids) { 102 | foreach ($langs as $lang) { 103 | if ($lang === 'en_fix') { 104 | continue; 105 | } 106 | $component = mlang_component::from_snapshot($componentname, $lang, $version, null, false, false, array_keys($stringids)); 107 | if ($component->has_string()) { 108 | $file = AMOS_EXPORT_INSTALLER_DIR . '/' . $version->dir . '/install/lang/' . $lang . '/' . $component->name . '.php'; 109 | if (!file_exists(dirname($file))) { 110 | mkdir(dirname($file), 0755, true); 111 | } 112 | $component->export_phpfile($file, $phpdoc); 113 | } 114 | if ($lang == 'en') { 115 | // check that all string were exported 116 | foreach (array_keys($stringids) as $stringid) { 117 | if (!$component->has_string($stringid)) { 118 | fputs(STDERR, "ERROR Unknown $stringid,$componentname\n"); 119 | $status = 1; 120 | } 121 | } 122 | } 123 | $component->clear(); 124 | } 125 | } 126 | } 127 | 128 | fputs(STDOUT, date('Y-m-d H:i', time())); 129 | fputs(STDOUT, " EXPORT INSTALLER JOB DONE\n"); 130 | 131 | exit($status); 132 | -------------------------------------------------------------------------------- /cli/rev-clean.php: -------------------------------------------------------------------------------- 1 | . 17 | 18 | /** 19 | * Propagates deletions of English strings into the other lang packs 20 | * 21 | * The procedure is known as reverse cleanup. This script takes the most recent 22 | * snapshot of every component in the English lang pack. If a string removal is 23 | * part of the snapshot, the script propagates such removal into all other 24 | * languages. If the string is already removed in the other language, the 25 | * removing commit is not recorded. 26 | * 27 | * By default, the script checks just for the recent deletions, not older 28 | * than one day. During the initial import and once per day, for example, 29 | * run it with --full argument to re-check all the history, so that commits 30 | * dated into past are propadagated as well (full check takes ~30 mins). 31 | * 32 | * @package local_amos 33 | * @copyright 2010 David Mudrak 34 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 | */ 36 | 37 | define('CLI_SCRIPT', true); 38 | 39 | require_once(dirname(dirname(dirname(dirname(__FILE__)))) . '/config.php'); 40 | require_once($CFG->dirroot . '/local/amos/cli/config.php'); 41 | require_once($CFG->dirroot . '/local/amos/mlanglib.php'); 42 | require_once($CFG->dirroot . '/local/amos/renderer.php'); 43 | require_once($CFG->libdir.'/clilib.php'); 44 | 45 | list($options, $unrecognized) = cli_get_params(array('full'=>false), array('f'=>'full')); 46 | 47 | fputs(STDOUT, "*****************************************\n"); 48 | fputs(STDOUT, date('Y-m-d H:i', time())); 49 | fputs(STDOUT, " REVERSE CLEANUP JOB STARTED\n"); 50 | 51 | $mem = memory_get_usage(); 52 | $tree = mlang_tools::components_tree(); 53 | foreach ($tree as $vercode => $languages) { 54 | $version = mlang_version::by_code($vercode); 55 | foreach ($languages['en'] as $componentname => $unused) { 56 | if ($componentname == 'langconfig') { 57 | continue; 58 | } 59 | $memprev = $mem; 60 | $mem = memory_get_usage(); 61 | $memdiff = $memprev < $mem ? '+' : '-'; 62 | $memdiff = $memdiff . abs($mem - $memprev); 63 | $english = mlang_component::from_snapshot($componentname, 'en', $version, null, true, true); 64 | foreach ($english->get_iterator() as $string) { 65 | if (empty($options['full']) and $string->timemodified < time() - DAYSECS) { 66 | continue; 67 | } 68 | if ($string->deleted) { 69 | // propagate removal of this string to all other languages where it is present 70 | $stage = new mlang_stage(); 71 | foreach (array_keys($tree[$vercode]) as $otherlang) { 72 | if ($otherlang == 'en') { 73 | continue; 74 | } 75 | $other = mlang_component::from_snapshot($componentname, $otherlang, $version, null, true, false, array($string->id)); 76 | if ($other->has_string($string->id)) { 77 | $current = $other->get_string($string->id); 78 | if (!$current->deleted) { 79 | $current->deleted = true; 80 | $current->timemodified = time(); 81 | $stage->add($other); 82 | } 83 | } 84 | $other->clear(); 85 | unset($other); 86 | } 87 | $stage->rebase(); 88 | if ($stage->has_component()) { 89 | $string->timemodified = local_amos_renderer::commit_datetime($string->timemodified); 90 | $msg = <<id}' was removed from the English language pack by 94 | {$string->extra->userinfo} at {$string->timemodified}. Their commit message was: 95 | {$string->extra->commitmsg} 96 | {$string->extra->commithash} 97 | EOF; 98 | fputs(STDOUT, "COMMIT removal of '{$string->id}' from '{$english->name}'\n"); 99 | $stage->commit($msg, array('source' => 'revclean', 'userinfo' => 'AMOS-bot '), true); 100 | } 101 | $stage->clear(); 102 | unset($stage); 103 | } 104 | } 105 | } 106 | } 107 | 108 | fputs(STDOUT, date('Y-m-d H:i', time())); 109 | fputs(STDOUT, " REVERSE CLEANUP JOB DONE\n"); 110 | -------------------------------------------------------------------------------- /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('creditstitleshort', '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 | --------------------------------------------------------------------------------