From 1e96963085abe0bd0bea6565f84c67fa51f90779 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Fri, 16 Feb 2024 20:14:00 +0100 Subject: [PATCH 1/2] Added new MoodleUtil::isLangFile() method We are going to use it with the LangFilesOrdering Sniff that needs to check when a given file is a lang one or no. --- moodle/Tests/Util/MoodleUtilTest.php | 49 ++++++++++++++++++++++++++++ moodle/Util/MoodleUtil.php | 22 +++++++++++++ 2 files changed, 71 insertions(+) diff --git a/moodle/Tests/Util/MoodleUtilTest.php b/moodle/Tests/Util/MoodleUtilTest.php index c8e8844..de526f9 100644 --- a/moodle/Tests/Util/MoodleUtilTest.php +++ b/moodle/Tests/Util/MoodleUtilTest.php @@ -480,6 +480,55 @@ protected function cleanMoodleUtilCaches() { $apiCache->setValue(null, []); } + /** + * Data provider for testIsLangFile. + * + * @return array + */ + public static function isLangFileProvider(): array + { + return [ + 'Not in lang directory' => [ + 'value' => '/path/to/standard/file.php', + 'return' => false, + ], + 'In lang/en directory' => [ + 'value' => '/path/to/standard/lang/en/file.php', + 'return' => true, + ], + 'In lang directory but missing en' => [ + 'value' => '/path/to/standard/lang/file.php', + 'return' => false, + ], + 'In lang/en directory but missing .php' => [ + 'value' => '/path/to/standard/lang/en/file', + 'return' => false, + ], + 'In lang/en directory but another extension' => [ + 'value' => '/path/to/standard/lang/en/file.md', + 'return' => false, + ], + 'In lang sub-directory with not allowed chars' => [ + 'value' => '/path/to/standard/lang/@@@/file.php', + 'return' => false, + ], + ]; + } + + /** + * @dataProvider isLangFileProvider + */ + public function testIsLangFile( + string $filepath, + bool $expected + ): void { + $phpcsConfig = new Config(); + $phpcsRuleset = new Ruleset($phpcsConfig); + $file = new File($filepath, $phpcsRuleset, $phpcsConfig); + + $this->assertEquals($expected, MoodleUtil::isLangFile($file)); + } + /** * Data provider for testIsUnitTest. * diff --git a/moodle/Util/MoodleUtil.php b/moodle/Util/MoodleUtil.php index 563c28f..045d3ed 100644 --- a/moodle/Util/MoodleUtil.php +++ b/moodle/Util/MoodleUtil.php @@ -442,6 +442,28 @@ public static function getMoodleRoot(?File $file = null, bool $selfPath = true): return self::$moodleRoot; } + /** + * Whether this file is a lang file. + * + * @param File $phpcsFile + * @return bool + */ + public static function isLangFile(File $phpcsFile): bool + { + // If the file is not under a /lang/[a-zA-Z0-9_-]+/ directory, nothing to check. + // (note that we are using that regex because it's what PARAM_LANG does). + if (preg_match('~/lang/[a-zA-Z0-9_-]+/~', $phpcsFile->getFilename()) === 0) { + return false; + } + + // If the file is not a PHP file, nothing to check. + if (substr($phpcsFile->getFilename(), -4) !== '.php') { + return false; + } + + return true; + } + /** * Whether this file is a unit test file. * From 238d3f495af0422eb115b377b7a550f1b8f29349 Mon Sep 17 00:00:00 2001 From: "Eloy Lafuente (stronk7)" Date: Mon, 19 Feb 2024 10:31:12 +0100 Subject: [PATCH 2/2] Add the new moodle.FilesLangFilesOrdering sniff This sniff examines exclusively lang files, only for Moodle 4.4 and up, and has the following features: - Detect "groups" of lang strings, (normal, deprecated) within the lang file, checking for correct order within each group. - If other comments, different from the deprecated ones, are found, for safety the sniff will stop fixing strings after that point, although it will continue reporting them as out of order. - Detect wrong variable names (different from `$string`). - Detect duplicate strings within each lang file group. - Detect strings with incorrect trailing contents after it (php code, invalid comment...). - Detect strings out of order within each lang file group and fix them. Notes about the fixing: - Due to CodeSniffer fixing limits, this has been the really complex thing to implement and to guarantee that the fixer works also for "big" lang files (say error.php and similar ones). - To do so, instead of fixing every case 1 by 1, while we are checking the file... we have to accumulate all the changes in custom structure and then, apply everything together as a big changeset. - As said above, for safety, if an unexpected comment is found, the fixer stops applying any change after that line. In order to allow it to continue fixing, the comment must be removed. Only comments allowed are the "^// Deprecated since " ones, used to detect the groups of strings. --- .../Sniffs/Files/LangFilesOrderingSniff.php | 335 ++++++++++++++++++ .../Sniffs/Files/LangFilesOrderingTest.php | 97 +++++ .../langFilesOrdering/lang/en/correct.php | 28 ++ .../lang/en/correct.php.fixed | 28 ++ .../lang/en/withWarningsAndErrors.php | 64 ++++ .../lang/en/withWarningsAndErrors.php.fixed | 64 ++++ .../lang/en/withoutLangStrings.php | 25 ++ .../lang/en@wrong/incorrectLangDir.php | 25 ++ 8 files changed, 666 insertions(+) create mode 100644 moodle/Sniffs/Files/LangFilesOrderingSniff.php create mode 100644 moodle/Tests/Sniffs/Files/LangFilesOrderingTest.php create mode 100644 moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php create mode 100644 moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php.fixed create mode 100644 moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php create mode 100644 moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php.fixed create mode 100644 moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withoutLangStrings.php create mode 100644 moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en@wrong/incorrectLangDir.php diff --git a/moodle/Sniffs/Files/LangFilesOrderingSniff.php b/moodle/Sniffs/Files/LangFilesOrderingSniff.php new file mode 100644 index 0000000..a2dc577 --- /dev/null +++ b/moodle/Sniffs/Files/LangFilesOrderingSniff.php @@ -0,0 +1,335 @@ +. + +/** + * This sniff verifies that lang files are sorted alphabetically by string key. + * + * @copyright 2024 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com} + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace MoodleHQ\MoodleCS\moodle\Sniffs\Files; + +use MoodleHQ\MoodleCS\moodle\Util\MoodleUtil; +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Sniffs\Sniff; + +class LangFilesOrderingSniff implements Sniff +{ + /** + * @var string|null The previous string that has been processed. + * + * We use this variable to compare the current string with the previous one. And decide + * if the current string is a duplicate or if it's out of order. + */ + protected ?string $previousString = null; + + /** + * @var int pointer to the token where we should stop fixing the file (defaults to last token). + * + * When we find a comment that is not a "Deprecated since Moodle" one, we will stop fixing the file. + */ + protected int $stopFixingPtr = 999999999; + + /** + * @var array An array which keys are all the known strings, grouped, and the values are the start and end pointers to them. + * + * We use this array to, accurately, know where to move every string on each fixing iteration. + */ + protected array $strings = []; + + public function register(): array { + return [T_OPEN_TAG]; // We are going to process the whole file, finding all the strings and comments within it. + } + + public function process(File $phpcsFile, $stackPtr): void { + // If the file is not a lang file, return. + if (!MoodleUtil::isLangFile($phpcsFile)) { + return; + } + + // Only for Moodle 4.4dev (404) and up. + // Make and exception for unit tests, so they are run always. + if (!MoodleUtil::meetsMinimumMoodleVersion($phpcsFile, 404) && !MoodleUtil::isUnitTestRunning()) { + return; // @codeCoverageIgnore + } + + // Get the file tokens, for ease of use. + $tokens = $phpcsFile->getTokens(); + + // Ensure that we start from scratch on each file and pass. + $this->previousString = null; + $this->stopFixingPtr = 999999999; + $this->strings = []; + + // Let's find the first variable and start the process. + $currentPtr = $phpcsFile->findNext(T_VARIABLE, $stackPtr + 1); + if ($currentPtr === false) { + return; // No strings found, nothing to do. + } + + // It's time to iterate over all the strings and comments till the end of the file. + // We'll go accumulating all the strings by group, with their start and end pointers as values. + $currentGroup = 'main'; // The default group to start with, we'll change it each time we find a new section. + do { + // Let's manage comments first (so we know if we are changing the current group). + + // Correct comments are in new line and begin with "// Deprecated since ". + if ( + $tokens[$currentPtr]['code'] === T_COMMENT && + strpos($tokens[$currentPtr]['content'], '// Deprecated since ') === 0 && + $tokens[$currentPtr - 1]['content'] === "\n" + ) { + $currentGroup = trim($tokens[$currentPtr]['content']); + } + + // If we find a comment that is not the standard one, we will stop fixing the file here. And error. + if ( + $tokens[$currentPtr]['code'] === T_COMMENT && + strpos($tokens[$currentPtr]['content'], '// Deprecated since ') === false + ) { + $phpcsFile->addWarning( + 'Unexpected comment found. Auto-fixing will not work after this comment', + $currentPtr, + 'UnexpectedComment' + ); + if ($this->stopFixingPtr > $currentPtr) { + $this->stopFixingPtr = $currentPtr; // Update the stop fixing pointer. + } + } + + if ($tokens[$currentPtr]['code'] === T_COMMENT) { + continue; // We are done for comment tokens. + } + + // Arrived here, all the tokens are variables, so we don't need to check for that. + + // If the name of the variable is not "$string", error. + if ($tokens[$currentPtr]['content'] !== '$string') { + $phpcsFile->addError( + 'Variable "%s" is not expected in a lang file', + $currentPtr, + 'UnexpectedVariable', + [$tokens[$currentPtr]['content']] + ); + continue; // We are done for this token. + } + + // Get the string key, if any. + if (!$stringKey = $this->getStringKey($phpcsFile, $currentPtr)) { + continue; // Problems with this string key, skip it (has been already reported). + } + + // Have found a valid $string[KEY], let's calculate the end and store it. + if ($currentEnd = $this->getStringEnd($phpcsFile, $currentPtr)) { + if (!isset($this->strings[$currentGroup])) { + $this->strings[$currentGroup] = []; + $this->previousString = null; // Reset the previous string on new group. + } + // Check if the string already has been found earlier. + if (isset($this->strings[$currentGroup][$stringKey])) { + $phpcsFile->addError('The string key "%s" is duplicated', $currentPtr, 'DuplicatedKey', [$stringKey]); + continue; // We are done for this string, won't report anything about it till fixed. + } else { + // We can safely add the string to the group, if we are before the last pointer to fix. + if ($currentPtr < $this->stopFixingPtr) { + $this->strings[$currentGroup][$stringKey] = [$currentPtr, $currentEnd]; + } + } + } + + if (null === $currentEnd) { + // The string end is not as expected, report as error unless the next token + // after the semicolon is a comment. In that case, we won't report it because + // UnexpectedComment will take on it. + $delegateToUnexpectedComment = false; + $semicolonPtr = $phpcsFile->findNext(T_SEMICOLON, $currentPtr + 1); + if ( + ( + isset($tokens[$semicolonPtr + 1]) && // There is a next token (not the end of the file) + $tokens[$semicolonPtr + 1]['code'] === T_COMMENT // And it's a comment. + ) || + ( + isset($tokens[$semicolonPtr + 2]) && // Or there are 2 more tokens (not the end of the file). + $tokens[$semicolonPtr + 1]['code'] === T_WHITESPACE && // And they are whitespace + comment. + $tokens[$semicolonPtr + 2]['code'] === T_COMMENT + ) + ) { + $delegateToUnexpectedComment = true; + } + if (!$delegateToUnexpectedComment) { + $phpcsFile->addError( + 'Unexpected string end, it should be a line feed after a semicolon', + $currentPtr, + 'UnexpectedEnd' + ); + continue; // We are done for this string, won't report anything about it till fixed. + } + } + + // Note: We only issue these warnings if there are previous strings to compare with, + // and, obviously, if the string is out of order. + if ($this->previousString && strcmp($this->previousString, $stringKey) > 0) { + // We are going to add this as fixable warning only if we are + // before the last pointer to fix. This is an unordered string. + $phpcsFile->addWarning( + 'The string key "%s" is not in the correct order, it should be before "%s"', + $currentPtr, + 'IncorrectOrder', + [$stringKey, $this->previousString], + 0, + $currentPtr < $this->stopFixingPtr + ); + } + + // Feed $previousString with the current string key. + $this->previousString = $stringKey; + } while ($currentPtr = $phpcsFile->findNext([T_VARIABLE, T_COMMENT], $currentPtr + 1)); // Move to next. + + // If we are fixing the file, we need to sort the strings and move them to the correct position. + if ($phpcsFile->fixer->enabled) { + $this->sortStringsAndFix($phpcsFile); + } + } + + /** + * Given a lang file, fix all the sorting issues found. + * + * This is really similar to the insertion-sort algorithm, but with + * a few optimisations to avoid unnecessary iterations. Should be + * efficient enough against lists that are expected to be not + * too long and already mostly sorted. + * + * @param File $phpcsFile The lang file being processed. + */ + protected function sortStringsAndFix(File $phpcsFile): void { + // Because of hard restrictions in CodeSniffer fixer (we cannot apply more than one change + // to the same token in the same pass), we need to accumulate all the changes and apply them + // at the end of the process. So we are going to build a big changeset to be applied all together. + // Keys will be the token index and values an array, with operation (index, DELETE, INSERT) and content. + + // Get the file tokens, for ease of use. + $tokens = $phpcsFile->getTokens(); + + // We are going to perform the sorting within each detected group/section. + foreach ($this->strings as $group => $strings) { + $changeSet = []; // The changeset to be applied at the end of the iteration. + + $strings = $this->strings[$group]; + // Let's compare the keys in the array of strings with the sorted version of it. + $sorted = $unSorted = array_keys($strings); + sort($sorted, SORT_STRING); + $count = count($sorted); + for ($i = 0; $i < $count; $i++) { + $sortedKey = $sorted[$i]; + $stringsKey = $unSorted[$i]; + + // The string being checked is not in order (comparing with the sorted array). + if ($sortedKey !== $stringsKey) { + // Apply the changes to the strings array by moving the key to the correct position. + $keyValue = $strings[$sortedKey]; + // Remove the element to move. + unset($strings[$sortedKey]); + // Rebuild the array, with the element in new position. + $strings = array_slice($strings, 0, $i, true) + + [$sortedKey => $keyValue] + + array_slice($strings, $i, null, true); + $this->strings[$group] = $strings; // Update the group array with the rebuilt version. + $unSorted = array_keys($strings); // Update the unsorted keys array. + + // Now add the required changes to the changeset that we'll be using when fixing the file. + // For every token in the string being moved, delete it and add it in the correct position. + foreach (range($keyValue[0], $keyValue[1]) as $tokenIndex) { + $tempToken = $tokens[$tokenIndex]; // Store the token. + $changeSet[$tokenIndex]['DELETE'] = ''; // Delete the current string token. + // Insert the token before the previous string. + if (!isset($changeSet[$strings[$stringsKey][0] - 1]['INSERT'])) { + $changeSet[$strings[$stringsKey][0] - 1]['INSERT'] = ''; + } + $changeSet[$strings[$stringsKey][0] - 1]['INSERT'] .= $tempToken['content']; + } + } + } + + // Let's apply the accumulated changes to the file. + if (!empty($changeSet)) { + $phpcsFile->fixer->beginChangeset(); + foreach ($changeSet as $tokenIndex => $operations) { + if (isset($operations['DELETE'])) { + $phpcsFile->fixer->replaceToken($tokenIndex, ''); + } + if (isset($operations['INSERT'])) { + $phpcsFile->fixer->addContent($tokenIndex, $operations['INSERT']); + } + } + $phpcsFile->fixer->endChangeset(); + } + } + } + + /** + * Return the string key corresponding to the string at the pointer. + * Note that the key has got any quote (single or double) trimmed. + * + * @param File $phpcsFile + * @param int $stackPtr + * @return string|null + */ + protected function getStringKey(File $phpcsFile, int $stackPtr): ?string { + $tokens = $phpcsFile->getTokens(); + + // If the structure is not exactly: $string[KEY], add error and return null. + if ( + $tokens[$stackPtr + 1]['code'] !== T_OPEN_SQUARE_BRACKET || + $tokens[$stackPtr + 2]['code'] !== T_CONSTANT_ENCAPSED_STRING || + $tokens[$stackPtr + 3]['code'] !== T_CLOSE_SQUARE_BRACKET + ) { + $phpcsFile->addError( + "Unexpected string syntax, it should be `\$string['key']`", + $stackPtr, + 'UnexpectedSyntax' + ); + return null; + } + + // Now we can safely extract the string key and return it. + return trim($tokens[$stackPtr + 2]['content'], "'\""); + } + + /** + * Return the string final pointer, it should be always a \n after a T_SEMICOLON. + * + * @param File $phpcsFile + * @param int $stackPtr + * @return int|null The pointer to the end of the string, or null if it's not an expected string end. + */ + protected function getStringEnd(File $phpcsFile, int $stackPtr): ?int { + $tokens = $phpcsFile->getTokens(); + $currentEndToken = $phpcsFile->findNext(T_SEMICOLON, $stackPtr + 1) + 1; + + // Verify that the current end token is a line feed, if not, we won't be able to fix (swap). + if ( + !isset($tokens[$currentEndToken]) || + $tokens[$currentEndToken]['code'] !== T_WHITESPACE || + $tokens[$currentEndToken]['content'] !== "\n" + ) { + return null; // This is not an expected string end. + } + + return $currentEndToken; + } +} diff --git a/moodle/Tests/Sniffs/Files/LangFilesOrderingTest.php b/moodle/Tests/Sniffs/Files/LangFilesOrderingTest.php new file mode 100644 index 0000000..65eca2c --- /dev/null +++ b/moodle/Tests/Sniffs/Files/LangFilesOrderingTest.php @@ -0,0 +1,97 @@ +. + +namespace MoodleHQ\MoodleCS\moodle\Tests\Files; + +use MoodleHQ\MoodleCS\moodle\Tests\MoodleCSBaseTestCase; + +/** + * Test the LangFilesOrderingSniff sniff. + * + * @copyright 2024 onwards Eloy Lafuente (stronk7) {@link https://stronk7.com} + * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * + * @covers \MoodleHQ\MoodleCS\moodle\Sniffs\Files\LangFilesOrderingSniff + */ +class LangFilesOrderingTest extends MoodleCSBaseTestCase +{ + /** + * @dataProvider filesOrderingProvider + */ + public function testLangFilesOrdering( + string $fixture, + array $warnings, + array $errors + ) { + $this->setStandard('moodle'); + $this->setSniff('moodle.Files.LangFilesOrdering'); + $this->setFixture(__DIR__ . '/fixtures/langFilesOrdering/' . $fixture); + $this->setWarnings($warnings); + $this->setErrors($errors); + + $this->verifyCsResults(); + } + + /** + * Data provider for testLangFilesOrdering tests. + * + * @return array + */ + public static function filesOrderingProvider(): array { + return [ + 'processed correct' => [ + 'lang/en/correct.php', + [], + [], + ], + 'processed with problems' => [ + 'lang/en/withWarningsAndErrors.php', + [ + 27 => '"modvisiblewithstealth_help" is not in the correct order', + 30 => 1, + 34 => 1, + 37 => 1, + 38 => 1, + 45 => 1, + 46 => 1, + 47 => 1, + 51 => 1, + 60 => 'Unexpected comment found. Auto-fixing will not work after this', + 61 => 1, + 63 => 1, + 64 => 1, + ], + [ + 31 => 'Variable "$anothervar" is not expected', + 33 => 'Unexpected string syntax, it should be', + 40 => 'The string key "yourself" is duplicated', + 42 => 'Unexpected string end', + ], + ], + 'without strings' => [ + 'lang/en/withoutLangStrings.php', + [], + [], + ], + 'not processed' => [ + 'lang/en@wrong/incorrectLangDir.php', + [], + [], + ], + ]; + } +} diff --git a/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php new file mode 100644 index 0000000..57ff946 --- /dev/null +++ b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php @@ -0,0 +1,28 @@ +. + +/** + * A fixture lang-like file in correct place to test the LangFilesOrderingSniff. + * + * @package core + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// This lang file is perfect! +$string['aaaa'] = 'aaaa'; +$string['bbbb'] = 'bbbb'; +$string['cccc'] = 'cccc'; diff --git a/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php.fixed b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php.fixed new file mode 100644 index 0000000..57ff946 --- /dev/null +++ b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/correct.php.fixed @@ -0,0 +1,28 @@ +. + +/** + * A fixture lang-like file in correct place to test the LangFilesOrderingSniff. + * + * @package core + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// This lang file is perfect! +$string['aaaa'] = 'aaaa'; +$string['bbbb'] = 'bbbb'; +$string['cccc'] = 'cccc'; diff --git a/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php new file mode 100644 index 0000000..50de328 --- /dev/null +++ b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php @@ -0,0 +1,64 @@ +. + +/** + * A fixture lang-like file in correct place to test the LangFilesOrderingSniff. + * + * @package core + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['abouttobeinstalled'] = 'about to be installed'; +$string['yourlastlogin'] = 'Your last login was'; +$string['modvisiblewithstealth_help'] = '* Show on course page: Available to students (subject to any access restrictions which may be set). +* Hide on course page: Not available to students. +* Make available but don\'t show on course page: Available to students if you provide a link. Activities will still appear in the gradebook and other reports.'; +$string['action'] = 'Action'; +$anothervar['choco'] = 'kk'; +$string['youneedtoenrol'] = 'To perform that action you need to enrol in this course.'; +$string['bad' ] = 'Bad'; +$string['actionchoice'] = 'What do you want to do with the file \'{$a}\'?'; +$string['actions'] = 'Actions'; +$string['moodlenet:sharesuccesstext'] = "Almost done! Visit your drafts in MoodleNet to finish sharing your content."; +$string['actionsmenu2'] = "Actions menu2"; +$string["actionsmenu"] = 'Actions menu'; +$string['yourself'] = 'yourself'; +$string['yourself'] = 'yourself'; +$string['yourteacher'] = 'your {$a}'; +$string['withoutlinefeed'] = 'Without line feed'; echo 'I should not be here. Not fixable'; +$string['yourwordforx'] = 'Your word for \'{$a}\''; +$string['zippingbackup'] = 'Zipping backup'; +$string['deprecatedeventname'] = '{$a} (no longer in use)'; +$string['accept'] = 'Accept'; +$string['1111'] = '1111'; + +// Deprecated since Moodle 4.0. +$string['cupplyinfo'] = 'More details'; +$string['bcreateuserandpass'] = 'Choose your username and password'; + +// Deprecated since Moodle 4.3. +$string['clicktochangeinbrackets'] = '{$a} (Click to change)'; + +// Deprecated since Moodle 4.4. +$string['asocialheadline'] = 'Social forum - latest topics'; +$string['topicshow'] = 'Show this topic to {$a}'; + +$string['zzzz'] = 'zzzz'; // This comment shouldn't be here. No more auto-fixing after this line. +$string['yyyy'] = 'yyyy'; + +$string['bbbb'] = 'bbbb'; +$string['aaaa'] = 'aaaa'; diff --git a/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php.fixed b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php.fixed new file mode 100644 index 0000000..68f43fc --- /dev/null +++ b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withWarningsAndErrors.php.fixed @@ -0,0 +1,64 @@ +. + +/** + * A fixture lang-like file in correct place to test the LangFilesOrderingSniff. + * + * @package core + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['1111'] = '1111'; +$string['abouttobeinstalled'] = 'about to be installed'; +$string['accept'] = 'Accept'; +$string['action'] = 'Action'; +$string['actionchoice'] = 'What do you want to do with the file \'{$a}\'?'; +$string['actions'] = 'Actions'; +$string["actionsmenu"] = 'Actions menu'; +$string['actionsmenu2'] = "Actions menu2"; +$string['deprecatedeventname'] = '{$a} (no longer in use)'; +$string['modvisiblewithstealth_help'] = '* Show on course page: Available to students (subject to any access restrictions which may be set). +* Hide on course page: Not available to students. +* Make available but don\'t show on course page: Available to students if you provide a link. Activities will still appear in the gradebook and other reports.'; +$string['moodlenet:sharesuccesstext'] = "Almost done! Visit your drafts in MoodleNet to finish sharing your content."; +$string['youneedtoenrol'] = 'To perform that action you need to enrol in this course.'; +$string['yourlastlogin'] = 'Your last login was'; +$anothervar['choco'] = 'kk'; +$string['bad' ] = 'Bad'; +$string['yourself'] = 'yourself'; +$string['yourself'] = 'yourself'; +$string['yourteacher'] = 'your {$a}'; +$string['withoutlinefeed'] = 'Without line feed'; echo 'I should not be here. Not fixable'; +$string['yourwordforx'] = 'Your word for \'{$a}\''; +$string['zippingbackup'] = 'Zipping backup'; + +// Deprecated since Moodle 4.0. +$string['bcreateuserandpass'] = 'Choose your username and password'; +$string['cupplyinfo'] = 'More details'; + +// Deprecated since Moodle 4.3. +$string['clicktochangeinbrackets'] = '{$a} (Click to change)'; + +// Deprecated since Moodle 4.4. +$string['asocialheadline'] = 'Social forum - latest topics'; +$string['topicshow'] = 'Show this topic to {$a}'; + +$string['zzzz'] = 'zzzz'; // This comment shouldn't be here. No more auto-fixing after this line. +$string['yyyy'] = 'yyyy'; + +$string['bbbb'] = 'bbbb'; +$string['aaaa'] = 'aaaa'; diff --git a/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withoutLangStrings.php b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withoutLangStrings.php new file mode 100644 index 0000000..5a93d88 --- /dev/null +++ b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en/withoutLangStrings.php @@ -0,0 +1,25 @@ +. + +/** + * A fixture lang-like file in correct place to test the LangFilesOrderingSniff. + * + * @package core + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// Surprise, surprise, this lang file does not have any string. \ No newline at end of file diff --git a/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en@wrong/incorrectLangDir.php b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en@wrong/incorrectLangDir.php new file mode 100644 index 0000000..673b4b3 --- /dev/null +++ b/moodle/Tests/Sniffs/Files/fixtures/langFilesOrdering/lang/en@wrong/incorrectLangDir.php @@ -0,0 +1,25 @@ +. + +/** + * A fixture lang-like file in incorrect place to test the LangFilesOrderingSniff. + * + * @package core + * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +$string['wedontneedmany'] = 'As far as this file is not going to be processed (wrong location), one string is enough.'; \ No newline at end of file