diff --git a/docs/commands.md b/docs/commands.md index 00f4f8c61..b9f68054e 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` @@ -45,6 +45,8 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). - **/authme version**: Show detailed information about the installed AuthMeReloaded version, the developers, contributors, and license. - **/authme converter** <job>: Converter command for AuthMeReloaded.
Requires `authme.admin.converter` +- **/authme messages**: Adds missing messages to the current messages file. +
Requires `authme.admin.updatemessages` - **/authme help** [query]: View detailed help for /authme commands. - **/login** <password>: Command to log in using AuthMeReloaded.
Requires `authme.player.login` @@ -61,7 +63,8 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`). - **/changepassword** <oldPassword> <newPassword>: Command to change your password using AuthMeReloaded.
Requires `authme.player.changepassword` - **/changepassword help** [query]: View detailed help for /changepassword commands. -- **/email**: The AuthMeReloaded Email command base. +- **/email**: The AuthMeReloaded email command base. +- **/email show**: Show your current email address. - **/email add** <email> <verifyEmail>: Add a new email address to your account.
Requires `authme.player.email.add` - **/email change** <oldEmail> <newEmail>: Change an email address of your account. @@ -73,7 +76,6 @@ brackets; optional arguments are enclosed in square brackets (`[ ]`).
Requires `authme.player.captcha` - **/captcha help** [query]: View detailed help for /captcha commands. - --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sat Oct 01 23:33:39 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 18:25:12 CEST 2016 diff --git a/src/main/resources/config.yml b/docs/config.md similarity index 54% rename from src/main/resources/config.yml rename to docs/config.md index bed1d2c43..10980d8a5 100644 --- a/src/main/resources/config.yml +++ b/docs/config.md @@ -1,109 +1,159 @@ + + + +## AuthMe Configuration +The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, +with which you can configure various settings. This following is the initial contents of +the generated config.yml file. + +```yml + DataSource: # What type of database do you want to use? # Valid values: sqlite, mysql - backend: sqlite + backend: 'SQLITE' # Enable database caching, should improve database performance caching: true - # Database location - mySQLHost: 127.0.0.1 - # Database Port + # Database host address + mySQLHost: '127.0.0.1' + # Database port mySQLPort: '3306' # Username about Database Connection Infos - mySQLUsername: authme + mySQLUsername: 'authme' # Password about Database Connection Infos mySQLPassword: '12345' # Database Name, use with converters or as SQLITE database name - mySQLDatabase: authme + mySQLDatabase: 'authme' # Table of the database - mySQLTablename: authme + mySQLTablename: 'authme' # Column of IDs to sort data - mySQLColumnId: id + mySQLColumnId: 'id' # Column for storing or checking players nickname - mySQLColumnName: username + mySQLColumnName: 'username' + # Column for storing or checking players RealName + mySQLRealName: 'realname' # Column for storing players passwords - mySQLColumnPassword: password + mySQLColumnPassword: 'password' # Column for storing players emails - mySQLColumnEmail: email - # Column for Saving if a player is logged in or not - mySQLColumnLogged: isLogged - # Column for storing players IPs - mySQLColumnIp: ip + mySQLColumnEmail: 'email' + # Column for storing if a player is logged in or not + mySQLColumnLogged: 'isLogged' + # Column for storing players ips + mySQLColumnIp: 'ip' # Column for storing players lastlogins - mySQLColumnLastLogin: lastlogin - # Column for SaveQuitLocation - X - mySQLlastlocX: x - # Column for SaveQuitLocation - Y - mySQLlastlocY: y - # Column for SaveQuitLocation - Z - mySQLlastlocZ: z - # Column for SaveQuitLocation - World name - mySQLlastlocWorld: world - # Column for RealName - mySQLRealName: realname + mySQLColumnLastLogin: 'lastlogin' + # Column for storing player LastLocation - X + mySQLlastlocX: 'x' + # Column for storing player LastLocation - Y + mySQLlastlocY: 'y' + # Column for storing player LastLocation - Z + mySQLlastlocZ: 'z' + # Column for storing player LastLocation - World Name + mySQLlastlocWorld: 'world' + # Overrides the size of the DB Connection Pool, -1 = Auto + poolSize: -1 +ExternalBoardOptions: + # Column for storing players passwords salts + mySQLColumnSalt: '' + # Column for storing players groups + mySQLColumnGroup: '' + # -1 means disabled. If you want that only activated players + # can log into your server, you can set here the group number + # of unactivated users, needed for some forum/CMS support + nonActivedUserGroup: -1 + # Other MySQL columns where we need to put the username (case-sensitive) + mySQLOtherUsernameColumns: [] + # How much log2 rounds needed in BCrypt (do not change if you do not know what it does) + bCryptLog2Round: 10 + # phpBB table prefix defined during the phpBB installation process + phpbbTablePrefix: 'phpbb_' + # phpBB activated group ID; 2 is the default registered group defined by phpBB + phpbbActivatedGroupId: 2 + # Wordpress prefix defined during WordPress installation + wordpressTablePrefix: 'wp_' +Converter: + Rakamak: + # Rakamak file name + fileName: 'users.rak' + # Rakamak use IP? + useIP: false + # Rakamak IP file name + ipFileName: 'UsersIp.rak' + CrazyLogin: + # CrazyLogin database file name + fileName: 'accounts.db' settings: sessions: # Do you want to enable the session feature? # If enabled, when a player authenticates successfully, # his IP and his nickname is saved. # The next time the player joins the server, if his IP - # is the same of the last time, and the timeout time - # hasn't expired, he will not need to authenticate. + # is the same as last time and the timeout hasn't + # expired, he will not need to authenticate. enabled: false - # After how many minutes a session should expire? - # Consider that session will end only after the timeout time, and - # if the player's ip has changed but the timeout hasn't expired, - # player will be kicked out of sever due to invalidSession! + # After how many minutes should a session expire? + # Remember that sessions will end only after the timeout, and + # if the player's IP has changed but the timeout hasn't expired, + # the player will be kicked from the server due to invalid session timeout: 10 - # Should the session expire if the player try to login with an - # another IP Address? + # Should the session expire if the player tries to log in with + # another IP address? sessionExpireOnIpChange: true + # Message language, available languages: + # https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md + messagesLanguage: 'en' restrictions: - # Can not authenticated players chat and see the chat log? - # Care that this feature blocks also all the commands not + # Keeps collisions disabled for logged players + # Works only with MC 1.9 + keepCollisionsDisabled: false + # Can not authenticated players chat? + # Keep in mind that this feature also blocks all commands not # listed in the list below. allowChat: false - # Can not authenticated players see the chat log? + # Hide the chat log from players who are not authenticated? hideChat: false - # Commands allowed when a player is not authenticated - allowCommands: - - /login - - /register - - /l - - /reg - - /email - - /captcha - # Max number of allowed registrations per IP (default: 1) + # Allowed commands for unauthenticated players + allowCommands: + - '/login' + - '/register' + - '/l' + - '/reg' + - '/email' + - '/captcha' + # Max number of allowed registrations per IP + # The value 0 means an unlimited number of registrations! maxRegPerIp: 1 - # Max allowed username length + # Minimum allowed username length + minNicknameLength: 4 + # Maximum allowed username length maxNicknameLength: 16 # When this setting is enabled, online players can't be kicked out # due to "Logged in from another Location" - # This setting will prevent potetial security exploits. + # This setting will prevent potential security exploits. ForceSingleSession: true ForceSpawnLocOnJoin: - # If enabled, every player will be teleported to the world spawnpoint - # after successful authentication. - # The quit location of the player will be overwritten. + # If enabled, every player that spawn in one of the world listed in + # "ForceSpawnLocOnJoin.worlds" will be teleported to the spawnpoint after successful + # authentication. The quit location of the player will be overwritten. # This is different from "teleportUnAuthedToSpawn" that teleport player - # back to his quit location after the authentication. + # to the spawnpoint on join. enabled: false # WorldNames where we need to force the spawn location # Case-sensitive! - worlds: + worlds: - 'world' - 'world_nether' - 'world_the_end' # This option will save the quit location of the players. SaveQuitLocation: false # To activate the restricted user feature you need - # to enable this option and configure the - # AllowedRestrctedUser field. + # to enable this option and configure the AllowedRestrictedUser field. AllowRestrictedUser: false # The restricted user feature will kick players listed below - # if they don't match of the defined ip address. + # if they don't match the defined IP address. # Example: - # AllowedRestrictedUser: - # - playername;127.0.0.1 + # AllowedRestrictedUser: + # - playername;127.0.0.1 AllowedRestrictedUser: [] # Should unregistered players be kicked immediately? kickNonRegistered: false @@ -113,23 +163,21 @@ settings: # After the authentication they will be teleported back to # their normal position. teleportUnAuthedToSpawn: false - # Minimum allowed nick length - minNicknameLength: 4 # Can unregistered players walk around? allowMovement: false # Should not authenticated players have speed = 0? # This will reset the fly/walk speed to default value after the login. removeSpeed: true - # After how many time players who fail to login or register - # should be kicked? Set to 0 to disable. + # After how many seconds should players who fail to login or register + # be kicked? Set to 0 to disable. timeout: 30 - # Regex sintax of allowed characters in the player name. + # Regex syntax of allowed characters in the player name. allowedNicknameCharacters: '[a-zA-Z0-9_]*' - # How far can unregistered players walk? Set to 0 - # for unlimited radius + # How far can unregistered players walk? + # Set to 0 for unlimited radius allowedMovementRadius: 100 # Enable double check of password when you register - # when it's true, registration require that kind of command: + # when it's true, registration requires that kind of command: # /register enablePasswordConfirmation: true # Should we protect the player inventory before logging in? Requires ProtocolLib. @@ -141,64 +189,67 @@ settings: displayOtherAccounts: true # Ban ip when the ip is not the ip registered in database banUnsafedIP: false - # Spawn Priority, Values : authme, essentials, multiverse, default - spawnPriority: authme,essentials,multiverse,default + # Spawn priority; values: authme, essentials, multiverse, default + spawnPriority: 'authme,essentials,multiverse,default' # Maximum Login authorized by IP maxLoginPerIp: 0 # Maximum Join authorized by IP maxJoinPerIp: 0 - # AuthMe will NEVER teleport players ! + # AuthMe will NEVER teleport players if set to true! noTeleport: false - # Regex syntax for allowed Chars in passwords. + # Regex syntax for allowed chars in passwords allowedPasswordCharacters: '[\x21-\x7E]*' - # Keeps collisions disabled for logged players - # Works only with MC 1.9 - keepCollisionsDisabled: false + # Log level: INFO, FINE, DEBUG. Use INFO for general messages, + # FINE for some additional detailed ones (like password failed), + # and DEBUG for debugging + logLevel: 'FINE' + # By default we schedule async tasks when talking to the database. If you want + # typical communication with the database to happen synchronously, set this to false + useAsyncTasks: true GameMode: - # ForceSurvivalMode to player when join ? + # Force survival gamemode when player joins? ForceSurvivalMode: false + unrestrictions: + # Below you can list all account names that + # AuthMe will ignore for registration or login, configure it + # at your own risk!! Remember that if you are going to add + # nickname with [], you have to delimit name with ' '. + # this option add compatibility with BuildCraft and some + # other mods. + # It is case-sensitive! + UnrestrictedName: [] security: # Minimum length of password minPasswordLength: 5 # Maximum length of password passwordMaxLength: 30 - # this is very important options, - # every time player join the server, - # if they are registered, AuthMe will switch him - # to unLoggedInGroup, this - # should prevent all major exploit. - # So you can set up on your Permission Plugin - # this special group with 0 permissions, or permissions to chat, - # or permission to - # send private message or all other perms that you want, - # the better way is to set up - # this group with few permissions, - # so if player try to exploit some account, - # they can - # do anything except what you set in perm Group. - # After a correct logged-in player will be - # moved to his correct permissions group! - # Pay attention group name is case sensitive, - # so Admin is different from admin, - # otherwise your group will be wiped, - # and player join in default group []! + # This is a very important option: every time a player joins the server, + # if they are registered, AuthMe will switch him to unLoggedInGroup. + # This should prevent all major exploits. + # You can set up your permission plugin with this special group to have no permissions, + # or only permission to chat (or permission to send private messages etc.). + # The better way is to set up this group with few permissions, so if a player + # tries to exploit an account they can do only what you've defined for the group. + # After, a logged in player will be moved to his correct permissions group! + # Please note that the group name is case-sensitive, so 'admin' is different from 'Admin' + # Otherwise your group will be wiped and the player will join in the default group []! # Example unLoggedinGroup: NotLogged - unLoggedinGroup: unLoggedinGroup - # possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, - # MYBB, IPB3, IPB4, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, - # DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM(for developpers only) - passwordHash: SHA256 - # salt length for the SALTED2MD5 MD5(MD5(password)+salt) + unLoggedinGroup: 'unLoggedinGroup' + # Possible values: MD5, SHA1, SHA256, WHIRLPOOL, XAUTH, MD5VB, PHPBB, + # MYBB, IPB3, PHPFUSION, SMF, XENFORO, SALTED2MD5, JOOMLA, BCRYPT, WBB3, SHA512, + # DOUBLEMD5, PBKDF2, PBKDF2DJANGO, WORDPRESS, ROYALAUTH, CUSTOM (for developers only) + passwordHash: 'SHA256' + # Salt length for the SALTED2MD5 MD5(MD5(password)+salt) doubleMD5SaltLength: 8 # If password checking return false, do we need to check with all # other password algorithm to check an old password? - # AuthMe will update the password to the new passwordHash! + # AuthMe will update the password to the new password hash supportOldPasswordHash: false - # Cancel unsafe passwords for being used, put them on lowercase! - #unsafePasswords: - #- '123456' - #- 'password' - unsafePasswords: + # Prevent unsafe passwords from being used; put them in lowercase! + # unsafePasswords: + # - '123456' + # - 'password' + unsafePasswords: - '123456' - 'password' - 'qwerty' @@ -206,7 +257,7 @@ settings: - '54321' - '123456789' registration: - # enable registration on the server? + # Enable registration on the server? enabled: true # Send every X seconds a message to a player to # remind him that he has to login/register @@ -217,40 +268,33 @@ settings: # Do we replace password registration by an email registration method? enableEmailRegistrationSystem: false # Enable double check of email when you register - # when it's true, registration require that kind of command: + # when it's true, registration requires that kind of command: # /register doubleEmailCheck: false - # Do we force kicking player after a successful registration? + # Do we force kick a player after a successful registration? # Do not use with login feature below forceKickAfterRegister: false # Does AuthMe need to enforce a /login after a successful registration? forceLoginAfterRegister: false - unrestrictions: - # below you can list all account names that - # AuthMe will ignore for registration or login, configure it - # at your own risk!! Remember that if you are going to add - # nickname with [], you have to delimit name with ' '. - # this option add compatibility with BuildCraft and some - # other mods. - # It is CaseSensitive! - UnrestrictedName: [] - # Message language, available : en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt - messagesLanguage: en - # Force these commands after /login, without any '/', use %p for replace with player name + # Force these commands after /login, without any '/', use %p to replace with player name forceCommands: [] - # Force these commands after /login as a server console, without any '/', use %p for replace with player name + # Force these commands after /login as service console, without any '/'. + # Use %p to replace with player name forceCommandsAsConsole: [] - # Force these commands after /register, without any '/', use %p for replace with player name + # Force these commands after /register, without any '/', use %p to replace with player name forceRegisterCommands: [] - # Force these commands after /register as a server console, without any '/', use %p for replace with player name + # Force these commands after /register as a server console, without any '/'. + # Use %p to replace with player name forceRegisterCommandsAsConsole: [] - # Do we need to display the welcome message (welcome.txt) after a login? + # Enable to display the welcome message (welcome.txt) after a login # You can use colors in this welcome.txt + some replaced strings: - # {PLAYER}: player name, {ONLINE}: display number of online players, {MAXPLAYERS}: display server slots, - # {IP}: player ip, {LOGINS}: number of players logged, {WORLD}: player current world, {SERVER}: server name + # {PLAYER}: player name, {ONLINE}: display number of online players, + # {MAXPLAYERS}: display server slots, {IP}: player ip, {LOGINS}: number of players logged, + # {WORLD}: player current world, {SERVER}: server name # {VERSION}: get current bukkit version, {COUNTRY}: player country useWelcomeMessage: true - # Do we need to broadcast the welcome message to all server or only to the player? set true for server or false for player + # Broadcast the welcome message to the server or only to the player? + # set true for server or false for player broadcastWelcomeMessage: false # Should we delay the join message and display it once the player has logged in? delayJoinMessage: false @@ -260,64 +304,115 @@ settings: removeJoinMessage: false # Should we remove leave messages altogether? removeLeaveMessage: false - # Do we need to add potion effect Blinding before login/register? + # Do we need to add potion effect Blinding before login/reigster? applyBlindEffect: false # Do we need to prevent people to login with another case? # If Xephi is registered, then Xephi can login, but not XEPHI/xephi/XePhI preventOtherCase: false - # Log level: INFO, FINE, DEBUG. Use INFO for general messages, - # FINE for some additional detailed ones (like password failed), - # and DEBUG for debug messages - logLevel: 'FINE' - # By default we schedule async tasks when talking to the database - # If you want typical communication with the database to happen synchronously, set this to false - useAsyncTasks: true -ExternalBoardOptions: - # MySQL column for the salt, needed for some forum/cms support - mySQLColumnSalt: '' - # MySQL column for the group, needed for some forum/cms support - mySQLColumnGroup: '' - # -1 mean disabled. If u want that only - # activated player can login in your server - # u can put in this options the group number - # of unactivated user, needed for some forum/cms support - nonActivedUserGroup: -1 - # Other MySQL columns where we need to put the Username (case sensitive) - mySQLOtherUsernameColumns: [] - # How much Log to Round needed in BCrypt(do not change it if you do not know what's your doing) - bCryptLog2Round: 10 - # phpBB prefix defined during phpbb installation process - phpbbTablePrefix: 'phpbb_' - # phpBB activated group id, 2 is default registered group defined by phpbb - phpbbActivatedGroupId: 2 - # WordPress prefix defined during WordPress installation process - wordpressTablePrefix: 'wp_' permission: - # Take care with this options, if you dont want - # to use Vault and Group Switching of - # AuthMe for unloggedIn players put true - # below, default is false. + # Take care with this option; if you want + # to use group switching of AuthMe + # for unloggedIn players, set this setting to true. + # Default is false. EnablePermissionCheck: false -BackupSystem: - # Enable or Disable Automatic Backup - ActivateBackup: false - # set Backup at every start of Server - OnServerStart: false - # set Backup at every stop of Server - OnServerStop: true - # Windows only mysql installation Path - MysqlWindowsPath: 'C:\Program Files\MySQL\MySQL Server 5.1\' +Email: + # Email SMTP server host + mailSMTP: 'smtp.gmail.com' + # Email SMTP server port + mailPort: 465 + # Email account which sends the mails + mailAccount: '' + # Email account password + mailPassword: '' + # Custom sender name, replacing the mailAccount name in the email + mailSenderName: '' + # Recovery password length + RecoveryPasswordLength: 8 + # Mail Subject + mailSubject: 'Your new AuthMe password' + # Like maxRegPerIP but with email + maxRegPerEmail: 1 + # Recall players to add an email? + recallPlayers: false + # Delay in minute for the recall scheduler + delayRecall: 5 + # Blacklist these domains for emails + emailBlacklisted: + - '10minutemail.com' + # Whitelist ONLY these domains for emails + emailWhitelisted: [] + # Send the new password drawn in an image? + generateImage: false + # The OAuth2 token + emailOauth2Token: '' +Hooks: + # Do we need to hook with multiverse for spawn checking? + multiverse: true + # Do we need to hook with BungeeCord? + bungeecord: false + # Send player to this BungeeCord server after register/login + sendPlayerTo: '' + # Do we need to disable Essentials SocialSpy on join? + disableSocialSpy: true + # Do we need to force /motd Essentials command on join? + useEssentialsMotd: false +GroupOptions: + # Unregistered permission group + UnregisteredPlayerGroup: '' + # Registered permission group + RegisteredPlayerGroup: '' +Protection: + # Enable some servers protection (country based login, antibot) + enableProtection: false + # Apply the protection also to registered usernames + enableProtectionRegistered: true + # Countries allowed to join the server and register. For country codes, see + # http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ + # PLEASE USE QUOTES! + countries: + - 'US' + - 'GB' + # Countries not allowed to join the server and register + # PLEASE USE QUOTES! + countriesBlacklist: + - 'A1' + # Do we need to enable automatic antibot system? + enableAntiBot: true + # Max number of players allowed to login in 5 secs + # before the AntiBot system is enabled automatically + antiBotSensibility: 10 + # Duration in minutes of the antibot automatic system + antiBotDuration: 10 + # Delay in seconds before the antibot activation + antiBotDelay: 60 +Purge: + # If enabled, AuthMe automatically purges old, unused accounts + useAutoPurge: false + # Number of days after which an account should be purged + daysBeforeRemovePlayer: 60 + # Do we need to remove the player.dat file during purge process? + removePlayerDat: false + # Do we need to remove the Essentials/userdata/player.yml file during purge process? + removeEssentialsFile: false + # World where are players.dat stores + defaultWorld: 'world' + # Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge? + removeLimitedCreativesInventories: false + # Do we need to remove the AntiXRayData/PlayerData/player file during purge process? + removeAntiXRayFile: false + # Do we need to remove permissions? + removePermissions: false Security: SQLProblem: # Stop the server if we can't contact the sql database - # Take care with this, if you set that to false, - # AuthMe automatically disable and the server is not protected! + # Take care with this, if you set this to false, + # AuthMe will automatically disable and the server won't be protected! stopServer: true ReloadCommand: # /reload support useReloadCommandSupport: true console: - # Replace passwords in the console when player type a command like /login + # Remove passwords from console? removePassword: true # Copy AuthMe log output in a separate file as well? logConsole: true @@ -344,99 +439,20 @@ Security: length: 8 # How many hours is a recovery code valid for? validForHours: 4 -Converter: - Rakamak: - # Rakamak file name - fileName: users.rak - # Rakamak use ip ? - useIP: false - # IP file name for rakamak - ipFileName: UsersIp.rak - CrazyLogin: - # CrazyLogin database file - fileName: accounts.db -Email: - # Email SMTP server host - mailSMTP: smtp.gmail.com - # Email SMTP server port - mailPort: 465 - # Email account that send the mail - mailAccount: '' - # Email account password - mailPassword: '' - # Custom SenderName, that replace the mailAccount name in the email - mailSenderName: '' - # Random password length - RecoveryPasswordLength: 8 - # Email subject of password get - mailSubject: 'Your new AuthMe password' - # Like maxRegPerIp but with email - maxRegPerEmail: 1 - # Recall players to add an email? - recallPlayers: false - # Delay in minute for the recall scheduler - delayRecall: 5 - # Blacklist these domains for emails - emailBlacklisted: - - 10minutemail.com - # WhiteList only these domains for emails - emailWhitelisted: [] - # Do we need to send new password draw in an image? - generateImage: false - # The email OAuth 2 token (leave empty if not used) - emailOauth2Token: '' -Hooks: - # Do we need to hook with multiverse for spawn checking? - multiverse: true - # Do we need to hook with BungeeCord ? - bungeecord: false - # Send player to this BungeeCord server after register/login - sendPlayerTo: '' - # Do we need to disable Essentials SocialSpy on join? - disableSocialSpy: true - # Do we need to force /motd Essentials command on join? - useEssentialsMotd: false -Purge: - # If enabled, AuthMe automatically purges old, unused accounts - useAutoPurge: false - # Number of Days an account become Unused - daysBeforeRemovePlayer: 60 - # Do we need to remove the player.dat file during purge process? - removePlayerDat: false - # Do we need to remove the Essentials/userdata/player.yml file during purge process? - removeEssentialsFile: false - # World where are players.dat stores - defaultWorld: 'world' - # Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ? - removeLimitedCreativesInventories: false - # Do we need to remove the AntiXRayData/PlayerData/player file during purge process? - removeAntiXRayFile: false - # Do we need to remove permissions? - removePermissions: false -Protection: - # Enable some servers protection ( country based login, antibot ) - enableProtection: false - # Apply the protection also to registered usernames - enableProtectionRegistered: true - # Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes - # PLEASE USE QUOTES! - countries: - - 'US' - - 'GB' - # Countries blacklisted automatically (without any needed to enable protection) - # PLEASE USE QUOTES! - countriesBlacklist: - - 'A1' - # Do we need to enable automatic antibot system? - enableAntiBot: true - # Max number of player allowed to login in 5 secs before enable AntiBot system automatically - antiBotSensibility: 10 - # Duration in minutes of the antibot automatic system - antiBotDuration: 10 - # Delay in seconds before the antibot activation - antiBotDelay: 60 -GroupOptions: - # Registered permission group - RegisteredPlayerGroup: '' - # Unregistered permission group - UnregisteredPlayerGroup: '' +BackupSystem: + # Enable or disable automatic backup + ActivateBackup: false + # Set backup at every start of server + OnServerStart: false + # Set backup at every stop of server + OnServerStop: true + # Windows only mysql installation Path + MysqlWindowsPath: 'C:\Program Files\MySQL\MySQL Server 5.1\' +``` + +To change settings on a running server, save your changes to config.yml and use +`/authme reload`. + +--- + +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 21:08:57 CEST 2016 diff --git a/docs/permission_nodes.md b/docs/permission_nodes.md index 61f61c3e1..fb6408206 100644 --- a/docs/permission_nodes.md +++ b/docs/permission_nodes.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. @@ -26,25 +26,27 @@ The following are the permission nodes that are currently supported by the lates - **authme.admin.spawn** – Administrator command to teleport to the AuthMe spawn. - **authme.admin.switchantibot** – Administrator command to toggle the AntiBot protection status. - **authme.admin.unregister** – Administrator command to unregister an existing user. +- **authme.admin.updatemessages** – Permission to use the update messages command. - **authme.allowmultipleaccounts** – Permission to be able to register multiple accounts. - **authme.bypassantibot** – Permission node to bypass AntiBot protection. - **authme.bypassforcesurvival** – Permission for users to bypass force-survival mode. -- **authme.bypasspurge** – Permission to bypass the purging process +- **authme.bypasspurge** – Permission to bypass the purging process. - **authme.player.*** – Permission to use all player (non-admin) commands. - **authme.player.canbeforced** – Permission for users a login can be forced to. - **authme.player.captcha** – Command permission to use captcha. - **authme.player.changepassword** – Command permission to change the password. +- **authme.player.email** – Grants all email permissions. - **authme.player.email.add** – Command permission to add an email address. - **authme.player.email.change** – Command permission to change the email address. -- **authme.player.email.recover** – Command permission to recover an account using it's email address. +- **authme.player.email.recover** – Command permission to recover an account using its email address. - **authme.player.login** – Command permission to login. - **authme.player.logout** – Command permission to logout. - **authme.player.register** – Command permission to register. - **authme.player.seeownaccounts** – Permission to use to see own other accounts. - **authme.player.unregister** – Command permission to unregister. -- **authme.vip** – Permission node to identify VIP users. +- **authme.vip** – When the server is full and someone with this permission joins the server, someone will be kicked. --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 02 10:47:16 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 15:38:58 CEST 2016 diff --git a/docs/translations.md b/docs/translations.md index c261bb8f6..c4288f152 100644 --- a/docs/translations.md +++ b/docs/translations.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code @@ -8,32 +8,32 @@ in your config.yml to use the language, or use another language code to start a Code | Language | Translated |   ---- | -------- | ---------: | ------ [en](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_en.yml) | English | 100% | bar -[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 73% | bar -[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 100% | bar -[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 91% | bar -[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 97% | bar -[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 100% | bar -[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 66% | bar -[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 70% | bar -[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 97% | bar -[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 74% | bar -[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 100% | bar -[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 74% | bar +[bg](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_bg.yml) | Bulgarian | 71% | bar +[br](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_br.yml) | Brazilian | 97% | bar +[cz](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_cz.yml) | Czech | 88% | bar +[de](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_de.yml) | German | 95% | bar +[es](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_es.yml) | Spanish | 97% | bar +[eu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_eu.yml) | Basque | 64% | bar +[fi](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fi.yml) | Finnish | 68% | bar +[fr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_fr.yml) | French | 95% | bar +[gl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_gl.yml) | Galician | 72% | bar +[hu](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_hu.yml) | Hungarian | 97% | bar +[id](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_id.yml) | Indonesian | 72% | bar [it](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_it.yml) | Italian | 100% | bar -[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 76% | bar -[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Latvian | 57% | bar -[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 80% | bar -[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 95% | bar -[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 91% | bar -[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 97% | bar -[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 50% | bar -[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 85% | bar -[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 97% | bar -[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 85% | bar -[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 85% | bar -[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 85% | bar -[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 85% | bar +[ko](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ko.yml) | Korean | 74% | bar +[lt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_lt.yml) | Latvian | 55% | bar +[nl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_nl.yml) | Dutch | 78% | bar +[pl](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pl.yml) | Polish | 92% | bar +[pt](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_pt.yml) | Portuguese | 88% | bar +[ru](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_ru.yml) | Russian | 95% | bar +[sk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_sk.yml) | Slovakian | 49% | bar +[tr](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_tr.yml) | Turkish | 83% | bar +[uk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_uk.yml) | Ukrainian | 95% | bar +[vn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_vn.yml) | Vietnamese | 83% | bar +[zhcn](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhcn.yml) | Chinese (China) | 83% | bar +[zhhk](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhhk.yml) | Chinese (Hong Kong) | 83% | bar +[zhtw](https://github.com/AuthMe/AuthMeReloaded/blob/master/src/main/resources/messages/messages_zhtw.yml) | Chinese (Taiwan) | 83% | bar --- -This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 09 09:42:48 CEST 2016 +This page was automatically generated on the [AuthMe/AuthMeReloaded repository](https://github.com/AuthMe/AuthMeReloaded/tree/master/docs/) on Sun Oct 23 18:25:14 CEST 2016 diff --git a/src/main/java/fr/xephi/authme/AuthMe.java b/src/main/java/fr/xephi/authme/AuthMe.java index 8e9818bf2..b52caf32e 100644 --- a/src/main/java/fr/xephi/authme/AuthMe.java +++ b/src/main/java/fr/xephi/authme/AuthMe.java @@ -199,6 +199,9 @@ public class AuthMe extends JavaPlugin { ConsoleLogger.setLogger(getLogger()); ConsoleLogger.setLogFile(new File(getDataFolder(), LOG_FILENAME)); + // Create plugin folder + getDataFolder().mkdir(); + // Load settings and set up the console and console filter settings = Initializer.createSettings(this); bukkitService = new BukkitService(this, settings); diff --git a/src/main/java/fr/xephi/authme/command/CommandDescription.java b/src/main/java/fr/xephi/authme/command/CommandDescription.java index 36ad6da6e..b6c006f9b 100644 --- a/src/main/java/fr/xephi/authme/command/CommandDescription.java +++ b/src/main/java/fr/xephi/authme/command/CommandDescription.java @@ -56,16 +56,10 @@ public class CommandDescription { private PermissionNode permission; /** - * Private constructor. Use {@link CommandDescription#builder()} to create instances of this class. + * Private constructor. *

- * Note for developers: Instances should be created with {@link CommandDescription#createInstance} to be properly + * Note for developers: Instances should be created with {@link CommandBuilder#register()} to be properly * registered in the command tree. - */ - private CommandDescription() { - } - - /** - * Create an instance. * * @param labels command labels * @param description description of the command @@ -74,30 +68,17 @@ public class CommandDescription { * @param parent parent command * @param arguments command arguments * @param permission permission node required to execute this command - * - * @return the created instance - * @see CommandDescription#builder() */ - private static CommandDescription createInstance(List labels, String description, - String detailedDescription, Class executableCommand, CommandDescription parent, - List arguments, PermissionNode permission) { - CommandDescription instance = new CommandDescription(); - instance.labels = labels; - instance.description = description; - instance.detailedDescription = detailedDescription; - instance.executableCommand = executableCommand; - instance.parent = parent; - instance.arguments = arguments; - instance.permission = permission; - - if (parent != null) { - parent.addChild(instance); - } - return instance; - } - - private void addChild(CommandDescription command) { - children.add(command); + private CommandDescription(List labels, String description, String detailedDescription, + Class executableCommand, CommandDescription parent, + List arguments, PermissionNode permission) { + this.labels = labels; + this.description = description; + this.detailedDescription = detailedDescription; + this.executableCommand = executableCommand; + this.parent = parent; + this.arguments = arguments; + this.permission = permission; } /** @@ -224,8 +205,21 @@ public class CommandDescription { private PermissionNode permission; /** - * Build a CommandDescription from the builder or throw an exception if a mandatory - * field has not been set. + * Build a CommandDescription and register it onto the parent if available. + * + * @return The generated CommandDescription object + */ + public CommandDescription register() { + CommandDescription command = build(); + + if (command.parent != null) { + command.parent.children.add(command); + } + return command; + } + + /** + * Build a CommandDescription (without registering it on the parent). * * @return The generated CommandDescription object */ @@ -236,8 +230,8 @@ public class CommandDescription { checkArgument(executableCommand != null, "Executable command must be set"); // parents and permissions may be null; arguments may be empty - return createInstance(labels, description, detailedDescription, executableCommand, - parent, arguments, permission); + return new CommandDescription(labels, description, detailedDescription, executableCommand, + parent, arguments, permission); } public CommandBuilder labels(List labels) { diff --git a/src/main/java/fr/xephi/authme/command/CommandInitializer.java b/src/main/java/fr/xephi/authme/command/CommandInitializer.java index a6d7b7674..7e01e0e30 100644 --- a/src/main/java/fr/xephi/authme/command/CommandInitializer.java +++ b/src/main/java/fr/xephi/authme/command/CommandInitializer.java @@ -1,6 +1,6 @@ package fr.xephi.authme.command; -import com.google.common.collect.ImmutableSet; +import com.google.common.collect.ImmutableList; import fr.xephi.authme.command.executable.HelpCommand; import fr.xephi.authme.command.executable.authme.AccountsCommand; import fr.xephi.authme.command.executable.authme.AuthMeCommand; @@ -11,6 +11,7 @@ import fr.xephi.authme.command.executable.authme.ForceLoginCommand; import fr.xephi.authme.command.executable.authme.GetEmailCommand; import fr.xephi.authme.command.executable.authme.GetIpCommand; import fr.xephi.authme.command.executable.authme.LastLoginCommand; +import fr.xephi.authme.command.executable.authme.MessagesCommand; import fr.xephi.authme.command.executable.authme.PurgeBannedPlayersCommand; import fr.xephi.authme.command.executable.authme.PurgeCommand; import fr.xephi.authme.command.executable.authme.PurgeLastPositionCommand; @@ -40,14 +41,13 @@ import fr.xephi.authme.permission.PlayerPermission; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Set; /** * Initializes all available AuthMe commands. */ public class CommandInitializer { - private Set commands; + private List commands; public CommandInitializer() { buildCommands(); @@ -58,7 +58,7 @@ public class CommandInitializer { * * @return the command descriptions */ - public Set getCommands() { + public List getCommands() { return commands; } @@ -66,10 +66,10 @@ public class CommandInitializer { // Register the base AuthMe Reloaded command final CommandDescription AUTHME_BASE = CommandDescription.builder() .labels("authme") - .description("Main command") + .description("AuthMe op commands") .detailedDescription("The main AuthMeReloaded command. The root for all admin commands.") .executableCommand(AuthMeCommand.class) - .build(); + .register(); // Register the register command CommandDescription.builder() @@ -81,7 +81,7 @@ public class CommandInitializer { .withArgument("password", "Password", false) .permission(AdminPermission.REGISTER) .executableCommand(RegisterAdminCommand.class) - .build(); + .register(); // Register the unregister command CommandDescription.builder() @@ -92,7 +92,7 @@ public class CommandInitializer { .withArgument("player", "Player name", false) .permission(AdminPermission.UNREGISTER) .executableCommand(UnregisterAdminCommand.class) - .build(); + .register(); // Register the forcelogin command CommandDescription.builder() @@ -103,7 +103,7 @@ public class CommandInitializer { .withArgument("player", "Online player name", true) .permission(AdminPermission.FORCE_LOGIN) .executableCommand(ForceLoginCommand.class) - .build(); + .register(); // Register the changepassword command CommandDescription.builder() @@ -115,7 +115,7 @@ public class CommandInitializer { .withArgument("pwd", "New password", false) .permission(AdminPermission.CHANGE_PASSWORD) .executableCommand(ChangePasswordAdminCommand.class) - .build(); + .register(); // Register the last login command CommandDescription.builder() @@ -126,7 +126,7 @@ public class CommandInitializer { .withArgument("player", "Player name", true) .permission(AdminPermission.LAST_LOGIN) .executableCommand(LastLoginCommand.class) - .build(); + .register(); // Register the accounts command CommandDescription.builder() @@ -137,7 +137,7 @@ public class CommandInitializer { .withArgument("player", "Player name or IP", true) .permission(AdminPermission.ACCOUNTS) .executableCommand(AccountsCommand.class) - .build(); + .register(); // Register the getemail command CommandDescription.builder() @@ -148,7 +148,7 @@ public class CommandInitializer { .withArgument("player", "Player name", true) .permission(AdminPermission.GET_EMAIL) .executableCommand(GetEmailCommand.class) - .build(); + .register(); // Register the setemail command CommandDescription.builder() @@ -160,7 +160,7 @@ public class CommandInitializer { .withArgument("email", "Player email", false) .permission(AdminPermission.CHANGE_EMAIL) .executableCommand(SetEmailCommand.class) - .build(); + .register(); // Register the getip command CommandDescription.builder() @@ -171,7 +171,7 @@ public class CommandInitializer { .withArgument("player", "Player name", false) .permission(AdminPermission.GET_IP) .executableCommand(GetIpCommand.class) - .build(); + .register(); // Register the spawn command CommandDescription.builder() @@ -181,7 +181,7 @@ public class CommandInitializer { .detailedDescription("Teleport to the spawn.") .permission(AdminPermission.SPAWN) .executableCommand(SpawnCommand.class) - .build(); + .register(); // Register the setspawn command CommandDescription.builder() @@ -191,7 +191,7 @@ public class CommandInitializer { .detailedDescription("Change the player's spawn to your current position.") .permission(AdminPermission.SET_SPAWN) .executableCommand(SetSpawnCommand.class) - .build(); + .register(); // Register the firstspawn command CommandDescription.builder() @@ -201,7 +201,7 @@ public class CommandInitializer { .detailedDescription("Teleport to the first spawn.") .permission(AdminPermission.FIRST_SPAWN) .executableCommand(FirstSpawnCommand.class) - .build(); + .register(); // Register the setfirstspawn command CommandDescription.builder() @@ -211,7 +211,7 @@ public class CommandInitializer { .detailedDescription("Change the first player's spawn to your current position.") .permission(AdminPermission.SET_FIRST_SPAWN) .executableCommand(SetFirstSpawnCommand.class) - .build(); + .register(); // Register the purge command CommandDescription.builder() @@ -223,7 +223,7 @@ public class CommandInitializer { .withArgument("all", "Add 'all' at the end to also purge players with lastlogin = 0", true) .permission(AdminPermission.PURGE) .executableCommand(PurgeCommand.class) - .build(); + .register(); // Register the purgelastposition command CommandDescription.builder() @@ -235,7 +235,7 @@ public class CommandInitializer { .withArgument("player/*", "Player name or * for all players", false) .permission(AdminPermission.PURGE_LAST_POSITION) .executableCommand(PurgeLastPositionCommand.class) - .build(); + .register(); // Register the purgebannedplayers command CommandDescription.builder() @@ -245,7 +245,7 @@ public class CommandInitializer { .detailedDescription("Purge all AuthMeReloaded data for banned players.") .permission(AdminPermission.PURGE_BANNED_PLAYERS) .executableCommand(PurgeBannedPlayersCommand.class) - .build(); + .register(); // Register the switchantibot command CommandDescription.builder() @@ -256,7 +256,7 @@ public class CommandInitializer { .withArgument("mode", "ON / OFF", true) .permission(AdminPermission.SWITCH_ANTIBOT) .executableCommand(SwitchAntiBotCommand.class) - .build(); + .register(); // Register the reload command CommandDescription.builder() @@ -266,7 +266,7 @@ public class CommandInitializer { .detailedDescription("Reload the AuthMeReloaded plugin.") .permission(AdminPermission.RELOAD) .executableCommand(ReloadCommand.class) - .build(); + .register(); // Register the version command CommandDescription.builder() @@ -276,7 +276,7 @@ public class CommandInitializer { .detailedDescription("Show detailed information about the installed AuthMeReloaded version, the " + "developers, contributors, and license.") .executableCommand(VersionCommand.class) - .build(); + .register(); CommandDescription.builder() .parent(AUTHME_BASE) @@ -287,7 +287,16 @@ public class CommandInitializer { "royalauth / vauth / sqliteToSql / mysqlToSqlite", false) .permission(AdminPermission.CONVERTER) .executableCommand(ConverterCommand.class) - .build(); + .register(); + + CommandDescription.builder() + .parent(AUTHME_BASE) + .labels("messages", "msg") + .description("Add missing messages") + .detailedDescription("Adds missing messages to the current messages file.") + .permission(AdminPermission.UPDATE_MESSAGES) + .executableCommand(MessagesCommand.class) + .register(); // Register the base login command final CommandDescription LOGIN_BASE = CommandDescription.builder() @@ -298,7 +307,7 @@ public class CommandInitializer { .withArgument("password", "Login password", false) .permission(PlayerPermission.LOGIN) .executableCommand(LoginCommand.class) - .build(); + .register(); // Register the base logout command CommandDescription LOGOUT_BASE = CommandDescription.builder() @@ -308,51 +317,51 @@ public class CommandInitializer { .detailedDescription("Command to logout using AuthMeReloaded.") .permission(PlayerPermission.LOGOUT) .executableCommand(LogoutCommand.class) - .build(); + .register(); // Register the base register command final CommandDescription REGISTER_BASE = CommandDescription.builder() .parent(null) .labels("register", "reg") - .description("Registration command") + .description("Register an account") .detailedDescription("Command to register using AuthMeReloaded.") .withArgument("password", "Password", true) .withArgument("verifyPassword", "Verify password", true) .permission(PlayerPermission.REGISTER) .executableCommand(RegisterCommand.class) - .build(); + .register(); // Register the base unregister command CommandDescription UNREGISTER_BASE = CommandDescription.builder() .parent(null) .labels("unregister", "unreg") - .description("Unregistration Command") + .description("Unregister an account") .detailedDescription("Command to unregister using AuthMeReloaded.") .withArgument("password", "Password", false) .permission(PlayerPermission.UNREGISTER) .executableCommand(UnregisterCommand.class) - .build(); + .register(); // Register the base changepassword command final CommandDescription CHANGE_PASSWORD_BASE = CommandDescription.builder() .parent(null) .labels("changepassword", "changepass", "cp") - .description("Change password Command") + .description("Change password of an account") .detailedDescription("Command to change your password using AuthMeReloaded.") - .withArgument("oldPassword", "Old Password", false) - .withArgument("newPassword", "New Password.", false) + .withArgument("oldPassword", "Old password", false) + .withArgument("newPassword", "New password", false) .permission(PlayerPermission.CHANGE_PASSWORD) .executableCommand(ChangePasswordCommand.class) - .build(); + .register(); // Register the base Email command CommandDescription EMAIL_BASE = CommandDescription.builder() .parent(null) .labels("email") - .description("Email command") - .detailedDescription("The AuthMeReloaded Email command base.") + .description("Add email or recover password") + .detailedDescription("The AuthMeReloaded email command base.") .executableCommand(EmailBaseCommand.class) - .build(); + .register(); // Register the show command CommandDescription.builder() @@ -361,7 +370,7 @@ public class CommandInitializer { .description("Show Email") .detailedDescription("Show your current email address.") .executableCommand(ShowEmailCommand.class) - .build(); + .register(); // Register the add command CommandDescription.builder() @@ -373,7 +382,7 @@ public class CommandInitializer { .withArgument("verifyEmail", "Email address verification", false) .permission(PlayerPermission.ADD_EMAIL) .executableCommand(AddEmailCommand.class) - .build(); + .register(); // Register the change command CommandDescription.builder() @@ -385,20 +394,20 @@ public class CommandInitializer { .withArgument("newEmail", "New email address", false) .permission(PlayerPermission.CHANGE_EMAIL) .executableCommand(ChangeEmailCommand.class) - .build(); + .register(); // Register the recover command CommandDescription.builder() .parent(EMAIL_BASE) .labels("recover", "recovery", "recoveremail", "recovermail") - .description("Recover password using Email") + .description("Recover password using email") .detailedDescription("Recover your account using an Email address by sending a mail containing " + "a new password.") .withArgument("email", "Email address", false) .withArgument("code", "Recovery code", true) .permission(PlayerPermission.RECOVER_EMAIL) .executableCommand(RecoverEmailCommand.class) - .build(); + .register(); // Register the base captcha command CommandDescription CAPTCHA_BASE = CommandDescription.builder() @@ -409,9 +418,9 @@ public class CommandInitializer { .withArgument("captcha", "The Captcha", false) .permission(PlayerPermission.CAPTCHA) .executableCommand(CaptchaCommand.class) - .build(); + .register(); - Set baseCommands = ImmutableSet.of( + List baseCommands = ImmutableList.of( AUTHME_BASE, LOGIN_BASE, LOGOUT_BASE, @@ -441,7 +450,7 @@ public class CommandInitializer { .detailedDescription("View detailed help for /" + base.getLabels().get(0) + " commands.") .withArgument("query", "The command or query to view help for.", true) .executableCommand(HelpCommand.class) - .build(); + .register(); } } } diff --git a/src/main/java/fr/xephi/authme/command/CommandMapper.java b/src/main/java/fr/xephi/authme/command/CommandMapper.java index 44d5b5833..e36664929 100644 --- a/src/main/java/fr/xephi/authme/command/CommandMapper.java +++ b/src/main/java/fr/xephi/authme/command/CommandMapper.java @@ -8,6 +8,7 @@ import org.bukkit.command.CommandSender; import javax.inject.Inject; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -26,7 +27,7 @@ public class CommandMapper { */ private static final Class HELP_COMMAND_CLASS = HelpCommand.class; - private final Set baseCommands; + private final Collection baseCommands; private final PermissionsManager permissionsManager; @Inject diff --git a/src/main/java/fr/xephi/authme/command/CommandUtils.java b/src/main/java/fr/xephi/authme/command/CommandUtils.java index 9ea1f5c55..699e4f37e 100644 --- a/src/main/java/fr/xephi/authme/command/CommandUtils.java +++ b/src/main/java/fr/xephi/authme/command/CommandUtils.java @@ -1,9 +1,11 @@ package fr.xephi.authme.command; import com.google.common.collect.Lists; +import org.bukkit.ChatColor; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; public final class CommandUtils { @@ -34,6 +36,14 @@ public final class CommandUtils { return sb.toString(); } + /** + * Constructs a hierarchical list of commands for the given command. The commands are in order: + * the parents of the given command precede the provided command. For example, given the command + * for {@code /authme register}, a list with {@code [{authme}, {authme register}]} is returned. + * + * @param command the command to build a parent list for + * @return the parent list + */ public static List constructParentList(CommandDescription command) { List commands = new ArrayList<>(); CommandDescription currentCommand = command; @@ -43,4 +53,35 @@ public final class CommandUtils { } return Lists.reverse(commands); } + + public static String buildSyntax(CommandDescription command) { + String arguments = command.getArguments().stream() + .map(arg -> formatArgument(arg)) + .collect(Collectors.joining(" ")); + return (constructCommandPath(command) + " " + arguments).trim(); + } + + public static String buildSyntax(CommandDescription command, List correctLabels) { + String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW; + for (int i = 1; i < correctLabels.size(); ++i) { + commandSyntax += " " + correctLabels.get(i); + } + for (CommandArgumentDescription argument : command.getArguments()) { + commandSyntax += " " + formatArgument(argument); + } + return commandSyntax; + } + + /** + * Format a command argument with the proper type of brackets. + * + * @param argument the argument to format + * @return the formatted argument + */ + public static String formatArgument(CommandArgumentDescription argument) { + if (argument.isOptional()) { + return "[" + argument.getName() + "]"; + } + return "<" + argument.getName() + ">"; + } } diff --git a/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java b/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java index 2076bcbce..0f7d90d20 100644 --- a/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java +++ b/src/main/java/fr/xephi/authme/command/executable/HelpCommand.java @@ -14,6 +14,11 @@ import java.util.List; import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND; import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL; +import static fr.xephi.authme.command.help.HelpProvider.ALL_OPTIONS; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION; public class HelpCommand implements ExecutableCommand { @@ -46,9 +51,9 @@ public class HelpCommand implements ExecutableCommand { int mappedCommandLevel = result.getCommandDescription().getLabelCount(); if (mappedCommandLevel == 1) { - helpProvider.outputHelp(sender, result, HelpProvider.SHOW_CHILDREN); + helpProvider.outputHelp(sender, result, SHOW_COMMAND | SHOW_DESCRIPTION | SHOW_CHILDREN | SHOW_ALTERNATIVES); } else { - helpProvider.outputHelp(sender, result, HelpProvider.ALL_OPTIONS); + helpProvider.outputHelp(sender, result, ALL_OPTIONS); } } diff --git a/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java b/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java new file mode 100644 index 000000000..a7d38d5ac --- /dev/null +++ b/src/main/java/fr/xephi/authme/command/executable/authme/MessagesCommand.java @@ -0,0 +1,54 @@ +package fr.xephi.authme.command.executable.authme; + +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.command.ExecutableCommand; +import fr.xephi.authme.initialization.DataFolder; +import fr.xephi.authme.message.Messages; +import fr.xephi.authme.service.MessageUpdater; +import fr.xephi.authme.settings.Settings; +import fr.xephi.authme.settings.properties.PluginSettings; +import org.bukkit.command.CommandSender; + +import javax.inject.Inject; +import java.io.File; +import java.util.List; + +/** + * Messages command, updates the user's messages file with any missing files + * from the provided file in the JAR. + */ +public class MessagesCommand implements ExecutableCommand { + + private static final String DEFAULT_LANGUAGE = "en"; + + @Inject + private Settings settings; + @Inject + @DataFolder + private File dataFolder; + @Inject + private Messages messages; + + @Override + public void executeCommand(CommandSender sender, List arguments) { + final String language = settings.getProperty(PluginSettings.MESSAGES_LANGUAGE); + + try { + boolean isFileUpdated = new MessageUpdater( + new File(dataFolder, getMessagePath(language)), + getMessagePath(language), + getMessagePath(DEFAULT_LANGUAGE)) + .executeCopy(sender); + if (isFileUpdated) { + messages.reload(); + } + } catch (Exception e) { + sender.sendMessage("Could not update messages: " + e.getMessage()); + ConsoleLogger.logException("Could not update messages:", e); + } + } + + private static String getMessagePath(String code) { + return "messages/messages_" + code + ".yml"; + } +} diff --git a/src/main/java/fr/xephi/authme/command/help/CommandSyntaxHelper.java b/src/main/java/fr/xephi/authme/command/help/CommandSyntaxHelper.java deleted file mode 100644 index 86ffb5427..000000000 --- a/src/main/java/fr/xephi/authme/command/help/CommandSyntaxHelper.java +++ /dev/null @@ -1,36 +0,0 @@ -package fr.xephi.authme.command.help; - -import fr.xephi.authme.command.CommandArgumentDescription; -import fr.xephi.authme.command.CommandDescription; -import org.bukkit.ChatColor; - -import java.util.List; - -/** - * Helper class for displaying the syntax of a command properly to a user. - */ -final class CommandSyntaxHelper { - - private CommandSyntaxHelper() { - } - - public static String getSyntax(CommandDescription command, List correctLabels) { - String commandSyntax = ChatColor.WHITE + "/" + correctLabels.get(0) + ChatColor.YELLOW; - for (int i = 1; i < correctLabels.size(); ++i) { - commandSyntax += " " + correctLabels.get(i); - } - for (CommandArgumentDescription argument : command.getArguments()) { - commandSyntax += " " + formatArgument(argument); - } - return commandSyntax; - } - - /** Format a command argument with the proper type of brackets. */ - private static String formatArgument(CommandArgumentDescription argument) { - if (argument.isOptional()) { - return "[" + argument.getName() + "]"; - } - return "<" + argument.getName() + ">"; - } - -} diff --git a/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java b/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java index b500e4310..c2cbaff99 100644 --- a/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java +++ b/src/main/java/fr/xephi/authme/command/help/HelpMessagesService.java @@ -67,6 +67,10 @@ public class HelpMessagesService implements Reloadable { return localCommand; } + public String getDescription(CommandDescription command) { + return getText(getCommandPath(command) + DESCRIPTION_SUFFIX, command::getDescription); + } + public String getMessage(HelpMessage message) { return messageFileHandler.getMessage(message.getKey()); } diff --git a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java index 8fc941617..23e87cab6 100644 --- a/src/main/java/fr/xephi/authme/command/help/HelpProvider.java +++ b/src/main/java/fr/xephi/authme/command/help/HelpProvider.java @@ -15,11 +15,12 @@ import org.bukkit.command.CommandSender; import javax.inject.Inject; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.function.Function; import static fr.xephi.authme.command.help.HelpSection.DETAILED_DESCRIPTION; import static fr.xephi.authme.command.help.HelpSection.SHORT_DESCRIPTION; -import static java.util.Arrays.asList; import static java.util.Collections.singletonList; /** @@ -79,7 +80,7 @@ public class HelpProvider implements Reloadable { if (hasFlag(SHOW_COMMAND, options)) { lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.COMMAND) + ": " - + CommandSyntaxHelper.getSyntax(command, correctLabels)); + + CommandUtils.buildSyntax(command, correctLabels)); } if (hasFlag(SHOW_DESCRIPTION, options)) { lines.add(ChatColor.GOLD + helpMessagesService.getMessage(SHORT_DESCRIPTION) + ": " @@ -171,19 +172,29 @@ public class HelpProvider implements Reloadable { } private void printAlternatives(CommandDescription command, List correctLabels, List lines) { - if (command.getLabels().size() <= 1 || correctLabels.size() <= 1) { + if (command.getLabels().size() <= 1) { return; } lines.add(ChatColor.GOLD + helpMessagesService.getMessage(HelpSection.ALTERNATIVES) + ":"); - // Get the label used - final String parentLabel = correctLabels.get(0); - final String childLabel = correctLabels.get(1); + + // Label with which the command was called -> don't show it as an alternative + final String usedLabel; + // Takes alternative label and constructs list of labels, e.g. "reg" -> [authme, reg] + final Function> commandLabelsFn; + + if (correctLabels.size() == 1) { + usedLabel = correctLabels.get(0); + commandLabelsFn = label -> singletonList(label); + } else { + usedLabel = correctLabels.get(1); + commandLabelsFn = label -> Arrays.asList(correctLabels.get(0), label); + } // Create a list of alternatives - for (String entry : command.getLabels()) { - if (!entry.equalsIgnoreCase(childLabel)) { - lines.add(" " + CommandSyntaxHelper.getSyntax(command, asList(parentLabel, entry))); + for (String label : command.getLabels()) { + if (!label.equalsIgnoreCase(usedLabel)) { + lines.add(" " + CommandUtils.buildSyntax(command, commandLabelsFn.apply(label))); } } } @@ -238,7 +249,7 @@ public class HelpProvider implements Reloadable { String parentCommandPath = String.join(" ", parentLabels); for (CommandDescription child : command.getChildren()) { lines.add(" /" + parentCommandPath + " " + child.getLabels().get(0) - + ChatColor.GRAY + ChatColor.ITALIC + ": " + child.getDescription()); + + ChatColor.GRAY + ChatColor.ITALIC + ": " + helpMessagesService.getDescription(child)); } } diff --git a/src/main/java/fr/xephi/authme/datasource/MySQL.java b/src/main/java/fr/xephi/authme/datasource/MySQL.java index 9c613ec35..9da225b94 100644 --- a/src/main/java/fr/xephi/authme/datasource/MySQL.java +++ b/src/main/java/fr/xephi/authme/datasource/MySQL.java @@ -40,6 +40,7 @@ public class MySQL implements DataSource { private String password; private String database; private String tableName; + private int poolSize; private List columnOthers; private Columns col; private HashAlgorithm hashAlgorithm; @@ -98,6 +99,10 @@ public class MySQL implements DataSource { this.phpBbPrefix = settings.getProperty(HooksSettings.PHPBB_TABLE_PREFIX); this.phpBbGroup = settings.getProperty(HooksSettings.PHPBB_ACTIVATED_GROUP_ID); this.wordpressPrefix = settings.getProperty(HooksSettings.WORDPRESS_TABLE_PREFIX); + this.poolSize = settings.getProperty(DatabaseSettings.MYSQL_POOL_SIZE); + if(poolSize == -1) { + poolSize = RuntimeUtils.getCoreCount(); + } } private void setConnectionArguments() throws RuntimeException { @@ -105,7 +110,7 @@ public class MySQL implements DataSource { ds.setPoolName("AuthMeMYSQLPool"); // Pool size - ds.setMaximumPoolSize(RuntimeUtils.getCoreCount()); + ds.setMaximumPoolSize(poolSize); // Database URL ds.setJdbcUrl("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database); diff --git a/src/main/java/fr/xephi/authme/initialization/Initializer.java b/src/main/java/fr/xephi/authme/initialization/Initializer.java index eaac2abc9..525334c27 100644 --- a/src/main/java/fr/xephi/authme/initialization/Initializer.java +++ b/src/main/java/fr/xephi/authme/initialization/Initializer.java @@ -62,13 +62,13 @@ public class Initializer { */ public static Settings createSettings(AuthMe authMe) throws Exception { File configFile = new File(authMe.getDataFolder(), "config.yml"); - if (FileUtils.copyFileFromResource(configFile, "config.yml")) { - PropertyResource resource = new YamlFileResource(configFile); - SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder()); - ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); - return new Settings(authMe.getDataFolder(), resource, migrationService, configurationData); + if(!configFile.exists()) { + configFile.createNewFile(); } - throw new Exception("Could not copy config.yml from JAR to plugin folder"); + PropertyResource resource = new YamlFileResource(configFile); + SettingsMigrationService migrationService = new SettingsMigrationService(authMe.getDataFolder()); + ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); + return new Settings(authMe.getDataFolder(), resource, migrationService, configurationData); } /** diff --git a/src/main/java/fr/xephi/authme/permission/AdminPermission.java b/src/main/java/fr/xephi/authme/permission/AdminPermission.java index ed5ab9f4c..46d58072a 100644 --- a/src/main/java/fr/xephi/authme/permission/AdminPermission.java +++ b/src/main/java/fr/xephi/authme/permission/AdminPermission.java @@ -8,126 +8,125 @@ public enum AdminPermission implements PermissionNode { /** * Administrator command to register a new user. */ - REGISTER("authme.admin.register", DefaultPermission.OP_ONLY), + REGISTER("authme.admin.register"), /** * Administrator command to unregister an existing user. */ - UNREGISTER("authme.admin.unregister", DefaultPermission.OP_ONLY), + UNREGISTER("authme.admin.unregister"), /** * Administrator command to force-login an existing user. */ - FORCE_LOGIN("authme.admin.forcelogin", DefaultPermission.OP_ONLY), + FORCE_LOGIN("authme.admin.forcelogin"), /** * Administrator command to change the password of a user. */ - CHANGE_PASSWORD("authme.admin.changepassword", DefaultPermission.OP_ONLY), + CHANGE_PASSWORD("authme.admin.changepassword"), /** * Administrator command to see the last login date and time of a user. */ - LAST_LOGIN("authme.admin.lastlogin", DefaultPermission.OP_ONLY), + LAST_LOGIN("authme.admin.lastlogin"), /** * Administrator command to see all accounts associated with a user. */ - ACCOUNTS("authme.admin.accounts", DefaultPermission.OP_ONLY), + ACCOUNTS("authme.admin.accounts"), /** * Administrator command to get the email address of a user, if set. */ - GET_EMAIL("authme.admin.getemail", DefaultPermission.OP_ONLY), + GET_EMAIL("authme.admin.getemail"), /** * Administrator command to set or change the email address of a user. */ - CHANGE_EMAIL("authme.admin.changemail", DefaultPermission.OP_ONLY), + CHANGE_EMAIL("authme.admin.changemail"), /** * Administrator command to get the last known IP of a user. */ - GET_IP("authme.admin.getip", DefaultPermission.OP_ONLY), + GET_IP("authme.admin.getip"), /** * Administrator command to teleport to the AuthMe spawn. */ - SPAWN("authme.admin.spawn", DefaultPermission.OP_ONLY), + SPAWN("authme.admin.spawn"), /** * Administrator command to set the AuthMe spawn. */ - SET_SPAWN("authme.admin.setspawn", DefaultPermission.OP_ONLY), + SET_SPAWN("authme.admin.setspawn"), /** * Administrator command to teleport to the first AuthMe spawn. */ - FIRST_SPAWN("authme.admin.firstspawn", DefaultPermission.OP_ONLY), + FIRST_SPAWN("authme.admin.firstspawn"), /** * Administrator command to set the first AuthMe spawn. */ - SET_FIRST_SPAWN("authme.admin.setfirstspawn", DefaultPermission.OP_ONLY), + SET_FIRST_SPAWN("authme.admin.setfirstspawn"), /** * Administrator command to purge old user data. */ - PURGE("authme.admin.purge", DefaultPermission.OP_ONLY), + PURGE("authme.admin.purge"), /** * Administrator command to purge the last position of a user. */ - PURGE_LAST_POSITION("authme.admin.purgelastpos", DefaultPermission.OP_ONLY), + PURGE_LAST_POSITION("authme.admin.purgelastpos"), /** * Administrator command to purge all data associated with banned players. */ - PURGE_BANNED_PLAYERS("authme.admin.purgebannedplayers", DefaultPermission.OP_ONLY), + PURGE_BANNED_PLAYERS("authme.admin.purgebannedplayers"), /** * Administrator command to toggle the AntiBot protection status. */ - SWITCH_ANTIBOT("authme.admin.switchantibot", DefaultPermission.OP_ONLY), + SWITCH_ANTIBOT("authme.admin.switchantibot"), /** * Administrator command to convert old or other data to AuthMe data. */ - CONVERTER("authme.admin.converter", DefaultPermission.OP_ONLY), + CONVERTER("authme.admin.converter"), /** * Administrator command to reload the plugin configuration. */ - RELOAD("authme.admin.reload", DefaultPermission.OP_ONLY), + RELOAD("authme.admin.reload"), /** * Permission to see Antibot messages. */ - ANTIBOT_MESSAGES("authme.admin.antibotmessages", DefaultPermission.OP_ONLY), + ANTIBOT_MESSAGES("authme.admin.antibotmessages"), + + /** + * Permission to use the update messages command. + */ + UPDATE_MESSAGES("authme.admin.updatemessages"), /** * Permission to see the other accounts of the players that log in. */ - SEE_OTHER_ACCOUNTS("authme.admin.seeotheraccounts", DefaultPermission.OP_ONLY); + SEE_OTHER_ACCOUNTS("authme.admin.seeotheraccounts"); /** * The permission node. */ private String node; - /** - * The default permission level - */ - private DefaultPermission defaultPermission; - /** * Constructor. * * @param node Permission node. */ - AdminPermission(String node, DefaultPermission defaultPermission) { + AdminPermission(String node) { this.node = node; - this.defaultPermission = defaultPermission; } @Override @@ -137,6 +136,6 @@ public enum AdminPermission implements PermissionNode { @Override public DefaultPermission getDefaultPermission() { - return defaultPermission; + return DefaultPermission.OP_ONLY; } } diff --git a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java index e2932e932..a42c9c9c8 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerPermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerPermission.java @@ -8,76 +8,70 @@ public enum PlayerPermission implements PermissionNode { /** * Command permission to login. */ - LOGIN("authme.player.login", DefaultPermission.ALLOWED), + LOGIN("authme.player.login"), /** * Command permission to logout. */ - LOGOUT("authme.player.logout", DefaultPermission.ALLOWED), + LOGOUT("authme.player.logout"), /** * Command permission to register. */ - REGISTER("authme.player.register", DefaultPermission.ALLOWED), + REGISTER("authme.player.register"), /** * Command permission to unregister. */ - UNREGISTER("authme.player.unregister", DefaultPermission.ALLOWED), + UNREGISTER("authme.player.unregister"), /** * Command permission to change the password. */ - CHANGE_PASSWORD("authme.player.changepassword", DefaultPermission.ALLOWED), + CHANGE_PASSWORD("authme.player.changepassword"), /** * Command permission to add an email address. */ - ADD_EMAIL("authme.player.email.add", DefaultPermission.ALLOWED), + ADD_EMAIL("authme.player.email.add"), /** * Command permission to change the email address. */ - CHANGE_EMAIL("authme.player.email.change", DefaultPermission.ALLOWED), + CHANGE_EMAIL("authme.player.email.change"), /** - * Command permission to recover an account using it's email address. + * Command permission to recover an account using its email address. */ - RECOVER_EMAIL("authme.player.email.recover", DefaultPermission.ALLOWED), + RECOVER_EMAIL("authme.player.email.recover"), /** * Command permission to use captcha. */ - CAPTCHA("authme.player.captcha", DefaultPermission.ALLOWED), + CAPTCHA("authme.player.captcha"), /** * Permission for users a login can be forced to. */ - CAN_LOGIN_BE_FORCED("authme.player.canbeforced", DefaultPermission.ALLOWED), + CAN_LOGIN_BE_FORCED("authme.player.canbeforced"), /** * Permission to use to see own other accounts. */ - SEE_OWN_ACCOUNTS("authme.player.seeownaccounts", DefaultPermission.ALLOWED); + SEE_OWN_ACCOUNTS("authme.player.seeownaccounts"); /** * The permission node. */ private String node; - /** - * The default permission level - */ - private DefaultPermission defaultPermission; - /** * Constructor. * * @param node Permission node. */ - PlayerPermission(String node, DefaultPermission defaultPermission) { + PlayerPermission(String node) { this.node = node; - this.defaultPermission = defaultPermission; } @Override @@ -87,7 +81,7 @@ public enum PlayerPermission implements PermissionNode { @Override public DefaultPermission getDefaultPermission() { - return defaultPermission; + return DefaultPermission.ALLOWED; } } diff --git a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java index d733814eb..aaeb0eea3 100644 --- a/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java +++ b/src/main/java/fr/xephi/authme/permission/PlayerStatePermission.java @@ -17,7 +17,7 @@ public enum PlayerStatePermission implements PermissionNode { BYPASS_FORCE_SURVIVAL("authme.bypassforcesurvival", DefaultPermission.OP_ONLY), /** - * Permission node to identify VIP users. + * When the server is full and someone with this permission joins the server, someone will be kicked. */ IS_VIP("authme.vip", DefaultPermission.OP_ONLY), @@ -27,7 +27,7 @@ public enum PlayerStatePermission implements PermissionNode { ALLOW_MULTIPLE_ACCOUNTS("authme.allowmultipleaccounts", DefaultPermission.OP_ONLY), /** - * Permission to bypass the purging process + * Permission to bypass the purging process. */ BYPASS_PURGE("authme.bypasspurge", DefaultPermission.NOT_ALLOWED); @@ -44,7 +44,8 @@ public enum PlayerStatePermission implements PermissionNode { /** * Constructor. * - * @param node Permission node. + * @param node Permission node + * @param defaultPermission The default permission */ PlayerStatePermission(String node, DefaultPermission defaultPermission) { this.node = node; diff --git a/src/main/java/fr/xephi/authme/service/MessageUpdater.java b/src/main/java/fr/xephi/authme/service/MessageUpdater.java new file mode 100644 index 000000000..129a47b81 --- /dev/null +++ b/src/main/java/fr/xephi/authme/service/MessageUpdater.java @@ -0,0 +1,139 @@ +package fr.xephi.authme.service; + +import com.github.authme.configme.SettingsManager; +import com.github.authme.configme.knownproperties.ConfigurationData; +import com.github.authme.configme.properties.Property; +import com.github.authme.configme.properties.StringProperty; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.ConsoleLogger; +import fr.xephi.authme.message.MessageKey; +import fr.xephi.authme.util.FileUtils; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Updates a user's messages file with messages from the JAR files. + */ +public class MessageUpdater { + + private final FileConfiguration userConfiguration; + private final FileConfiguration localJarConfiguration; + private final FileConfiguration defaultJarConfiguration; + + private final List> properties; + private final SettingsManager settingsManager; + private boolean hasMissingMessages = false; + + /** + * Constructor. + * + * @param userFile messages file in the data folder + * @param localJarFile path to messages file in JAR in local language + * @param defaultJarFile path to messages file in JAR for default language + * @throws Exception if userFile does not exist or no JAR messages file can be loaded + */ + public MessageUpdater(File userFile, String localJarFile, String defaultJarFile) throws Exception { + if (!userFile.exists()) { + throw new Exception("Local messages file does not exist"); + } + + userConfiguration = YamlConfiguration.loadConfiguration(userFile); + localJarConfiguration = loadJarFileOrSendError(localJarFile); + defaultJarConfiguration = localJarFile.equals(defaultJarFile) ? null : loadJarFileOrSendError(defaultJarFile); + if (localJarConfiguration == null && defaultJarConfiguration == null) { + throw new Exception("Could not load any JAR messages file to copy from"); + } + + properties = buildPropertyEntriesForMessageKeys(); + settingsManager = new SettingsManager( + new YamlFileResource(userFile), (r, p) -> true, new ConfigurationData((List) properties)); + } + + /** + * Copies missing messages to the messages file. + * + * @param sender sender starting the copy process + * @return true if the messages file was updated, false otherwise + * @throws Exception if an error occurs during saving + */ + public boolean executeCopy(CommandSender sender) throws Exception { + copyMissingMessages(); + + if (!hasMissingMessages) { + sender.sendMessage("No new messages to add"); + return false; + } + + // Save user configuration file + try { + settingsManager.save(); + sender.sendMessage("Message file updated with new messages"); + return true; + } catch (Exception e) { + throw new Exception("Could not save to messages file: " + StringUtils.formatException(e)); + } + } + + @SuppressWarnings("unchecked") + private void copyMissingMessages() { + for (Property property : properties) { + String message = userConfiguration.getString(property.getPath()); + if (message == null) { + hasMissingMessages = true; + message = getMessageFromJar(property.getPath()); + } + settingsManager.setProperty(property, message); + } + } + + private String getMessageFromJar(String key) { + String message = (localJarConfiguration == null ? null : localJarConfiguration.getString(key)); + if (message != null) { + return message; + } + return (defaultJarConfiguration == null) ? null : defaultJarConfiguration.getString(key); + } + + private static FileConfiguration loadJarFileOrSendError(String jarPath) { + try (InputStream stream = FileUtils.getResourceFromJar(jarPath)) { + if (stream == null) { + ConsoleLogger.info("Could not load '" + jarPath + "' from JAR"); + return null; + } + InputStreamReader isr = new InputStreamReader(stream); + FileConfiguration configuration = YamlConfiguration.loadConfiguration(isr); + close(isr); + return configuration; + } catch (IOException e) { + ConsoleLogger.logException("Exception while handling JAR path '" + jarPath + "'", e); + } + return null; + } + + private static List> buildPropertyEntriesForMessageKeys() { + return Arrays.stream(MessageKey.values()) + .map(key -> new StringProperty(key.getKey(), "")) + .collect(Collectors.toList()); + } + + private static void close(Closeable closeable) { + if (closeable != null) { + try { + closeable.close(); + } catch (IOException e) { + ConsoleLogger.info("Cannot close '" + closeable + "': " + StringUtils.formatException(e)); + } + } + } +} diff --git a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java index 2dc769ec6..c82a7a81d 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/DatabaseSettings.java @@ -98,6 +98,10 @@ public class DatabaseSettings implements SettingsHolder { public static final Property MYSQL_COL_GROUP = newProperty("ExternalBoardOptions.mySQLColumnGroup", ""); + @Comment("Overrides the size of the DB Connection Pool, -1 = Auto") + public static final Property MYSQL_POOL_SIZE = + newProperty("DataSource.poolSize", -1); + private DatabaseSettings() { } diff --git a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java index bfb565337..094a3d5b9 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PluginSettings.java @@ -36,7 +36,10 @@ public class PluginSettings implements SettingsHolder { public static final Property SESSIONS_EXPIRE_ON_IP_CHANGE = newProperty("settings.sessions.sessionExpireOnIpChange", true); - @Comment("Message language, available: en, de, br, cz, pl, fr, ru, hu, sk, es, zhtw, fi, zhcn, lt, it, ko, pt") + @Comment({ + "Message language, available languages:", + "https://github.com/AuthMe/AuthMeReloaded/blob/master/docs/translations.md" + }) public static final Property MESSAGES_LANGUAGE = newProperty("settings.messagesLanguage", "en"); @@ -65,8 +68,8 @@ public class PluginSettings implements SettingsHolder { newProperty(LogLevel.class, "settings.logLevel", LogLevel.FINE); @Comment({ - "By default we schedule async tasks when talking to the database", - "If you want typical communication with the database to happen synchronously, set this to false" + "By default we schedule async tasks when talking to the database. If you want", + "typical communication with the database to happen synchronously, set this to false" }) public static final Property USE_ASYNC_TASKS = newProperty("settings.useAsyncTasks", true); diff --git a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java index cdd760bd2..b725172f0 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/ProtectionSettings.java @@ -20,13 +20,16 @@ public class ProtectionSettings implements SettingsHolder { public static final Property ENABLE_PROTECTION_REGISTERED = newProperty("Protection.enableProtectionRegistered", true); - @Comment({"Countries allowed to join the server and register, see http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/ for countries' codes", - "PLEASE USE QUOTES!"}) + @Comment({ + "Countries allowed to join the server and register. For country codes, see", + "http://dev.bukkit.org/bukkit-plugins/authme-reloaded/pages/countries-codes/", + "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_WHITELIST = newListProperty("Protection.countries", "US", "GB"); - @Comment({"Countries not allowed to join the server and register", - "PLEASE USE QUOTES!"}) + @Comment({ + "Countries not allowed to join the server and register", + "PLEASE USE QUOTES!"}) public static final Property> COUNTRIES_BLACKLIST = newListProperty("Protection.countriesBlacklist", "A1"); @@ -34,7 +37,13 @@ public class ProtectionSettings implements SettingsHolder { public static final Property ENABLE_ANTIBOT = newProperty("Protection.enableAntiBot", true); - @Comment("Max number of players allowed to login in 5 secs before the AntiBot system is enabled automatically") + @Comment("The interval in seconds") + public static final Property ANTIBOT_INTERVAL = + newProperty("Protection.antiBotInterval", 5); + + @Comment({ + "Max number of players allowed to login in the interval", + "before the AntiBot system is enabled automatically"}) public static final Property ANTIBOT_SENSIBILITY = newProperty("Protection.antiBotSensibility", 10); diff --git a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java index f23a3eb12..b3733b5ac 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/PurgeSettings.java @@ -12,7 +12,7 @@ public class PurgeSettings implements SettingsHolder { public static final Property USE_AUTO_PURGE = newProperty("Purge.useAutoPurge", false); - @Comment("Number of Days an account become Unused") + @Comment("Number of days after which an account should be purged") public static final Property DAYS_BEFORE_REMOVE_PLAYER = newProperty("Purge.daysBeforeRemovePlayer", 60); @@ -28,7 +28,7 @@ public class PurgeSettings implements SettingsHolder { public static final Property DEFAULT_WORLD = newProperty("Purge.defaultWorld", "world"); - @Comment("Do we need to remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge process ?") + @Comment("Remove LimitedCreative/inventories/player.yml, player_creative.yml files during purge?") public static final Property REMOVE_LIMITED_CREATIVE_INVENTORIES = newProperty("Purge.removeLimitedCreativesInventories", false); diff --git a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java index b593869a3..487039ba7 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RegistrationSettings.java @@ -52,8 +52,9 @@ public class RegistrationSettings implements SettingsHolder { public static final Property> FORCE_COMMANDS = newListProperty("settings.forceCommands"); - @Comment("Force these commands after /login as service console, without any '/'. " - + "Use %p to replace with player name") + @Comment({ + "Force these commands after /login as service console, without any '/'.", + "Use %p to replace with player name"}) public static final Property> FORCE_COMMANDS_AS_CONSOLE = newListProperty("settings.forceCommandsAsConsole"); @@ -61,22 +62,25 @@ public class RegistrationSettings implements SettingsHolder { public static final Property> FORCE_REGISTER_COMMANDS = newListProperty("settings.forceRegisterCommands"); - @Comment("Force these commands after /register as a server console, without any '/'. " - + "Use %p to replace with player name") + @Comment({ + "Force these commands after /register as a server console, without any '/'.", + "Use %p to replace with player name"}) public static final Property> FORCE_REGISTER_COMMANDS_AS_CONSOLE = newListProperty("settings.forceRegisterCommandsAsConsole"); @Comment({ "Enable to display the welcome message (welcome.txt) after a login", "You can use colors in this welcome.txt + some replaced strings:", - "{PLAYER}: player name, {ONLINE}: display number of online players, {MAXPLAYERS}: display server slots,", - "{IP}: player ip, {LOGINS}: number of players logged, {WORLD}: player current world, {SERVER}: server name", + "{PLAYER}: player name, {ONLINE}: display number of online players,", + "{MAXPLAYERS}: display server slots, {IP}: player ip, {LOGINS}: number of players logged,", + "{WORLD}: player current world, {SERVER}: server name", "{VERSION}: get current bukkit version, {COUNTRY}: player country"}) public static final Property USE_WELCOME_MESSAGE = newProperty("settings.useWelcomeMessage", true); - @Comment("Do we need to broadcast the welcome message to all server or only to the player? set true for " - + "server or false for player") + @Comment({ + "Broadcast the welcome message to the server or only to the player?", + "set true for server or false for player"}) public static final Property BROADCAST_WELCOME_MESSAGE = newProperty("settings.broadcastWelcomeMessage", false); diff --git a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java index 9e16714dd..c05efa4b5 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/RestrictionSettings.java @@ -36,7 +36,7 @@ public class RestrictionSettings implements SettingsHolder { @Comment("Minimum allowed username length") public static final Property MIN_NICKNAME_LENGTH = - newProperty("settings.restrictions.minNicknameLength", 4); + newProperty("settings.restrictions.minNicknameLength", 3); @Comment("Maximum allowed username length") public static final Property MAX_NICKNAME_LENGTH = @@ -50,9 +50,9 @@ public class RestrictionSettings implements SettingsHolder { newProperty("settings.restrictions.ForceSingleSession", true); @Comment({ - "If enabled, every player that spawn in one of the world listed in \"ForceSpawnLocOnJoin.worlds\"", - "will be teleported to the spawnpoint after successful authentication.", - "The quit location of the player will be overwritten.", + "If enabled, every player that spawn in one of the world listed in", + "\"ForceSpawnLocOnJoin.worlds\" will be teleported to the spawnpoint after successful", + "authentication. The quit location of the player will be overwritten.", "This is different from \"teleportUnAuthedToSpawn\" that teleport player", "to the spawnpoint on join."}) public static final Property FORCE_SPAWN_LOCATION_AFTER_LOGIN = diff --git a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java index 7d91f8183..92ce2e5ec 100644 --- a/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java +++ b/src/main/java/fr/xephi/authme/settings/properties/SecuritySettings.java @@ -18,10 +18,6 @@ public class SecuritySettings implements SettingsHolder { public static final Property STOP_SERVER_ON_PROBLEM = newProperty("Security.SQLProblem.stopServer", true); - @Comment("/reload support") - public static final Property USE_RELOAD_COMMAND_SUPPORT = - newProperty("Security.ReloadCommand.useReloadCommandSupport", true); - @Comment("Remove passwords from console?") public static final Property REMOVE_PASSWORD_FROM_CONSOLE = newProperty("Security.console.removePassword", true); @@ -85,11 +81,13 @@ public class SecuritySettings implements SettingsHolder { newProperty("settings.security.supportOldPasswordHash", false); @Comment({"Prevent unsafe passwords from being used; put them in lowercase!", + "You should always set 'help' as unsafePassword due to possible conflicts.", "unsafePasswords:", "- '123456'", - "- 'password'"}) + "- 'password'", + "- 'help'"}) public static final Property> UNSAFE_PASSWORDS = - newLowercaseListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321", "123456789"); + newLowercaseListProperty("settings.security.unsafePasswords", "123456", "password", "qwerty", "12345", "54321", "123456789", "help"); @Comment("Tempban a user's IP address if they enter the wrong password too many times") public static final Property TEMPBAN_ON_MAX_LOGINS = diff --git a/src/main/java/fr/xephi/authme/util/FileUtils.java b/src/main/java/fr/xephi/authme/util/FileUtils.java index 831beafd6..86c8cae46 100644 --- a/src/main/java/fr/xephi/authme/util/FileUtils.java +++ b/src/main/java/fr/xephi/authme/util/FileUtils.java @@ -23,7 +23,7 @@ public final class FileUtils { * Copy a resource file (from the JAR) to the given file if it doesn't exist. * * @param destinationFile The file to check and copy to (outside of JAR) - * @param resourcePath Absolute path to the resource file (path to file within JAR) + * @param resourcePath Local path to the resource file (path to file within JAR) * * @return False if the file does not exist and could not be copied, true otherwise */ @@ -35,9 +35,7 @@ public final class FileUtils { return false; } - // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' - final String normalizedPath = resourcePath.replace("\\", "/"); - try (InputStream is = AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath)) { + try (InputStream is = getResourceFromJar(resourcePath)) { if (is == null) { ConsoleLogger.warning(format("Cannot copy resource '%s' to file '%s': cannot load resource", resourcePath, destinationFile.getPath())); @@ -52,6 +50,18 @@ public final class FileUtils { return false; } + /** + * Returns a JAR file as stream. Returns null if it doesn't exist. + * + * @param path the local path (starting from resources project, e.g. "config.yml" for 'resources/config.yml') + * @return the stream if the file exists, or false otherwise + */ + public static InputStream getResourceFromJar(String path) { + // ClassLoader#getResourceAsStream does not deal with the '\' path separator: replace to '/' + final String normalizedPath = path.replace("\\", "/"); + return AuthMe.class.getClassLoader().getResourceAsStream(normalizedPath); + } + /** * Delete a given directory and all its content. * diff --git a/src/main/resources/messages/help_br.yml b/src/main/resources/messages/help_br.yml new file mode 100644 index 000000000..90fc6cc04 --- /dev/null +++ b/src/main/resources/messages/help_br.yml @@ -0,0 +1,44 @@ +# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called + +# ------------------------------------------------------- +# List of texts used in the help section +common: + optional: 'Opcional' + hasPermission: 'Você tem permissão' + noPermission: 'Sem Permissão' + default: 'Default' + result: 'Resultado' + defaultPermissions: + notAllowed: 'Sem Permissão' + opOnly: 'OP''s only' + allowed: 'Todos podem' + +# ------------------------------------------------------- +# Titles of the individual help sections +# Set the translation text to empty text to disable the section, e.g. to hide alternatives: +# alternatives: '' +section: + command: 'Comando' + description: 'Pequena descrição' + detailedDescription: 'Descrição detalhada' + arguments: 'Argumentos' + permissions: 'Permissões' + alternatives: 'Alternativas' + children: 'Comandos' + +# ------------------------------------------------------- +# You can translate the data for all commands using the below pattern. +# For example to translate /authme reload, create a section "authme.reload", or "login" for /login +# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth +# Translations don't need to be complete; any missing section will be taken from the default silently +# Important: Put main commands like "authme" before their children (e.g. "authme.reload") +commands: + authme.register: + description: 'Registra um jogador' + detailedDescription: 'Registra um esprecifico jogador com uma senha especifica.' + arg1: + label: 'player' + description: 'Nome do player' + arg2: + label: 'password' + description: 'Senha' diff --git a/src/main/resources/messages/help_it.yml b/src/main/resources/messages/help_it.yml new file mode 100644 index 000000000..b69f85153 --- /dev/null +++ b/src/main/resources/messages/help_it.yml @@ -0,0 +1,154 @@ +# Lingua Italiana creata da Maxetto e sgdc3. +# Translation config for the AuthMe help, e.g. when /authme help or /authme help register is called + +# ------------------------------------------------------- +# List of texts used in the help section +common: + header: '==========[ Assistenza AuthMeReloaded ]==========' + optional: 'Opzionale' + hasPermission: 'Hai il permesso' + noPermission: 'Non hai il permesso' + default: 'Configurazione base' + result: 'Risultato' + defaultPermissions: + notAllowed: 'Nessuno autorizzato' + opOnly: 'Solo per OP' + allowed: 'Tutti autorizzati' + +# ------------------------------------------------------- +# Titles of the individual help sections +# Set the translation text to empty text to disable the section, e.g. to hide alternatives: +# alternatives: '' +section: + command: 'Comando' + description: 'Descrizione breve' + detailedDescription: 'Descrizione dettagliata' + arguments: 'Parametri' + permissions: 'Permessi' + alternatives: 'Alternative' + children: 'Comandi' + +# ------------------------------------------------------- +# You can translate the data for all commands using the below pattern. +# For example to translate /authme reload, create a section "authme.reload", or "login" for /login +# If the command has arguments, you can use arg1 as below to translate the first argument, and so forth +# Translations don't need to be complete; any missing section will be taken from the default silently +# Important: Put main commands like "authme" before their children (e.g. "authme.reload") +commands: + authme.register: + description: 'Registra un giocatore' + detailedDescription: 'Registra il giocatore indicato con la password inserita.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore' + arg2: + label: 'password' + description: 'Password' + authme.unregister: + description: 'Rimuovi un giocatore' + detailedDescription: 'Rimuovi il giocatore indicato dal Database.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore' + authme.forcelogin: + description: 'Forza l''autenticazione ad un giocatore' + detailedDescription: 'Autentica il giocatore indicato.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore connesso' + authme.password: + description: 'Cambia la password di un giocatore' + detailedDescription: 'Cambia la password del giocatore indicato.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore' + arg2: + label: 'password' + description: 'Nuova Password' + authme.lastlogin: + description: 'Ultima autenticazione di un giocatore' + detailedDescription: 'Visualizza l''ultima data di autenticazione del giocatore indicato.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore' + authme.accounts: + description: 'Mostra i profili di un giocatore' + detailedDescription: 'Mostra tutti i profili di un giocatore attraverso il nome o l''indirizzo IP.' + arg1: + label: 'giocatore' + description: 'Nome o indirizzo IP del giocatore' + authme.email: + description: 'Mostra l''indirizzo email di un giocatore' + detailedDescription: 'Mostra l''indirizzo email del giocatore indicato se impostato.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore' + authme.setemail: + description: 'Cambia l''indirizzo email di un giocatore' + detailedDescription: 'Cambia l''indirizzo email del giocatore indicato.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore' + arg2: + label: 'email' + description: 'Indirizzo email del giocatore' + authme.getip: + description: 'Mostra l''indirizzo IP di un giocatore' + detailedDescription: 'Mostra l''indirizzo IP del giocatore indicato.' + arg1: + label: 'giocatore' + description: 'Nome del giocatore connesso' + authme.spawn: + description: 'Teletrasportati al punto di rigenerazione' + detailedDescription: 'Teletrasportati al punto di rigenerazione.' + authme.setspawn: + description: 'Cambia il punto di rigenerazione' + detailedDescription: 'Cambia il punto di rigenerazione dei giocatori alla tua posizione.' + authme.firstspawn: + description: 'Teletrasportati al punto di rigenerazione iniziale' + detailedDescription: 'Teletrasportati al punto di rigenerazione iniziale.' + authme.setfirstspawn: + description: 'Cambia il punto di rigenerazione iniziale' + detailedDescription: 'Cambia il punto di rigenerazione iniziale dei giocatori alla tua posizione.' + authme.purge: + description: 'Elimina i vecchi dati' + detailedDescription: 'Elimina i dati di AuthMeReloaded più vecchi dei giorni indicati.' + arg1: + label: 'giorni' + description: 'Numero di giorni' + arg2: + label: 'all' + description: 'Aggiungi ''all'' alla fine per eliminare anche i giocatori con l''ultima autenticazione pari a 0' + authme.resetpos: + description: 'Elimina l''ultima posizione di un giocatore' + detailedDescription: 'Elimina l''ultima posizione conosciuta del giocatore indicato o di tutti i giocatori.' + arg1: + label: 'giocatore/*' + description: 'Nome del giocatore o ''*'' per tutti i giocatori' + authme.purgebannedplayers: + description: 'Elimina i dati dei giocatori banditi' + detailedDescription: 'Elimina tutti i dati di AuthMeReloaded dei giocatori banditi.' + authme.switchantibot: + description: 'Cambia lo stato del servizio di AntiBot' + detailedDescription: 'Cambia lo stato del servizio di AntiBot allo stato indicato.' + arg1: + label: 'stato' + description: 'ON / OFF' + authme.reload: + description: 'Ricarica il plugin' + detailedDescription: 'Ricarica il plugin AuthMeReloaded.' + authme.version: + description: 'Informazioni sulla versione' + detailedDescription: 'Mostra informazioni dettagliate riguardo la versione di AuthMeReloaded in uso, gli sviluppatori, i collaboratori e la licenza.' + authme.converter: + description: 'Comando per il convertitore' + detailedDescription: 'Comando per il convertitore di AuthMeReloaded.' + arg1: + label: 'incarico' + description: 'Incarico di conversione: xauth / crazylogin / rakamak / royalauth / vauth / sqliteToSql / mysqlToSqlite' + authme.help: + description: 'Visualizza l''assistenza' + detailedDescription: 'Visualizza informazioni dettagliate per i comandi ''/authme''.' + arg1: + label: 'comando' + description: 'Il comando di cui vuoi ricevere assistenza' diff --git a/src/main/resources/messages/messages_br.yml b/src/main/resources/messages/messages_br.yml index 8002a7a1a..8181658cd 100644 --- a/src/main/resources/messages/messages_br.yml +++ b/src/main/resources/messages/messages_br.yml @@ -61,6 +61,8 @@ email_confirm: '&cPor favor confirme seu endereço de email!' email_changed: '&2Troca de email com sucesso.!' email_send: '&2Recuperação de email enviada com sucesso! Por favor, verifique sua caixa de entrada de e-mail!' email_exists: '&cUm e-mail de recuperação já foi enviado! Você pode descartá-lo e enviar um novo usando o comando abaixo:' +email_show: '&2O seu endereço de e-mail atual é: &f%email' +show_no_email: '&2Você atualmente não têm endereço de e-mail associado a esta conta.' country_banned: '&4O seu país está banido neste servidor!' antibot_auto_enabled: '&4[AntiBotService] habilitado devido ao enorme número de conexões!' antibot_auto_disabled: '&2[AntiBotService] AntiBot desativada após %m minutos!' @@ -75,5 +77,3 @@ kicked_admin_registered: 'Um administrador registrou você; por favor faça logi incomplete_email_settings: 'Erro: Nem todas as configurações necessárias estão definidas para o envio de e-mails. Entre em contato com um administrador.' recovery_code_sent: 'Um código de recuperação para redefinir sua senha foi enviada para o seu e-mail.' recovery_code_incorrect: 'O código de recuperação esta incorreto! Use /email recovery [email] para gerar um novo!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' \ No newline at end of file diff --git a/src/main/resources/messages/messages_hu.yml b/src/main/resources/messages/messages_hu.yml index 88142bbce..daabe5734 100644 --- a/src/main/resources/messages/messages_hu.yml +++ b/src/main/resources/messages/messages_hu.yml @@ -72,5 +72,5 @@ kicked_admin_registered: 'Adminisztrátor által regisztrálva lettél; kérlek accounts_owned_self: '%count db regisztrációd van:' recovery_code_incorrect: 'A visszaállító kód helytelen volt! Használd a következő parancsot: /email recovery [email címed] egy új generálásához' recovery_code_sent: 'A jelszavad visszaállításához szükséges kódot sikeresen kiküldtük az email címedre!' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' \ No newline at end of file +email_show: '&2A jelenlegi email-ed a következő: &f%email' +show_no_email: '&2Ehhez a felhasználóhoz jelenleg még nincs email hozzárendelve.' diff --git a/src/main/resources/messages/messages_it.yml b/src/main/resources/messages/messages_it.yml index 3c0aaed2a..d257c7465 100644 --- a/src/main/resources/messages/messages_it.yml +++ b/src/main/resources/messages/messages_it.yml @@ -69,9 +69,9 @@ invalid_name_case: 'Dovresti entrare con questo nome utente: "%valid", al posto tempban_max_logins: '&cSei stato temporaneamente bandito per aver fallito l''autenticazione troppe volte.' accounts_owned_self: 'Possiedi %count account:' accounts_owned_other: 'Il giocatore %name possiede %count account:' -kicked_admin_registered: 'Un amministratore ti ha appena registrato; per favore rientra nel server' +kicked_admin_registered: 'Un amministratore ti ha appena registrato, per favore rientra nel server' incomplete_email_settings: 'Errore: non tutte le impostazioni richieste per inviare le email sono state impostate. Per favore contatta un amministratore.' recovery_code_incorrect: 'Il codice di recupero inserito non è corretto! Scrivi /email recovery per generarne uno nuovo' recovery_code_sent: 'Una email contenente il codice di recupero per reimpostare la tua password è stata appena inviata al tuo indirizzo email.' -# TODO email_show: '&2Your current email address is: &f%email' -# TODO show_no_email: '&2You currently don''t have email address associated with this account.' \ No newline at end of file +email_show: '&2Il tuo indirizzo email al momento è: &f%email' +show_no_email: '&2Al momento non hai nessun indirizzo email associato al tuo account.' diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2b950e3a7..9ca68c314 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -15,186 +15,197 @@ softdepend: - EssentialsSpawn - ProtocolLib commands: - authme: - description: AuthMe op commands - usage: '/authme reload|register playername password|changepassword playername password|unregister playername|version|converter' - register: - description: Register an account - usage: /register - aliases: [reg] - login: - description: Login command - usage: /login - aliases: [l,log] - changepassword: - description: Change password of an account - usage: /changepassword - aliases: [cp,changepass] - logout: - description: Logout - usage: /logout - unregister: - description: Unregister your account - usage: /unregister - aliases: [unreg] - email: - description: Add Email or recover password - usage: '/email add your@email.com your@email.com|change oldEmail newEmail|recovery your@email.com' - captcha: - description: Captcha command - usage: /captcha + authme: + description: AuthMe op commands + usage: /authme register|unregister|forcelogin|password|lastlogin|accounts|email|setemail|getip|spawn|setspawn|firstspawn|setfirstspawn|purge|resetpos|purgebannedplayers|switchantibot|reload|version|converter|messages + login: + description: Login command + usage: /login + aliases: + - l + - log + logout: + description: Logout command + usage: /logout + register: + description: Register an account + usage: /register [password] [verifyPassword] + aliases: + - reg + unregister: + description: Unregister an account + usage: /unregister + aliases: + - unreg + changepassword: + description: Change password of an account + usage: /changepassword + aliases: + - changepass + - cp + email: + description: Add email or recover password + usage: /email show|add|change|recover + captcha: + description: Captcha Command + usage: /captcha permissions: - authme.admin.*: - description: Give access to all admin commands. - children: - authme.admin.accounts: true - authme.admin.changemail: true - authme.admin.changepassword: true - authme.admin.converter: true - authme.admin.firstspawn: true - authme.admin.forcelogin: true - authme.admin.getemail: true - authme.admin.getip: true - authme.admin.lastlogin: true - authme.admin.purge: true - authme.admin.purgebannedplayers: true - authme.admin.purgelastpos: true - authme.admin.register: true - authme.admin.reload: true - authme.admin.setfirstspawn: true - authme.admin.setspawn: true - authme.admin.spawn: true - authme.admin.switchantibot: true - authme.admin.unregister: true - authme.admin.register: - description: Administrator command to register a new user. - default: op - authme.admin.unregister: - description: Administrator command to unregister an existing user. - default: op - authme.admin.forcelogin: - description: Administrator command to force-login an existing user. - default: op - authme.admin.changepassword: - description: Administrator command to change the password of a user. - default: op - authme.admin.lastlogin: - description: Administrator command to see the last login date and time of a user. - default: op - authme.admin.accounts: - description: Administrator command to see all accounts associated with a user. - default: op - authme.admin.getemail: - description: Administrator command to get the email address of a user, if set. - default: op - authme.admin.changemail: - description: Administrator command to set or change the email address of a user. - default: op - authme.admin.getip: - description: Administrator command to get the last known IP of a user. - default: op - authme.admin.spawn: - description: Administrator command to teleport to the AuthMe spawn. - default: op - authme.admin.setspawn: - description: Administrator command to set the AuthMe spawn. - default: op - authme.admin.firstspawn: - description: Administrator command to teleport to the first AuthMe spawn. - default: op - authme.admin.setfirstspawn: - description: Administrator command to set the first AuthMe spawn. - default: op - authme.admin.purge: - description: Administrator command to purge old user data. - default: op - authme.admin.purgelastpos: - description: Administrator command to purge the last position of a user. - default: op - authme.admin.purgebannedplayers: - description: Administrator command to purge all data associated with banned players. - default: op - authme.admin.seeotheraccounts: - description: Permission for user to see other accounts. - default: op - authme.admin.switchantibot: - description: Administrator command to toggle the AntiBot protection status. - default: op - authme.admin.converter: - description: Administrator command to convert old or other data to AuthMe data. - default: op - authme.admin.reload: - description: Administrator command to reload the plugin configuration. - default: op - authme.admin.antibotmessages: - description: Permission to see Antibot messages - default: op - authme.player.*: - description: Permission to use all player (non-admin) commands. - children: - authme.player.canbeforced: true - authme.player.captcha: true - authme.player.changepassword: true - authme.player.email.add: true - authme.player.email.change: true - authme.player.email.recover: true - authme.player.login: true - authme.player.logout: true - authme.player.register: true - authme.player.unregister: true - authme.player.seeownaccounts: true - authme.player.email: - description: Gives access to player email commands - default: false - children: - authme.player.email.add: true - authme.player.email.change: true - authme.player.email.recover: true - authme.player.login: - description: Command permission to login. - default: true - authme.player.logout: - description: Command permission to logout. - default: true - authme.player.register: - description: Command permission to register. - default: true - authme.player.unregister: - description: Command permission to unregister. - default: true - authme.player.changepassword: - description: Command permission to change the password. - default: true - authme.player.email.add: - description: Command permission to add an email address. - default: true - authme.player.email.change: - description: Command permission to change the email address. - default: true - authme.player.email.recover: - description: Command permission to recover an account using its email address. - default: true - authme.player.captcha: - description: Command permission to use captcha. - default: true - authme.player.canbeforced: - description: Permission for users a login can be forced to. - default: true - authme.player.seeownaccounts: - description: Permission to use to see own other accounts. - default: true - authme.vip: - description: Allow vip slot when the server is full - default: op - authme.bypassantibot: - description: Bypass the AntiBot check - default: op - authme.allowmultipleaccounts: - description: Allow more accounts for same ip - default: op - authme.bypassforcesurvival: - description: Bypass all ForceSurvival features - default: op - authme.bypasspurge: - description: Permission to bypass the purging process - default: false + authme.admin.*: + description: Gives access to all admin commands + children: + authme.admin.accounts: true + authme.admin.antibotmessages: true + authme.admin.changemail: true + authme.admin.changepassword: true + authme.admin.converter: true + authme.admin.firstspawn: true + authme.admin.forcelogin: true + authme.admin.getemail: true + authme.admin.getip: true + authme.admin.lastlogin: true + authme.admin.purge: true + authme.admin.purgebannedplayers: true + authme.admin.purgelastpos: true + authme.admin.register: true + authme.admin.reload: true + authme.admin.seeotheraccounts: true + authme.admin.setfirstspawn: true + authme.admin.setspawn: true + authme.admin.spawn: true + authme.admin.switchantibot: true + authme.admin.unregister: true + authme.admin.updatemessages: true + authme.admin.accounts: + description: Administrator command to see all accounts associated with a user. + default: op + authme.admin.antibotmessages: + description: Permission to see Antibot messages. + default: op + authme.admin.changemail: + description: Administrator command to set or change the email address of a user. + default: op + authme.admin.changepassword: + description: Administrator command to change the password of a user. + default: op + authme.admin.converter: + description: Administrator command to convert old or other data to AuthMe data. + default: op + authme.admin.firstspawn: + description: Administrator command to teleport to the first AuthMe spawn. + default: op + authme.admin.forcelogin: + description: Administrator command to force-login an existing user. + default: op + authme.admin.getemail: + description: Administrator command to get the email address of a user, if set. + default: op + authme.admin.getip: + description: Administrator command to get the last known IP of a user. + default: op + authme.admin.lastlogin: + description: Administrator command to see the last login date and time of a user. + default: op + authme.admin.purge: + description: Administrator command to purge old user data. + default: op + authme.admin.purgebannedplayers: + description: Administrator command to purge all data associated with banned players. + default: op + authme.admin.purgelastpos: + description: Administrator command to purge the last position of a user. + default: op + authme.admin.register: + description: Administrator command to register a new user. + default: op + authme.admin.reload: + description: Administrator command to reload the plugin configuration. + default: op + authme.admin.seeotheraccounts: + description: Permission to see the other accounts of the players that log in. + default: op + authme.admin.setfirstspawn: + description: Administrator command to set the first AuthMe spawn. + default: op + authme.admin.setspawn: + description: Administrator command to set the AuthMe spawn. + default: op + authme.admin.spawn: + description: Administrator command to teleport to the AuthMe spawn. + default: op + authme.admin.switchantibot: + description: Administrator command to toggle the AntiBot protection status. + default: op + authme.admin.unregister: + description: Administrator command to unregister an existing user. + default: op + authme.admin.updatemessages: + description: Permission to use the update messages command. + default: op + authme.allowmultipleaccounts: + description: Permission to be able to register multiple accounts. + default: op + authme.bypassantibot: + description: Permission node to bypass AntiBot protection. + default: op + authme.bypassforcesurvival: + description: Permission for users to bypass force-survival mode. + default: op + authme.bypasspurge: + description: Permission to bypass the purging process. + default: false + authme.player.*: + description: Gives access to all player commands + children: + authme.player.canbeforced: true + authme.player.captcha: true + authme.player.changepassword: true + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.login: true + authme.player.logout: true + authme.player.register: true + authme.player.seeownaccounts: true + authme.player.unregister: true + authme.player.canbeforced: + description: Permission for users a login can be forced to. + default: true + authme.player.captcha: + description: Command permission to use captcha. + default: true + authme.player.changepassword: + description: Command permission to change the password. + default: true + authme.player.email: + description: Gives access to all email commands + children: + authme.player.email.add: true + authme.player.email.change: true + authme.player.email.recover: true + authme.player.email.add: + description: Command permission to add an email address. + default: true + authme.player.email.change: + description: Command permission to change the email address. + default: true + authme.player.email.recover: + description: Command permission to recover an account using its email address. + default: true + authme.player.login: + description: Command permission to login. + default: true + authme.player.logout: + description: Command permission to logout. + default: true + authme.player.register: + description: Command permission to register. + default: true + authme.player.seeownaccounts: + description: Permission to use to see own other accounts. + default: true + authme.player.unregister: + description: Command permission to unregister. + default: true + authme.vip: + description: Permission node to identify VIP users. + default: op diff --git a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java index 944424f52..a4e51e28c 100644 --- a/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandInitializerTest.java @@ -18,6 +18,7 @@ import java.util.regex.Pattern; import static fr.xephi.authme.permission.DefaultPermission.OP_ONLY; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; @@ -32,7 +33,7 @@ public class CommandInitializerTest { */ private static int MAX_ALLOWED_DEPTH = 1; - private static Set commands; + private static Collection commands; @BeforeClass public static void initializeCommandCollection() { @@ -46,7 +47,7 @@ public class CommandInitializerTest { // It obviously doesn't make sense to test much of the concrete data // that is being initialized; we just want to guarantee with this test // that data is indeed being initialized and we take a few "probes" - assertThat(commands.size(), equalTo(8)); + assertThat(commands, hasSize(8)); assertThat(commandsIncludeLabel(commands, "authme"), equalTo(true)); assertThat(commandsIncludeLabel(commands, "register"), equalTo(true)); assertThat(commandsIncludeLabel(commands, "help"), equalTo(false)); diff --git a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java b/src/test/java/fr/xephi/authme/command/CommandMapperTest.java index 3fd023a4e..46efdaec4 100644 --- a/src/test/java/fr/xephi/authme/command/CommandMapperTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandMapperTest.java @@ -39,7 +39,7 @@ import static org.mockito.Mockito.mock; @RunWith(DelayedInjectionRunner.class) public class CommandMapperTest { - private static Set commands; + private static List commands; @InjectDelayed private CommandMapper mapper; diff --git a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java b/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java index 2d73549d8..c0b4771cd 100644 --- a/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java +++ b/src/test/java/fr/xephi/authme/command/CommandUtilsTest.java @@ -1,8 +1,15 @@ package fr.xephi.authme.command; import fr.xephi.authme.TestHelper; +import org.bukkit.ChatColor; +import org.junit.BeforeClass; import org.junit.Test; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; @@ -11,6 +18,13 @@ import static org.junit.Assert.assertThat; */ public class CommandUtilsTest { + private static Collection commands; + + @BeforeClass + public static void setUpTestCommands() { + commands = TestCommandsUtil.generateCommands(); + } + @Test public void shouldReturnCommandPath() { // given @@ -19,14 +33,14 @@ public class CommandUtilsTest { .description("Base") .detailedDescription("Test base command.") .executableCommand(ExecutableCommand.class) - .build(); + .register(); CommandDescription command = CommandDescription.builder() .parent(base) .labels("help", "h", "?") .description("Child") .detailedDescription("Test child command.") .executableCommand(ExecutableCommand.class) - .build(); + .register(); // when String commandPath = CommandUtils.constructCommandPath(command); @@ -42,7 +56,7 @@ public class CommandUtilsTest { @Test public void shouldComputeMinAndMaxOnEmptyCommand() { // given - CommandDescription command = getBuilderForArgsTest().build(); + CommandDescription command = getBuilderForArgsTest().register(); // when / then checkArgumentCount(command, 0, 0); @@ -54,7 +68,7 @@ public class CommandUtilsTest { CommandDescription command = getBuilderForArgsTest() .withArgument("Test", "Arg description", false) .withArgument("Test22", "Arg description 2", false) - .build(); + .register(); // when / then checkArgumentCount(command, 2, 2); @@ -67,7 +81,7 @@ public class CommandUtilsTest { .withArgument("arg1", "Arg description", false) .withArgument("arg2", "Arg description 2", true) .withArgument("arg3", "Arg description 3", true) - .build(); + .register(); // when / then checkArgumentCount(command, 1, 3); @@ -79,6 +93,46 @@ public class CommandUtilsTest { TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandUtils.class); } + @Test + public void shouldFormatSimpleArgument() { + // given + CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme"); + List labels = Collections.singletonList("authme"); + + // when + String result = CommandUtils.buildSyntax(command, labels); + + // then + assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW)); + } + + @Test + public void shouldFormatCommandWithMultipleArguments() { + // given + CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme", "register"); + List labels = Arrays.asList("authme", "reg"); + + // when + String result = CommandUtils.buildSyntax(command, labels); + + // then + assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW + " reg ")); + } + + + @Test + public void shouldFormatCommandWithOptionalArgument() { + // given + CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "email"); + List labels = Collections.singletonList("email"); + + // when + String result = CommandUtils.buildSyntax(command, labels); + + // then + assertThat(result, equalTo(ChatColor.WHITE + "/email" + ChatColor.YELLOW + " [player]")); + } + private static void checkArgumentCount(CommandDescription command, int expectedMin, int expectedMax) { assertThat(CommandUtils.getMinNumberOfArguments(command), equalTo(expectedMin)); diff --git a/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java b/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java index 285369973..9f9b02ca2 100644 --- a/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java +++ b/src/test/java/fr/xephi/authme/command/TestCommandsUtil.java @@ -1,5 +1,6 @@ package fr.xephi.authme.command; +import com.google.common.collect.ImmutableList; import fr.xephi.authme.command.executable.HelpCommand; import fr.xephi.authme.permission.AdminPermission; import fr.xephi.authme.permission.PermissionNode; @@ -8,9 +9,7 @@ import org.bukkit.command.CommandSender; import java.util.Collection; import java.util.List; -import java.util.Set; -import static com.google.common.collect.Sets.newHashSet; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; @@ -27,7 +26,7 @@ public final class TestCommandsUtil { * * @return The generated commands */ - public static Set generateCommands() { + public static List generateCommands() { // Register /authme CommandDescription authMeBase = createCommand(null, null, singletonList("authme"), ExecutableCommand.class); // Register /authme login @@ -42,13 +41,13 @@ public final class TestCommandsUtil { newArgument("player", true)); // Register /email helptest -- use only to test for help command arguments special case CommandDescription.builder().parent(emailBase).labels("helptest").executableCommand(HelpCommand.class) - .description("test").detailedDescription("Test.").withArgument("Query", "", false).build(); + .description("test").detailedDescription("Test.").withArgument("Query", "", false).register(); // Register /unregister , alias: /unreg CommandDescription unregisterBase = createCommand(AdminPermission.UNREGISTER, null, asList("unregister", "unreg"), TestUnregisterCommand.class, newArgument("player", false)); - return newHashSet(authMeBase, emailBase, unregisterBase); + return ImmutableList.of(authMeBase, emailBase, unregisterBase); } /** @@ -101,7 +100,7 @@ public final class TestCommandsUtil { } } - return command.build(); + return command.register(); } /** Shortcut command to initialize a new argument description. */ diff --git a/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java b/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java index 143ae26b8..3c57e2148 100644 --- a/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java +++ b/src/test/java/fr/xephi/authme/command/executable/HelpCommandTest.java @@ -20,6 +20,10 @@ import static fr.xephi.authme.command.FoundResultStatus.INCORRECT_ARGUMENTS; import static fr.xephi.authme.command.FoundResultStatus.MISSING_BASE_COMMAND; import static fr.xephi.authme.command.FoundResultStatus.SUCCESS; import static fr.xephi.authme.command.FoundResultStatus.UNKNOWN_LABEL; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_ALTERNATIVES; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_CHILDREN; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_COMMAND; +import static fr.xephi.authme.command.help.HelpProvider.SHOW_DESCRIPTION; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.containsString; @@ -108,7 +112,7 @@ public class HelpCommandTest { CommandDescription commandDescription = mock(CommandDescription.class); given(commandDescription.getLabelCount()).willReturn(1); FoundCommandResult foundCommandResult = new FoundCommandResult(commandDescription, singletonList("authme"), - Collections.emptyList(), 0.0, SUCCESS); + Collections.emptyList(), 0.0, SUCCESS); given(commandMapper.mapPartsToCommand(sender, arguments)).willReturn(foundCommandResult); // when @@ -116,7 +120,8 @@ public class HelpCommandTest { // then verify(sender, never()).sendMessage(anyString()); - verify(helpProvider).outputHelp(sender, foundCommandResult, HelpProvider.SHOW_CHILDREN); + verify(helpProvider).outputHelp(sender, foundCommandResult, + SHOW_DESCRIPTION | SHOW_COMMAND | SHOW_CHILDREN | SHOW_ALTERNATIVES); } @Test @@ -126,7 +131,7 @@ public class HelpCommandTest { CommandDescription commandDescription = mock(CommandDescription.class); given(commandDescription.getLabelCount()).willReturn(2); FoundCommandResult foundCommandResult = new FoundCommandResult(commandDescription, asList("authme", "getpos"), - Collections.emptyList(), 0.0, INCORRECT_ARGUMENTS); + Collections.emptyList(), 0.0, INCORRECT_ARGUMENTS); given(commandMapper.mapPartsToCommand(sender, arguments)).willReturn(foundCommandResult); // when diff --git a/src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java b/src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java deleted file mode 100644 index 77320d151..000000000 --- a/src/test/java/fr/xephi/authme/command/help/CommandSyntaxHelperTest.java +++ /dev/null @@ -1,76 +0,0 @@ -package fr.xephi.authme.command.help; - -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.command.CommandDescription; -import fr.xephi.authme.command.TestCommandsUtil; -import org.bukkit.ChatColor; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; - -/** - * Test for {@link CommandSyntaxHelper}. - */ -public class CommandSyntaxHelperTest { - - private static Set commands; - - @BeforeClass - public static void setUpTestCommands() { - commands = TestCommandsUtil.generateCommands(); - } - - @Test - public void shouldFormatSimpleArgument() { - // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme"); - List labels = Collections.singletonList("authme"); - - // when - String result = CommandSyntaxHelper.getSyntax(command, labels); - - // then - assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW)); - } - - @Test - public void shouldFormatCommandWithMultipleArguments() { - // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "authme", "register"); - List labels = Arrays.asList("authme", "reg"); - - // when - String result = CommandSyntaxHelper.getSyntax(command, labels); - - // then - assertThat(result, equalTo(ChatColor.WHITE + "/authme" + ChatColor.YELLOW + " reg ")); - } - - - @Test - public void shouldFormatCommandWithOptionalArgument() { - // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(commands, "email"); - List labels = Collections.singletonList("email"); - - // when - String result = CommandSyntaxHelper.getSyntax(command, labels); - - // then - assertThat(result, equalTo(ChatColor.WHITE + "/email" + ChatColor.YELLOW + " [player]")); - } - - @Test - public void shouldHaveHiddenConstructor() { - // given / when / then - TestHelper.validateHasOnlyPrivateEmptyConstructor(CommandSyntaxHelper.class); - } - -} diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java b/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java index 7c50829f8..5e79ef993 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpMessagesConsistencyTest.java @@ -9,8 +9,8 @@ import org.bukkit.configuration.file.YamlConfiguration; import org.junit.Test; import java.io.File; +import java.util.Collection; import java.util.List; -import java.util.Set; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; @@ -78,7 +78,7 @@ public class HelpMessagesConsistencyTest { * @return the CommandDescription object for the {@code /authme register} command. */ private static CommandDescription getAuthMeRegisterDescription() { - Set commands = new CommandInitializer().getCommands(); + Collection commands = new CommandInitializer().getCommands(); List children = commands.stream() .filter(command -> command.getLabels().contains("authme")) diff --git a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java index a451ff4d2..150bb42f5 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpMessagesServiceTest.java @@ -12,10 +12,11 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import java.util.Set; +import java.util.Collection; import java.util.function.Function; import static fr.xephi.authme.TestHelper.getJarFile; +import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.sameInstance; @@ -30,7 +31,7 @@ import static org.mockito.Matchers.any; public class HelpMessagesServiceTest { private static final String TEST_FILE = "/fr/xephi/authme/command/help/help_test.yml"; - private static final Set COMMANDS = TestCommandsUtil.generateCommands(); + private static final Collection COMMANDS = TestCommandsUtil.generateCommands(); @InjectDelayed private HelpMessagesService helpMessagesService; @@ -48,7 +49,7 @@ public class HelpMessagesServiceTest { @Test public void shouldReturnLocalizedCommand() { // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "authme", "register"); + CommandDescription command = getCommandWithLabel(COMMANDS, "authme", "register"); // when CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command); @@ -68,7 +69,7 @@ public class HelpMessagesServiceTest { @Test public void shouldReturnLocalizedCommandWithDefaults() { // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "authme", "login"); + CommandDescription command = getCommandWithLabel(COMMANDS, "authme", "login"); // when CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command); @@ -84,7 +85,7 @@ public class HelpMessagesServiceTest { @Test public void shouldReturnSameCommandForNoLocalization() { // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "email"); + CommandDescription command = getCommandWithLabel(COMMANDS, "email"); // when CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command); @@ -96,7 +97,7 @@ public class HelpMessagesServiceTest { @Test public void shouldKeepChildrenInLocalCommand() { // given - CommandDescription command = TestCommandsUtil.getCommandWithLabel(COMMANDS, "authme"); + CommandDescription command = getCommandWithLabel(COMMANDS, "authme"); // when CommandDescription localCommand = helpMessagesService.buildLocalizedDescription(command); @@ -114,4 +115,28 @@ public class HelpMessagesServiceTest { assertThat(helpMessagesService.getMessage(HelpMessage.RESULT), equalTo("res.")); assertThat(helpMessagesService.getMessage(HelpSection.ARGUMENTS), equalTo("arg.")); } + + @Test + public void shouldGetLocalCommandDescription() { + // given + CommandDescription command = getCommandWithLabel(COMMANDS, "authme", "register"); + + // when + String description = helpMessagesService.getDescription(command); + + // then + assertThat(description, equalTo("Registration")); + } + + @Test + public void shouldFallbackToDescriptionOnCommandObject() { + // given + CommandDescription command = getCommandWithLabel(COMMANDS, "unregister"); + + // when + String description = helpMessagesService.getDescription(command); + + // then + assertThat(description, equalTo(command.getDescription())); + } } diff --git a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java index 5a0f06f29..e90a76120 100644 --- a/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java +++ b/src/test/java/fr/xephi/authme/command/help/HelpProviderTest.java @@ -20,9 +20,9 @@ import org.mockito.Mock; import org.mockito.internal.stubbing.answers.ReturnsArgumentAt; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Set; import java.util.stream.Collectors; import static fr.xephi.authme.command.TestCommandsUtil.getCommandWithLabel; @@ -53,7 +53,7 @@ import static org.mockito.Mockito.verify; @RunWith(DelayedInjectionRunner.class) public class HelpProviderTest { - private static Set commands; + private static Collection commands; @InjectDelayed private HelpProvider helpProvider; @@ -251,6 +251,10 @@ public class HelpProviderTest { // given CommandDescription command = getCommandWithLabel(commands, "authme"); FoundCommandResult result = newFoundResult(command, Collections.singletonList("authme")); + given(helpMessagesService.getDescription(getCommandWithLabel(commands, "authme", "login"))) + .willReturn("Command for login [localized]"); + given(helpMessagesService.getDescription(getCommandWithLabel(commands, "authme", "register"))) + .willReturn("Registration command [localized]"); // when helpProvider.outputHelp(sender, result, SHOW_CHILDREN); @@ -258,9 +262,9 @@ public class HelpProviderTest { // then List lines = getLines(sender); assertThat(lines, hasSize(4)); - assertThat(lines.get(1), containsString("Children:")); - assertThat(lines.get(2), containsString("/authme login: login cmd")); - assertThat(lines.get(3), containsString("/authme register: register cmd")); + assertThat(lines.get(1), equalTo("Children:")); + assertThat(lines.get(2), equalTo(" /authme login: Command for login [localized]")); + assertThat(lines.get(3), equalTo(" /authme register: Registration command [localized]")); } @Test @@ -395,6 +399,24 @@ public class HelpProviderTest { assertThat(lines.get(0), equalTo("Command: /authme register ")); } + @Test + public void shouldShowAlternativesForRootCommand() { + // given + CommandDescription command = getCommandWithLabel(commands, "unregister"); + FoundCommandResult result = newFoundResult(command, Collections.singletonList("unreg")); + + // when + helpProvider.outputHelp(sender, result, SHOW_COMMAND | SHOW_ALTERNATIVES); + + // then + List lines = getLines(sender); + assertThat(lines, hasSize(4)); + assertThat(lines.get(0), equalTo("Header")); + assertThat(lines.get(1), equalTo("Command: /unreg ")); + assertThat(lines.get(2), equalTo("Alternatives:")); + assertThat(lines.get(3), equalTo(" /unregister ")); + } + /** * Generate an instance of {@link FoundCommandResult} with the given command and labels. All other fields aren't * retrieved by {@link HelpProvider} and so are initialized to default values for the tests. diff --git a/src/test/java/fr/xephi/authme/message/MessagesFileYamlCheckerTest.java b/src/test/java/fr/xephi/authme/message/MessagesFileYamlCheckerTest.java deleted file mode 100644 index ebb04fc2d..000000000 --- a/src/test/java/fr/xephi/authme/message/MessagesFileYamlCheckerTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package fr.xephi.authme.message; - -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.util.StringUtils; -import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.Test; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import static org.junit.Assert.fail; - -/** - * Tests that all YML message files can be loaded. - */ -public class MessagesFileYamlCheckerTest { - - /** Path in the resources folder where the message files are located. */ - private static final String MESSAGES_FOLDER = "/messages/"; - /** Pattern of the message file names. */ - private static final Pattern MESSAGE_FILE_PATTERN = Pattern.compile("messages_\\w+\\.yml"); - /** Message key that is present in all files. Used to make sure that text is returned. */ - private static final MessageKey MESSAGE_KEY = MessageKey.LOGIN_MESSAGE; - - @Test - public void shouldAllBeValidYaml() { - // given - List messageFiles = getMessageFiles(); - - // when - List errors = new ArrayList<>(); - for (File file : messageFiles) { - String error = null; - try { - YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); - if (StringUtils.isEmpty(configuration.getString(MESSAGE_KEY.getKey()))) { - error = "Message for '" + MESSAGE_KEY + "' is empty"; - } - } catch (Exception e) { - error = "Could not load file: " + StringUtils.formatException(e); - } - if (!StringUtils.isEmpty(error)) { - errors.add(file.getName() + ": " + error); - } - } - - // then - if (!errors.isEmpty()) { - fail("Errors during verification of message files:\n-" + String.join("\n-", errors)); - } - } - - - private List getMessageFiles() { - File folder = TestHelper.getJarFile(MESSAGES_FOLDER); - File[] files = folder.listFiles(); - if (files == null) { - throw new IllegalStateException("Could not read folder '" + folder.getName() + "'"); - } - - List messageFiles = new ArrayList<>(); - for (File file : files) { - if (MESSAGE_FILE_PATTERN.matcher(file.getName()).matches()) { - messageFiles.add(file); - } - } - if (messageFiles.isEmpty()) { - throw new IllegalStateException("Error getting message files: list of files is empty"); - } - return messageFiles; - } - -} diff --git a/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java new file mode 100644 index 000000000..7fa6fecba --- /dev/null +++ b/src/test/java/fr/xephi/authme/message/YamlTextFileCheckerTest.java @@ -0,0 +1,98 @@ +package fr.xephi.authme.message; + +import fr.xephi.authme.TestHelper; +import fr.xephi.authme.command.help.HelpSection; +import fr.xephi.authme.util.StringUtils; +import org.bukkit.configuration.file.YamlConfiguration; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Pattern; + +import static org.junit.Assert.fail; + +/** + * Tests that all YML text files can be loaded. + */ +public class YamlTextFileCheckerTest { + + /** Path in the resources folder where the message files are located. */ + private static final String MESSAGES_FOLDER = "/messages/"; + /** Contains all files of the MESSAGES_FOLDER. */ + private static List messageFiles; + + @BeforeClass + public static void loadMessagesFiles() { + File folder = TestHelper.getJarFile(MESSAGES_FOLDER); + File[] files = folder.listFiles(); + if (files == null || files.length == 0) { + throw new IllegalStateException("Could not read folder '" + folder.getName() + "'"); + } + messageFiles = Arrays.asList(files); + } + + @Test + public void testAllMessagesYmlFiles() { + checkFiles( + Pattern.compile("messages_\\w+\\.yml"), + MessageKey.LOGIN_MESSAGE.getKey()); + } + + @Test + public void testAllHelpYmlFiles() { + checkFiles( + Pattern.compile("help_\\w+\\.yml"), + HelpSection.ALTERNATIVES.getKey()); + } + + /** + * Checks all files in the messages folder that match the given pattern. + * + * @param pattern the pattern the file name needs to match + * @param mandatoryKey key present in all matched files + */ + private void checkFiles(Pattern pattern, String mandatoryKey) { + List errors = new ArrayList<>(); + + boolean hasMatch = false; + for (File file : messageFiles) { + if (pattern.matcher(file.getName()).matches()) { + checkFile(file, mandatoryKey, errors); + hasMatch = true; + } + } + + if (!errors.isEmpty()) { + fail("Errors while checking files matching '" + pattern + "':\n-" + String.join("\n-", errors)); + } else if (!hasMatch) { + fail("Could not find any files satisfying pattern '" + pattern + "'"); + } + } + + /** + * Checks that the provided YAML file can be loaded and that it contains a non-empty text + * for the provided mandatory key. + * + * @param file the file to check + * @param mandatoryKey the key for which text must be present + * @param errors collection of errors to add to if the verification fails + */ + private void checkFile(File file, String mandatoryKey, List errors) { + String error = null; + try { + YamlConfiguration configuration = YamlConfiguration.loadConfiguration(file); + if (StringUtils.isEmpty(configuration.getString(mandatoryKey))) { + error = "Message for '" + mandatoryKey + "' is empty"; + } + } catch (Exception e) { + error = "Could not load file: " + StringUtils.formatException(e); + } + if (!StringUtils.isEmpty(error)) { + errors.add(file.getName() + ": " + error); + } + } +} diff --git a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java index bd18df8ad..f44ef2220 100644 --- a/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java +++ b/src/test/java/fr/xephi/authme/permission/PermissionConsistencyTest.java @@ -26,7 +26,7 @@ public class PermissionConsistencyTest { /** All classes defining permission nodes. */ private static final Set> PERMISSION_CLASSES = ImmutableSet - .>of(PlayerPermission.class, AdminPermission.class, PlayerStatePermission.class); + .of(PlayerPermission.class, AdminPermission.class, PlayerStatePermission.class); /** Wildcard permissions (present in plugin.yml but not in the codebase). */ private static final Set PLUGIN_YML_PERMISSIONS_WILDCARDS = diff --git a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java index a39c7a6e2..7dfa0b0d3 100644 --- a/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java +++ b/src/test/java/fr/xephi/authme/service/AntiBotServiceTest.java @@ -51,6 +51,7 @@ public class AntiBotServiceTest { @BeforeInjecting public void initSettings() { given(settings.getProperty(ProtectionSettings.ANTIBOT_DURATION)).willReturn(10); + given(settings.getProperty(ProtectionSettings.ANTIBOT_INTERVAL)).willReturn(5); given(settings.getProperty(ProtectionSettings.ANTIBOT_SENSIBILITY)).willReturn(5); given(settings.getProperty(ProtectionSettings.ENABLE_ANTIBOT)).willReturn(true); given(settings.getProperty(ProtectionSettings.ANTIBOT_DELAY)).willReturn(8); diff --git a/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java deleted file mode 100644 index ab4031e25..000000000 --- a/src/test/java/fr/xephi/authme/settings/ConfigFileConsistencyTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package fr.xephi.authme.settings; - -import com.github.authme.configme.knownproperties.ConfigurationData; -import com.github.authme.configme.migration.MigrationService; -import com.github.authme.configme.migration.PlainMigrationService; -import com.github.authme.configme.properties.Property; -import com.github.authme.configme.resource.PropertyResource; -import com.github.authme.configme.resource.YamlFileResource; -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; -import org.bukkit.configuration.MemorySection; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.junit.Test; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; - -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -/** - * Test for {@link Settings} and the project's config.yml, - * verifying that no settings are missing from the file. - */ -public class ConfigFileConsistencyTest { - - /** The file name of the project's sample config file. */ - private static final String CONFIG_FILE = "/config.yml"; - - @Test - public void shouldHaveAllConfigs() throws IOException { - // given - File configFile = TestHelper.getJarFile(CONFIG_FILE); - PropertyResource resource = new YamlFileResource(configFile); - MigrationService migration = new PlainMigrationService(); - - // when - boolean result = migration.checkAndMigrate( - resource, AuthMeSettingsRetriever.buildConfigurationData().getProperties()); - - // then - if (result) { - Set knownProperties = getAllKnownPropertyPaths(); - List missingProperties = new ArrayList<>(); - for (String path : knownProperties) { - if (!resource.contains(path)) { - missingProperties.add(path); - } - } - fail("Found missing properties!\n-" + String.join("\n-", missingProperties)); - } - } - - @Test - public void shouldNotHaveUnknownConfigs() { - // given - File configFile = TestHelper.getJarFile(CONFIG_FILE); - FileConfiguration configuration = YamlConfiguration.loadConfiguration(configFile); - Map allReadProperties = configuration.getValues(true); - Set knownKeys = getAllKnownPropertyPaths(); - - // when - List unknownPaths = new ArrayList<>(); - for (Map.Entry entry : allReadProperties.entrySet()) { - // The value being a MemorySection means it's a parent node - if (!(entry.getValue() instanceof MemorySection) && !knownKeys.contains(entry.getKey())) { - unknownPaths.add(entry.getKey()); - } - } - - // then - if (!unknownPaths.isEmpty()) { - fail("Found " + unknownPaths.size() + " unknown property paths in the project's config.yml: \n- " - + String.join("\n- ", unknownPaths)); - } - } - - @Test - public void shouldHaveValueCorrespondingToPropertyDefault() { - // given - File configFile = TestHelper.getJarFile(CONFIG_FILE); - PropertyResource resource = new YamlFileResource(configFile); - ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); - - // when / then - for (Property property : configurationData.getProperties()) { - assertThat("Default value of '" + property.getPath() + "' in config.yml should be the same as in Property", - property.getValue(resource).equals(property.getDefaultValue()), equalTo(true)); - } - } - - private static Set getAllKnownPropertyPaths() { - return AuthMeSettingsRetriever.buildConfigurationData() - .getProperties().stream() - .map(Property::getPath) - .collect(Collectors.toSet()); - } -} diff --git a/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java b/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java new file mode 100644 index 000000000..7123ac26b --- /dev/null +++ b/src/test/java/fr/xephi/authme/settings/SettingsConsistencyTest.java @@ -0,0 +1,69 @@ +package fr.xephi.authme.settings; + +import com.github.authme.configme.knownproperties.ConfigurationData; +import com.github.authme.configme.properties.Property; +import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.fail; + +/** + * Tests the consistency of the settings configuration. + */ +public class SettingsConsistencyTest { + + /** + * Maximum characters one comment line may have (prevents horizontal scrolling). + */ + private static final int MAX_COMMENT_LENGTH = 90; + + private static ConfigurationData configurationData; + + @BeforeClass + public static void buildConfigurationData() { + configurationData = AuthMeSettingsRetriever.buildConfigurationData(); + } + + @Test + public void shouldHaveCommentOnEachProperty() { + // given + List> properties = configurationData.getProperties(); + + // when / then + for (Property property : properties) { + if (configurationData.getCommentsForSection(property.getPath()).length == 0) { + fail("No comment defined for '" + property + "'"); + } + } + } + + @Test + public void shouldNotHaveVeryLongCommentLines() { + // given + List> properties = configurationData.getProperties(); + List> badProperties = new ArrayList<>(); + + // when + for (Property property : properties) { + for (String comment : configurationData.getCommentsForSection(property.getPath())) { + if (comment.length() > MAX_COMMENT_LENGTH) { + badProperties.add(property); + break; + } + } + } + + // then + if (!badProperties.isEmpty()) { + fail("Comment lines should not be longer than " + MAX_COMMENT_LENGTH + " chars, " + + "but found too long comments for:\n- " + + badProperties.stream().map(Property::getPath).collect(Collectors.joining("\n- "))); + } + } + +} diff --git a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java b/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java deleted file mode 100644 index 93325110a..000000000 --- a/src/test/java/fr/xephi/authme/settings/SettingsMigrationServiceTest.java +++ /dev/null @@ -1,67 +0,0 @@ -package fr.xephi.authme.settings; - -import com.github.authme.configme.knownproperties.ConfigurationData; -import com.github.authme.configme.resource.PropertyResource; -import com.github.authme.configme.resource.YamlFileResource; -import com.google.common.io.Files; -import fr.xephi.authme.TestHelper; -import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.File; -import java.io.IOException; - -import static org.hamcrest.Matchers.arrayWithSize; -import static org.hamcrest.Matchers.equalTo; -import static org.junit.Assert.assertThat; -import static org.junit.Assume.assumeThat; - -/** - * Test for {@link SettingsMigrationService}. - */ -public class SettingsMigrationServiceTest { - - @Rule - public TemporaryFolder testFolderHandler = new TemporaryFolder(); - - private File testFolder; - private File configTestFile; - - /** - * Ensure that AuthMe regards the JAR's own config.yml as complete. - * If something legitimately needs migrating, a test from {@link ConfigFileConsistencyTest} should fail. - * If none fails in that class, it means something is wrong with the migration service - * as it wants to perform a migration on our up-to-date config.yml. - */ - @Test - public void shouldNotRewriteJarConfig() throws IOException { - // given - copyConfigToTestFolder(); - PropertyResource resource = new YamlFileResource(configTestFile); - ConfigurationData configurationData = AuthMeSettingsRetriever.buildConfigurationData(); - assumeThat(testFolder.listFiles(), arrayWithSize(1)); - SettingsMigrationService migrationService = new SettingsMigrationService(testFolder); - - // when - boolean result = migrationService.checkAndMigrate(resource, configurationData.getProperties()); - - // then - assertThat(result, equalTo(false)); - assertThat(testFolder.listFiles(), arrayWithSize(1)); - } - - private void copyConfigToTestFolder() throws IOException { - testFolder = testFolderHandler.newFolder("migrationtest"); - - final File testConfig = testFolderHandler.newFile("migrationtest/config.yml"); - final File realConfig = TestHelper.getJarFile("/config.yml"); - - Files.copy(realConfig, testConfig); - if (!testConfig.exists()) { - throw new IOException("Could not copy project's config.yml to test folder"); - } - configTestFile = testConfig; - } -} diff --git a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java index f36acf755..fa8ee9c6d 100644 --- a/src/test/java/fr/xephi/authme/util/FileUtilsTest.java +++ b/src/test/java/fr/xephi/authme/util/FileUtilsTest.java @@ -11,6 +11,8 @@ import java.io.File; import java.io.IOException; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertThat; /** @@ -47,15 +49,15 @@ public class FileUtilsTest { public void shouldCopyFileFromJar() throws IOException { // given File folder = temporaryFolder.newFolder(); - File file = new File(folder, "some/folders/config.yml"); + File file = new File(folder, "some/folders/welcome.txt"); // when - boolean result = FileUtils.copyFileFromResource(file, "config.yml"); + boolean result = FileUtils.copyFileFromResource(file, "welcome.txt"); // then assertThat(result, equalTo(true)); assertThat(file.exists(), equalTo(true)); - File configJarFile = TestHelper.getJarFile("/config.yml"); + File configJarFile = TestHelper.getJarFile("/welcome.txt"); assertThat(file.length(), equalTo(configJarFile.length())); } @@ -119,6 +121,13 @@ public class FileUtilsTest { // Nothing happens } + @Test + public void shouldGetResourceFromJar() { + // given / when / then + assertThat(FileUtils.getResourceFromJar("config.yml"), not(nullValue())); + assertThat(FileUtils.getResourceFromJar("does-not-exist"), nullValue()); + } + @Test public void shouldConstructPath() { // given/when diff --git a/src/test/java/tools/checktestmocks/CheckTestMocks.java b/src/test/java/tools/checktestmocks/CheckTestMocks.java index def4e822f..9af3ae040 100644 --- a/src/test/java/tools/checktestmocks/CheckTestMocks.java +++ b/src/test/java/tools/checktestmocks/CheckTestMocks.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Scanner; import java.util.Set; import java.util.stream.Collectors; @@ -29,11 +28,6 @@ public class CheckTestMocks implements AutoToolTask { return "checkTestMocks"; } - @Override - public void execute(Scanner scanner) { - executeDefault(); - } - @Override public void executeDefault() { ClassCollector collector = new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, TestHelper.PROJECT_ROOT); diff --git a/src/test/java/tools/docs/UpdateDocsTask.java b/src/test/java/tools/docs/UpdateDocsTask.java index cfdb69ef9..84f723c64 100644 --- a/src/test/java/tools/docs/UpdateDocsTask.java +++ b/src/test/java/tools/docs/UpdateDocsTask.java @@ -1,26 +1,20 @@ package tools.docs; -import com.google.common.collect.ImmutableSet; -import tools.docs.commands.CommandPageCreater; -import tools.docs.hashmethods.HashAlgorithmsDescriptionTask; -import tools.docs.permissions.PermissionsListWriter; -import tools.docs.translations.TranslationPageGenerator; +import fr.xephi.authme.ClassCollector; +import fr.xephi.authme.TestHelper; import tools.utils.AutoToolTask; import tools.utils.ToolTask; +import java.util.List; import java.util.Scanner; -import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; /** * Task that runs all tasks which update files in the docs folder. */ public class UpdateDocsTask implements AutoToolTask { - private static final Set> TASKS = ImmutableSet - .of(CommandPageCreater.class, HashAlgorithmsDescriptionTask.class, - PermissionsListWriter.class, TranslationPageGenerator.class); - @Override public String getTaskName() { return "updateDocs"; @@ -40,19 +34,18 @@ public class UpdateDocsTask implements AutoToolTask { }); } - private static ToolTask instantiateTask(Class clazz) { - try { - return clazz.newInstance(); - } catch (IllegalAccessException | InstantiationException e) { - throw new UnsupportedOperationException("Could not instantiate task class '" + clazz + "'", e); - } - } - - private static void executeTasks(Consumer taskRunner) { - for (Class taskClass : TASKS) { - ToolTask task = instantiateTask(taskClass); + private void executeTasks(Consumer taskRunner) { + for (ToolTask task : getDocTasks()) { System.out.println("\nRunning " + task.getTaskName() + "\n-------------------"); taskRunner.accept(task); } } + + private List getDocTasks() { + ClassCollector classCollector = + new ClassCollector(TestHelper.TEST_SOURCES_FOLDER, "tools/docs"); + return classCollector.getInstancesOfType(ToolTask.class).stream() + .filter(task -> task.getClass() != getClass()) + .collect(Collectors.toList()); + } } diff --git a/src/test/java/tools/docs/commands/CommandPageCreater.java b/src/test/java/tools/docs/commands/CommandPageCreater.java index 6a2f5edfe..ff1500a77 100644 --- a/src/test/java/tools/docs/commands/CommandPageCreater.java +++ b/src/test/java/tools/docs/commands/CommandPageCreater.java @@ -6,14 +6,12 @@ import fr.xephi.authme.command.CommandInitializer; import fr.xephi.authme.command.CommandUtils; import fr.xephi.authme.permission.PermissionNode; import tools.utils.AutoToolTask; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; import java.util.Collection; -import java.util.Scanner; -import java.util.Set; public class CommandPageCreater implements AutoToolTask { @@ -24,19 +22,14 @@ public class CommandPageCreater implements AutoToolTask { return "createCommandPage"; } - @Override - public void execute(Scanner scanner) { - executeDefault(); - } - @Override public void executeDefault() { CommandInitializer commandInitializer = new CommandInitializer(); - final Set baseCommands = commandInitializer.getCommands(); + final Collection baseCommands = commandInitializer.getCommands(); NestedTagValue commandTags = new NestedTagValue(); addCommandsInfo(commandTags, baseCommands); - FileUtils.generateFileFromTemplate( + FileIoUtils.generateFileFromTemplate( ToolsConstants.TOOLS_SOURCE_ROOT + "docs/commands/commands.tpl.md", OUTPUT_FILE, TagValueHolder.create().put("commands", commandTags)); diff --git a/src/test/java/tools/docs/commands/commands.tpl.md b/src/test/java/tools/docs/commands/commands.tpl.md index c0f4da120..61018634c 100644 --- a/src/test/java/tools/docs/commands/commands.tpl.md +++ b/src/test/java/tools/docs/commands/commands.tpl.md @@ -1,5 +1,5 @@ - + ## AuthMe Commands You can use the following commands to use the features of AuthMe. Mandatory arguments are marked with `< >` diff --git a/src/test/java/tools/docs/config/UpdateConfigPageTask.java b/src/test/java/tools/docs/config/UpdateConfigPageTask.java new file mode 100644 index 000000000..f9a249a70 --- /dev/null +++ b/src/test/java/tools/docs/config/UpdateConfigPageTask.java @@ -0,0 +1,49 @@ +package tools.docs.config; + +import com.github.authme.configme.SettingsManager; +import com.github.authme.configme.resource.YamlFileResource; +import fr.xephi.authme.settings.properties.AuthMeSettingsRetriever; +import fr.xephi.authme.util.FileUtils; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; +import tools.utils.TagValueHolder; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.io.IOException; + +/** + * Task for updating the config docs page. + */ +public class UpdateConfigPageTask implements AutoToolTask { + + private static final String TEMPLATE_FILE = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/config/config.tpl.md"; + private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "config.md"; + + @Override + public String getTaskName() { + return "updateConfigPage"; + } + + @Override + public void executeDefault() { + File config = null; + try { + // Create empty temporary .yml file and save the config to it + config = File.createTempFile("authme-config-", ".yml"); + SettingsManager settingsManager = new SettingsManager( + new YamlFileResource(config), null, AuthMeSettingsRetriever.buildConfigurationData()); + settingsManager.save(); + + // Get the contents and generate template file + TagValueHolder tagValueHolder = TagValueHolder.create() + .put("config", FileIoUtils.readFromFile(config.toPath())); + FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, OUTPUT_FILE, tagValueHolder); + System.out.println("Wrote to '" + OUTPUT_FILE + "'"); + } catch (IOException e) { + throw new IllegalStateException(e); + } finally { + FileUtils.delete(config); + } + } +} diff --git a/src/test/java/tools/docs/config/config.tpl.md b/src/test/java/tools/docs/config/config.tpl.md new file mode 100644 index 000000000..25cf8c2c0 --- /dev/null +++ b/src/test/java/tools/docs/config/config.tpl.md @@ -0,0 +1,16 @@ + + + +## AuthMe Configuration +The first time you run AuthMe it will create a config.yml file in the plugins/AuthMe folder, +with which you can configure various settings. This following is the initial contents of +the generated config.yml file. + +```yml +{config} +``` + +To change settings on a running server, save your changes to config.yml and use +`/authme reload`. + +{gen_footer} diff --git a/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java b/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java index 95da44d53..b21d7fda3 100644 --- a/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java +++ b/src/test/java/tools/docs/hashmethods/HashAlgorithmsDescriptionTask.java @@ -2,13 +2,12 @@ package tools.docs.hashmethods; import fr.xephi.authme.security.HashAlgorithm; import tools.utils.AutoToolTask; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; import java.util.Map; -import java.util.Scanner; /** * Task for generating the markdown page describing the AuthMe hash algorithms. @@ -20,11 +19,6 @@ public class HashAlgorithmsDescriptionTask implements AutoToolTask { private static final String CUR_FOLDER = ToolsConstants.TOOLS_SOURCE_ROOT + "docs/hashmethods/"; private static final String OUTPUT_FILE = ToolsConstants.DOCS_FOLDER + "hash_algorithms.md"; - @Override - public void execute(Scanner scanner) { - executeDefault(); - } - @Override public void executeDefault() { // Gather info and construct a row for each method @@ -34,7 +28,8 @@ public class HashAlgorithmsDescriptionTask implements AutoToolTask { // Write to the docs file TagValueHolder tags = TagValueHolder.create().put("algorithms", methodRows); - FileUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags); + FileIoUtils.generateFileFromTemplate(CUR_FOLDER + "hash_algorithms.tpl.md", OUTPUT_FILE, tags); + System.out.println("Wrote to '" + OUTPUT_FILE + "'"); } private static NestedTagValue constructMethodRows(Map descriptions) { diff --git a/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md b/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md index 17431eb5b..2f542b50f 100644 --- a/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md +++ b/src/test/java/tools/docs/hashmethods/hash_algorithms.tpl.md @@ -1,5 +1,5 @@ - + ## Hash Algorithms AuthMe supports the following hash algorithms for storing your passwords safely. diff --git a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java index f6221ac5f..e36355121 100644 --- a/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java +++ b/src/test/java/tools/docs/permissions/PermissionNodesGatherer.java @@ -2,15 +2,17 @@ package tools.docs.permissions; import fr.xephi.authme.ClassCollector; import fr.xephi.authme.permission.PermissionNode; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.ToolsConstants; import java.util.EnumSet; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * Gatherer to generate up-to-date lists of the AuthMe permission nodes. @@ -27,6 +29,11 @@ public class PermissionNodesGatherer { + "(.*?)\\s+\\*/" // Capture everything until we encounter '*/' + "\\s+([A-Z_]+)\\("); // Match the enum name (e.g. 'LOGIN'), until before the first '(' + /** + * List of all enum classes that implement the {@link PermissionNode} interface. + */ + private List> permissionClasses; + /** * Return a sorted collection of all permission nodes, including its JavaDoc description. * @@ -39,14 +46,27 @@ public class PermissionNodesGatherer { result.put("authme.player.*", "Permission to use all player (non-admin) commands."); result.put("authme.player.email", "Grants all email permissions."); - new ClassCollector(ToolsConstants.MAIN_SOURCE_ROOT, "") - .collectClasses(PermissionNode.class) - .stream() - .filter(Class::isEnum) - .forEach(clz -> addDescriptionsForClass((Class) clz, result)); + getPermissionClasses().forEach(clz -> addDescriptionsForClass((Class) clz, result)); return result; } + /** + * Return all enum classes implementing the PermissionNode interface. + * + * @return all permission node enums + */ + public List> getPermissionClasses() { + if (permissionClasses == null) { + ClassCollector classCollector = new ClassCollector(ToolsConstants.MAIN_SOURCE_ROOT, ""); + permissionClasses = classCollector + .collectClasses(PermissionNode.class) + .stream() + .filter(Class::isEnum) + .collect(Collectors.toList()); + } + return permissionClasses; + } + private & PermissionNode> void addDescriptionsForClass(Class clazz, Map descriptions) { String classSource = getSourceForClass(clazz); @@ -83,7 +103,7 @@ public class PermissionNodesGatherer { */ private static & PermissionNode> String getSourceForClass(Class clazz) { String classFile = ToolsConstants.MAIN_SOURCE_ROOT + clazz.getName().replace(".", "/") + ".java"; - return FileUtils.readFromFile(classFile); + return FileIoUtils.readFromFile(classFile); } } diff --git a/src/test/java/tools/docs/permissions/PermissionsListWriter.java b/src/test/java/tools/docs/permissions/PermissionsListWriter.java index 6d1b48d74..797e26e79 100644 --- a/src/test/java/tools/docs/permissions/PermissionsListWriter.java +++ b/src/test/java/tools/docs/permissions/PermissionsListWriter.java @@ -1,13 +1,12 @@ package tools.docs.permissions; import tools.utils.AutoToolTask; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; import java.util.Map; -import java.util.Scanner; /** * Task responsible for formatting a permissions node list and @@ -23,23 +22,13 @@ public class PermissionsListWriter implements AutoToolTask { return "writePermissionsList"; } - @Override - public void execute(Scanner scanner) { - generateAndWriteFile(); - } - @Override public void executeDefault() { - generateAndWriteFile(); - } - - private static void generateAndWriteFile() { final NestedTagValue permissionsTagValue = generatePermissionsList(); TagValueHolder tags = TagValueHolder.create().put("nodes", permissionsTagValue); - FileUtils.generateFileFromTemplate(TEMPLATE_FILE, PERMISSIONS_OUTPUT_FILE, tags); + FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, PERMISSIONS_OUTPUT_FILE, tags); System.out.println("Wrote to '" + PERMISSIONS_OUTPUT_FILE + "'"); - System.out.println("Before committing, please verify the output!"); } private static NestedTagValue generatePermissionsList() { diff --git a/src/test/java/tools/docs/permissions/permission_nodes.tpl.md b/src/test/java/tools/docs/permissions/permission_nodes.tpl.md index 8d2a2b4f4..8987f08bb 100644 --- a/src/test/java/tools/docs/permissions/permission_nodes.tpl.md +++ b/src/test/java/tools/docs/permissions/permission_nodes.tpl.md @@ -1,5 +1,5 @@ - + ## AuthMe Permission Nodes The following are the permission nodes that are currently supported by the latest dev builds. diff --git a/src/test/java/tools/docs/translations/TranslationPageGenerator.java b/src/test/java/tools/docs/translations/TranslationPageGenerator.java index e7785bd7a..240e7a640 100644 --- a/src/test/java/tools/docs/translations/TranslationPageGenerator.java +++ b/src/test/java/tools/docs/translations/TranslationPageGenerator.java @@ -3,14 +3,13 @@ package tools.docs.translations; import com.google.common.collect.ImmutableMap; import tools.docs.translations.TranslationsGatherer.TranslationInfo; import tools.utils.AutoToolTask; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.TagValue.NestedTagValue; import tools.utils.TagValueHolder; import tools.utils.ToolsConstants; import java.util.Arrays; import java.util.Map; -import java.util.Scanner; import java.util.stream.Collectors; import static com.google.common.base.Objects.firstNonNull; @@ -42,11 +41,6 @@ public class TranslationPageGenerator implements AutoToolTask { return "updateTranslations"; } - @Override - public void execute(Scanner scanner) { - executeDefault(); - } - @Override public void executeDefault() { NestedTagValue translationValuesHolder = new NestedTagValue(); @@ -63,7 +57,8 @@ public class TranslationPageGenerator implements AutoToolTask { } TagValueHolder tags = TagValueHolder.create().put("languages", translationValuesHolder); - FileUtils.generateFileFromTemplate(TEMPLATE_FILE, DOCS_PAGE, tags); + FileIoUtils.generateFileFromTemplate(TEMPLATE_FILE, DOCS_PAGE, tags); + System.out.println("Wrote to '" + DOCS_PAGE + "'"); } /** diff --git a/src/test/java/tools/docs/translations/translations.tpl.md b/src/test/java/tools/docs/translations/translations.tpl.md index 199d4029c..5308b6554 100644 --- a/src/test/java/tools/docs/translations/translations.tpl.md +++ b/src/test/java/tools/docs/translations/translations.tpl.md @@ -1,5 +1,5 @@ - + # AuthMe Translations The following translations are available in AuthMe. Set `messagesLanguage` to the language code diff --git a/src/test/java/tools/filegeneration/GeneratePluginYml.java b/src/test/java/tools/filegeneration/GeneratePluginYml.java new file mode 100644 index 000000000..b4a017c62 --- /dev/null +++ b/src/test/java/tools/filegeneration/GeneratePluginYml.java @@ -0,0 +1,176 @@ +package tools.filegeneration; + +import com.google.common.collect.ImmutableMap; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandInitializer; +import fr.xephi.authme.command.CommandUtils; +import fr.xephi.authme.permission.DefaultPermission; +import fr.xephi.authme.permission.PermissionNode; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import tools.docs.permissions.PermissionNodesGatherer; +import tools.utils.AutoToolTask; +import tools.utils.FileIoUtils; +import tools.utils.ToolsConstants; + +import java.io.StringReader; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +/** + * Generates the command and permission section of plugin.yml. + */ +public class GeneratePluginYml implements AutoToolTask { + + private static final String PLUGIN_YML_FILE = ToolsConstants.MAIN_RESOURCES_ROOT + "plugin.yml"; + + private static final Map WILDCARD_PERMISSIONS = ImmutableMap.of( + "authme.player.*", "Gives access to all player commands", + "authme.admin.*", "Gives access to all admin commands", + "authme.player.email", "Gives access to all email commands"); + + private List permissionNodes; + + private String pluginYmlStart; + + @Override + public void executeDefault() { + FileConfiguration configuration = loadPartialPluginYmlFile(); + + configuration.set("commands", generateCommands()); + configuration.set("permissions", generatePermissions()); + + FileIoUtils.writeToFile(PLUGIN_YML_FILE, + pluginYmlStart + "\n" + configuration.saveToString()); + } + + @Override + public String getTaskName() { + return "generatePluginYml"; + } + + /** + * Because some parts above the commands section have placeholders that aren't valid YAML, we need + * to split the contents into an upper part that we ignore and a lower part we load as YAML. When + * saving we prepend the YAML export with the stripped off part of the file again. + * + * @return file configuration with the lower part of the plugin.yml file + */ + private FileConfiguration loadPartialPluginYmlFile() { + List pluginYmlLines = FileIoUtils.readLinesFromFile(Paths.get(PLUGIN_YML_FILE)); + int lineNr = 0; + for (String line : pluginYmlLines) { + if (line.equals("commands:")) { + break; + } + ++lineNr; + } + if (lineNr == pluginYmlLines.size()) { + throw new IllegalStateException("Could not find line starting 'commands:' section"); + } + pluginYmlStart = String.join("\n", pluginYmlLines.subList(0, lineNr)); + String yamlContents = String.join("\n", pluginYmlLines.subList(lineNr, pluginYmlLines.size())); + return YamlConfiguration.loadConfiguration(new StringReader(yamlContents)); + } + + private static Map generateCommands() { + Collection commands = new CommandInitializer().getCommands(); + Map entries = new LinkedHashMap<>(); + for (CommandDescription command : commands) { + entries.put(command.getLabels().get(0), buildCommandEntry(command)); + } + return entries; + } + + private Map generatePermissions() { + PermissionNodesGatherer gatherer = new PermissionNodesGatherer(); + Map permissionDescriptions = gatherer.gatherNodesWithJavaDoc(); + + permissionNodes = gatherer.getPermissionClasses().stream() + // Note ljacqu 20161023: The compiler fails if we use method references below + .map(clz -> clz.getEnumConstants()) + .flatMap((PermissionNode[] nodes) -> Arrays.stream(nodes)) + .collect(Collectors.toList()); + + Map descriptions = new TreeMap<>(); + for (PermissionNode node : permissionNodes) { + descriptions.put(node.getNode(), buildPermissionEntry(node, permissionDescriptions.get(node.getNode()))); + } + addWildcardPermissions(descriptions); + return descriptions; + } + + private void addWildcardPermissions(Map permissions) { + for (Map.Entry entry : WILDCARD_PERMISSIONS.entrySet()) { + permissions.put(entry.getKey(), + buildWildcardPermissionEntry(entry.getValue(), gatherChildren(entry.getKey()))); + } + } + + private Map gatherChildren(String parentNode) { + String parentPath = parentNode.replaceAll("\\.\\*$", ""); + + Map children = new TreeMap<>(); + for (PermissionNode node : permissionNodes) { + if (node.getNode().startsWith(parentPath)) { + children.put(node.getNode(), Boolean.TRUE); + } + } + return children; + } + + private static Map buildCommandEntry(CommandDescription command) { + if (command.getLabels().size() > 1) { + return ImmutableMap.of( + "description", command.getDescription(), + "usage", buildUsage(command), + "aliases", command.getLabels().subList(1, command.getLabels().size())); + } else { + return ImmutableMap.of( + "description", command.getDescription(), + "usage", buildUsage(command)); + } + } + + private static String buildUsage(CommandDescription command) { + if (!command.getArguments().isEmpty()) { + return CommandUtils.buildSyntax(command); + } + final String commandStart = "/" + command.getLabels().get(0); + String usage = commandStart + " " + command.getChildren() + .stream() + .filter(cmd -> !cmd.getLabels().contains("help")) + .map(cmd -> cmd.getLabels().get(0)) + .collect(Collectors.joining("|")); + return usage.trim(); + } + + private static Map buildPermissionEntry(PermissionNode permissionNode, String description) { + return ImmutableMap.of( + "description", description, + "default", convertDefaultPermission(permissionNode.getDefaultPermission())); + } + + private static Map buildWildcardPermissionEntry(String description, Map children) { + return ImmutableMap.of( + "description", description, + "children", children); + } + + private static Object convertDefaultPermission(DefaultPermission defaultPermission) { + switch (defaultPermission) { + // Returning true/false as booleans will make SnakeYAML avoid using quotes + case ALLOWED: return true; + case NOT_ALLOWED: return false; + case OP_ONLY: return "op"; + default: + throw new IllegalArgumentException("Unknown default permission '" + defaultPermission + "'"); + } + } +} diff --git a/src/test/java/tools/helptranslation/HelpTranslationVerifier.java b/src/test/java/tools/helptranslation/HelpTranslationVerifier.java new file mode 100644 index 000000000..9cbf12b5c --- /dev/null +++ b/src/test/java/tools/helptranslation/HelpTranslationVerifier.java @@ -0,0 +1,171 @@ +package tools.helptranslation; + +import com.google.common.collect.Sets; +import de.bananaco.bpermissions.imp.YamlConfiguration; +import fr.xephi.authme.command.CommandDescription; +import fr.xephi.authme.command.CommandInitializer; +import fr.xephi.authme.command.CommandUtils; +import fr.xephi.authme.command.help.HelpMessage; +import fr.xephi.authme.command.help.HelpSection; +import org.bukkit.configuration.MemorySection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.google.common.collect.Lists.newArrayList; + +/** + * Verifies a help messages translation. + */ +public class HelpTranslationVerifier { + + private final FileConfiguration configuration; + + // missing and unknown HelpSection and HelpMessage entries + private final List missingSections = new ArrayList<>(); + private final List unknownSections = new ArrayList<>(); + // missing and unknown command entries + private final List missingCommands = new ArrayList<>(); + private final List unknownCommands = new ArrayList<>(); + + public HelpTranslationVerifier(File translation) { + this.configuration = YamlConfiguration.loadConfiguration(translation); + checkFile(); + } + + private void checkFile() { + checkHelpSections(); + checkCommands(); + } + + public List getMissingSections() { + return missingSections; + } + + public List getUnknownSections() { + return unknownSections; + } + + public List getMissingCommands() { + // All entries start with "command.", so remove that + return missingCommands.stream() + .map(s -> s.substring(9)).collect(Collectors.toList()); + } + + public List getUnknownCommands() { + // All entries start with "command.", so remove that + return unknownCommands.stream() + .map(s -> s.substring(9)).collect(Collectors.toList()); + } + + /** + * Verifies that the file has the expected entries for {@link HelpSection} and {@link HelpMessage}. + */ + private void checkHelpSections() { + Set knownSections = Arrays.stream(HelpSection.values()) + .map(HelpSection::getKey).collect(Collectors.toSet()); + knownSections.addAll(Arrays.stream(HelpMessage.values()).map(HelpMessage::getKey).collect(Collectors.toSet())); + knownSections.addAll(Arrays.asList("common.defaultPermissions.notAllowed", + "common.defaultPermissions.opOnly", "common.defaultPermissions.allowed")); + Set sectionKeys = getLeafKeys("section"); + sectionKeys.addAll(getLeafKeys("common")); + + if (sectionKeys.isEmpty()) { + missingSections.addAll(knownSections); + } else { + missingSections.addAll(Sets.difference(knownSections, sectionKeys)); + unknownSections.addAll(Sets.difference(sectionKeys, knownSections)); + } + } + + /** + * Verifies that the file has the expected entries for AuthMe commands. + */ + private void checkCommands() { + Set commandPaths = buildCommandPaths(); + Set existingKeys = getLeafKeys("commands"); + if (existingKeys.isEmpty()) { + missingCommands.addAll(commandPaths); // commandPaths should be empty in this case + } else { + missingCommands.addAll(Sets.difference(commandPaths, existingKeys)); + unknownCommands.addAll(Sets.difference(existingKeys, commandPaths)); + } + } + + private Set buildCommandPaths() { + Set commandPaths = new LinkedHashSet<>(); + for (CommandDescription command : new CommandInitializer().getCommands()) { + commandPaths.addAll(getYamlPaths(command)); + command.getChildren().forEach(child -> commandPaths.addAll(getYamlPaths(child))); + } + return commandPaths; + } + + private List getYamlPaths(CommandDescription command) { + // e.g. commands.authme.register + String commandPath = "commands." + CommandUtils.constructParentList(command).stream() + .map(cmd -> cmd.getLabels().get(0)) + .collect(Collectors.joining(".")); + // The entire command is not present, so just add it as a missing command and don't return any YAML path + if (!configuration.contains(commandPath)) { + missingCommands.add(commandPath); + return Collections.emptyList(); + } + + // Entries each command can have + List paths = newArrayList(commandPath + ".description", commandPath + ".detailedDescription"); + + // Add argument entries that may exist + for (int argIndex = 1; argIndex <= command.getArguments().size(); ++argIndex) { + String argPath = String.format("%s.arg%d", commandPath, argIndex); + paths.add(argPath + ".label"); + paths.add(argPath + ".description"); + } + return paths; + } + + /** + * Returns the leaf keys of the section at the given path of the file configuration. + * + * @param path the path whose leaf keys should be retrieved + * @return leaf keys of the memory section, + * empty set if the configuration does not have a memory section at the given path + */ + private Set getLeafKeys(String path) { + if (!(configuration.get(path) instanceof MemorySection)) { + return Collections.emptySet(); + } + MemorySection memorySection = (MemorySection) configuration.get(path); + + // MemorySection#getKeys(true) returns all keys on all levels, e.g. if the configuration has + // 'commands.authme.register' then it also has 'commands.authme' and 'commands'. We can traverse each node and + // build its parents (e.g. for commands.authme.register.description: commands.authme.register, commands.authme, + // and commands, which we can remove from the collection since we know they are not a leaf. + Set leafKeys = memorySection.getKeys(true); + Set allKeys = new HashSet<>(leafKeys); + + for (String key : allKeys) { + List pathParts = Arrays.asList(key.split("\\.")); + + // We perform construction of parents & their removal in reverse order so we can build the lowest-level + // parent of a node first. As soon as the parent doesn't exist in the set already, we know we can continue + // with the next node since another node has already removed the concerned parents. + for (int i = pathParts.size() - 1; i > 0; --i) { + // e.g. for commands.authme.register -> i = {2, 1} => {commands.authme, commands} + String parentPath = String.join(".", pathParts.subList(0, i)); + if (!leafKeys.remove(parentPath)) { + break; + } + } + } + return leafKeys.stream().map(leaf -> path + "." + leaf).collect(Collectors.toSet()); + } +} diff --git a/src/test/java/tools/helptranslation/VerifyHelpTranslations.java b/src/test/java/tools/helptranslation/VerifyHelpTranslations.java new file mode 100644 index 000000000..f97447bf9 --- /dev/null +++ b/src/test/java/tools/helptranslation/VerifyHelpTranslations.java @@ -0,0 +1,72 @@ +package tools.helptranslation; + +import tools.utils.ToolTask; +import tools.utils.ToolsConstants; + +import java.io.File; +import java.util.Arrays; +import java.util.List; +import java.util.Scanner; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Verifies the help translations for validity and completeness. + */ +public class VerifyHelpTranslations implements ToolTask { + + private static final Pattern HELP_MESSAGE_PATTERN = Pattern.compile("help_[a-z]{2,7}\\.yml"); + private static final String FOLDER = ToolsConstants.MAIN_RESOURCES_ROOT + "messages/"; + + @Override + public String getTaskName() { + return "verifyHelpTranslations"; + } + + @Override + public void execute(Scanner scanner) { + System.out.println("Check specific language file?"); + System.out.println("Enter the language code for a specific file (e.g. 'it' for help_it.yml)"); + System.out.println("Empty line will check all files in the resources messages folder (default)"); + + String language = scanner.nextLine(); + if (language.isEmpty()) { + getHelpTranslations().forEach(this::processFile); + } else { + processFile(new File(FOLDER, "help_" + language + ".yml")); + } + } + + private void processFile(File file) { + System.out.println("Checking '" + file.getName() + "'"); + HelpTranslationVerifier verifier = new HelpTranslationVerifier(file); + + // Check and output errors + if (!verifier.getMissingSections().isEmpty()) { + System.out.println("Missing sections: " + String.join(", ", verifier.getMissingSections())); + } + if (!verifier.getUnknownSections().isEmpty()) { + System.out.println("Unknown sections: " + String.join(", ", verifier.getUnknownSections())); + } + if (!verifier.getMissingCommands().isEmpty()) { + System.out.println("Missing command entries: " + String.join(", ", verifier.getMissingCommands())); + } + if (!verifier.getUnknownCommands().isEmpty()) { + System.out.println("Unknown command entries: " + String.join(", ", verifier.getUnknownCommands())); + } + } + + private static List getHelpTranslations() { + File[] files = new File(FOLDER).listFiles(); + if (files == null) { + throw new IllegalStateException("Could not get files from '" + FOLDER + "'"); + } + List helpFiles = Arrays.stream(files) + .filter(file -> HELP_MESSAGE_PATTERN.matcher(file.getName()).matches()) + .collect(Collectors.toList()); + if (helpFiles.isEmpty()) { + throw new IllegalStateException("Could not get any matching files!"); + } + return helpFiles; + } +} diff --git a/src/test/java/tools/messages/MessageFileVerifier.java b/src/test/java/tools/messages/MessageFileVerifier.java index f0be6bb44..b151228c1 100644 --- a/src/test/java/tools/messages/MessageFileVerifier.java +++ b/src/test/java/tools/messages/MessageFileVerifier.java @@ -8,7 +8,7 @@ import com.google.common.collect.Multimap; import fr.xephi.authme.message.MessageKey; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import java.io.File; import java.util.ArrayList; @@ -104,7 +104,7 @@ public class MessageFileVerifier { * @param defaultMessages The collection of default messages */ public void addMissingKeys(FileConfiguration defaultMessages) { - final List fileLines = FileUtils.readLinesFromFile(messagesFile.toPath()); + final List fileLines = FileIoUtils.readLinesFromFile(messagesFile.toPath()); List keysToAdd = new ArrayList<>(); for (MissingKey entry : missingKeys) { @@ -135,7 +135,7 @@ public class MessageFileVerifier { addCommentForMissingTags(fileLines, key, entry.getValue()); } - FileUtils.writeToFile(messagesFile.toPath(), String.join("\n", fileLines)); + FileIoUtils.writeToFile(messagesFile.toPath(), String.join("\n", fileLines)); } /** diff --git a/src/test/java/tools/messages/translation/ImportMessagesTask.java b/src/test/java/tools/messages/translation/ImportMessagesTask.java index c70caaa3a..765c49f1f 100644 --- a/src/test/java/tools/messages/translation/ImportMessagesTask.java +++ b/src/test/java/tools/messages/translation/ImportMessagesTask.java @@ -7,7 +7,7 @@ import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import tools.messages.MessageFileVerifier; import tools.messages.VerifyMessagesTask; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.ToolTask; import tools.utils.ToolsConstants; @@ -108,9 +108,9 @@ public class ImportMessagesTask implements ToolTask { * @param file The file whose to-do comments should be removed */ private static void removeAllTodoComments(String file) { - String contents = FileUtils.readFromFile(file); + String contents = FileIoUtils.readFromFile(file); String regex = "^# TODO .*$"; contents = Pattern.compile(regex, Pattern.MULTILINE).matcher(contents).replaceAll(""); - FileUtils.writeToFile(file, contents); + FileIoUtils.writeToFile(file, contents); } } diff --git a/src/test/java/tools/messages/translation/WriteAllExportsTask.java b/src/test/java/tools/messages/translation/WriteAllExportsTask.java index f5f3825bc..f0faf3b03 100644 --- a/src/test/java/tools/messages/translation/WriteAllExportsTask.java +++ b/src/test/java/tools/messages/translation/WriteAllExportsTask.java @@ -2,7 +2,7 @@ package tools.messages.translation; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import tools.utils.FileUtils; +import tools.utils.FileIoUtils; import tools.utils.ToolsConstants; import java.io.File; @@ -31,7 +31,7 @@ public class WriteAllExportsTask extends ExportMessagesTask { for (File file : messageFiles) { String code = file.getName().substring("messages_".length(), file.getName().length() - ".yml".length()); String json = convertToJson(code, defaultMessages, YamlConfiguration.loadConfiguration(file)); - FileUtils.writeToFile(OUTPUT_FOLDER + "messages_" + code + ".json", json); + FileIoUtils.writeToFile(OUTPUT_FOLDER + "messages_" + code + ".json", json); } } } diff --git a/src/test/java/tools/utils/AutoToolTask.java b/src/test/java/tools/utils/AutoToolTask.java index b635fe5fe..61b909a52 100644 --- a/src/test/java/tools/utils/AutoToolTask.java +++ b/src/test/java/tools/utils/AutoToolTask.java @@ -1,5 +1,7 @@ package tools.utils; +import java.util.Scanner; + /** * Interface for tasks that can be run automatically, i.e. without any user input. */ @@ -10,4 +12,9 @@ public interface AutoToolTask extends ToolTask { */ void executeDefault(); + @Override + default void execute(Scanner scanner) { + executeDefault(); + } + } diff --git a/src/test/java/tools/utils/FileUtils.java b/src/test/java/tools/utils/FileIoUtils.java similarity index 86% rename from src/test/java/tools/utils/FileUtils.java rename to src/test/java/tools/utils/FileIoUtils.java index 44bf5a296..4683633f9 100644 --- a/src/test/java/tools/utils/FileUtils.java +++ b/src/test/java/tools/utils/FileIoUtils.java @@ -9,11 +9,11 @@ import java.nio.file.StandardOpenOption; import java.util.List; /** - * Utility class for reading from and writing to files. + * Utility class for I/O operations on files. */ -public final class FileUtils { +public final class FileIoUtils { - private FileUtils() { + private FileIoUtils() { } public static void generateFileFromTemplate(String templateFile, String destinationFile, TagValueHolder tags) { @@ -43,8 +43,12 @@ public final class FileUtils { } public static String readFromFile(String file) { + return readFromFile(Paths.get(file)); + } + + public static String readFromFile(Path file) { try { - return new String(Files.readAllBytes(Paths.get(file)), StandardCharsets.UTF_8); + return new String(Files.readAllBytes(file), StandardCharsets.UTF_8); } catch (IOException e) { throw new UnsupportedOperationException("Could not read from file '" + file + "'", e); } diff --git a/src/test/java/tools/utils/ToolsConstants.java b/src/test/java/tools/utils/ToolsConstants.java index 5986c378b..e7ba3e05a 100644 --- a/src/test/java/tools/utils/ToolsConstants.java +++ b/src/test/java/tools/utils/ToolsConstants.java @@ -9,9 +9,6 @@ public final class ToolsConstants { public static final String MAIN_RESOURCES_ROOT = "src/main/resources/"; - // Add specific `fr.xephi.authme` package as not to include the tool tasks in the `tools` package - public static final String TEST_SOURCE_ROOT = "src/test/java/fr/xephi/authme"; - public static final String TOOLS_SOURCE_ROOT = "src/test/java/tools/"; public static final String DOCS_FOLDER = "docs/";