Added support for renaming groups. Fixes #1420

This commit is contained in:
ProfessorXZ 2017-09-21 10:51:37 +02:00
parent 623815b9f6
commit 02806a2429
3 changed files with 168 additions and 19 deletions

View file

@ -4,6 +4,7 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
## Upcoming Changes
* API: Added hooks for item, projectile and tile bans (@deadsurgeon42)
* API: Changed `PlayerHooks` permission hook mechanisms to allow negation from hooks (@deadsurgeon42)
* API: New WorldGrassSpread hook which shold allow corruption/crimson/hallow creep config options to work (@DeathCradle)
* Fixed saving when one player is one the server and another one joins (@MarioE)
* Fixed /spawnmob not spawning negative IDs (@MarioE)
@ -11,9 +12,9 @@ This is the rolling changelog for TShock for Terraria. Use past tense when addin
* Updated to new stat tracking system with more data so we can actually make informed software decisions (Jordan Coulam)
* Fixed /time display at the end of Terraria hours (@koneko-nyan)
* Added a warning notifying users of the minimum memory required to run TShock (@bartico6)
* Added /group rename to allow changing group names (@ColinBohn, @ProfessorXZ)
## TShock 4.3.24
* API: Changed `PlayerHooks` permission hook mechanisms to allow negation from hooks (@deadsurgeon42)
* Updated OpenTerraria API to 1.3.5.3 (@DeathCradle)
* Updated Terraria Server API to 1.3.5.3 (@WhiteXZ, @hakusaro)
* Updated TShock core components to 1.3.5.3 (@hakusaro)

View file

@ -2865,6 +2865,7 @@ namespace TShockAPI
"add <name> <permissions...> - Adds a new group.",
"addperm <group> <permissions...> - Adds permissions to a group.",
"color <group> <rrr,ggg,bbb> - Changes a group's chat color.",
"rename <group> <new name> - Changes a group's name.",
"del <group> - Deletes a group.",
"delperm <group> <permissions...> - Removes permissions from a group.",
"list [page] - Lists groups.",
@ -3074,6 +3075,29 @@ namespace TShockAPI
}
#endregion
return;
case "rename":
#region Rename group
{
if (args.Parameters.Count != 3)
{
args.Player.SendErrorMessage("Invalid syntax! Proper syntax: {0}group rename <group> <new name>", Specifier);
return;
}
string group = args.Parameters[1];
string newName = args.Parameters[2];
try
{
string response = TShock.Groups.RenameGroup(group, newName);
args.Player.SendSuccessMessage(response);
}
catch (GroupManagerException ex)
{
args.Player.SendErrorMessage(ex.Message);
}
}
#endregion
return;
case "del":
#region Delete group
{

View file

@ -26,6 +26,9 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB
{
/// <summary>
/// Represents the GroupManager, which is in charge of group management.
/// </summary>
public class GroupManager : IEnumerable<Group>
{
private IDbConnection database;
@ -36,17 +39,17 @@ namespace TShockAPI.DB
database = db;
var table = new SqlTable("GroupList",
new SqlColumn("GroupName", MySqlDbType.VarChar, 32) {Primary = true},
new SqlColumn("Parent", MySqlDbType.VarChar, 32),
new SqlColumn("Commands", MySqlDbType.Text),
new SqlColumn("ChatColor", MySqlDbType.Text),
new SqlColumn("Prefix", MySqlDbType.Text),
new SqlColumn("Suffix", MySqlDbType.Text)
);
new SqlColumn("GroupName", MySqlDbType.VarChar, 32) { Primary = true },
new SqlColumn("Parent", MySqlDbType.VarChar, 32),
new SqlColumn("Commands", MySqlDbType.Text),
new SqlColumn("ChatColor", MySqlDbType.Text),
new SqlColumn("Prefix", MySqlDbType.Text),
new SqlColumn("Suffix", MySqlDbType.Text)
);
var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator()
: new MysqlQueryCreator());
db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder)new SqliteQueryCreator()
: new MysqlQueryCreator());
if (creator.EnsureTableStructure(table))
{
// Add default groups if they don't exist
@ -85,7 +88,11 @@ namespace TShockAPI.DB
AddGroup(name, parent, permissions, Group.defaultChatColor);
}
/// <summary>
/// Determines whether the given group exists.
/// </summary>
/// <param name="group">The group.</param>
/// <returns><c>true</c> if it does; otherwise, <c>false</c>.</returns>
public bool GroupExists(string group)
{
if (group == "superadmin")
@ -99,11 +106,20 @@ namespace TShockAPI.DB
return GetEnumerator();
}
/// <summary>
/// Gets the enumerator.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<Group> GetEnumerator()
{
return groups.GetEnumerator();
}
/// <summary>
/// Gets the group matching the specified name.
/// </summary>
/// <param name="name">The name.</param>
/// <returns>The group.</returns>
public Group GetGroupByName(string name)
{
var ret = groups.Where(g => g.Name == name);
@ -139,8 +155,8 @@ namespace TShockAPI.DB
}
string query = (TShock.Config.StorageType.ToLower() == "sqlite")
? "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);"
: "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3";
? "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @3);"
: "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3";
if (database.Query(query, name, parentname, permissions, chatcolor) == 1)
{
groups.Add(group);
@ -200,6 +216,70 @@ namespace TShockAPI.DB
group.Suffix = suffix;
}
/// <summary>
/// Renames the specified group.
/// </summary>
/// <param name="name">The group's name.</param>
/// <param name="newName">The new name.</param>
/// <returns>The response.</returns>
public String RenameGroup(string name, string newName)
{
if (!GroupExists(name))
{
throw new GroupNotExistException(name);
}
if (GroupExists(newName))
{
throw new GroupExistsException(newName);
}
if (database.Query("UPDATE GroupList SET GroupName = @0 WHERE GroupName = @1", newName, name) == 1)
{
var oldGroup = GetGroupByName(name);
var newGroup = new Group(newName, oldGroup.Parent, oldGroup.ChatColor, oldGroup.Permissions)
{
Prefix = oldGroup.Prefix,
Suffix = oldGroup.Suffix
};
groups.Remove(oldGroup);
groups.Add(newGroup);
// We need to check if the old group has been referenced as a parent and update those references accordingly
database.Query("UPDATE GroupList SET Parent = @0 WHERE Parent = @1", newName, name);
foreach (var group in groups.Where(g => g.Parent != null && g.Parent == oldGroup))
{
group.Parent = newGroup;
}
if (TShock.Config.DefaultGuestGroupName == oldGroup.Name)
{
TShock.Config.DefaultGuestGroupName = newGroup.Name;
Group.DefaultGroup = newGroup;
}
if (TShock.Config.DefaultRegistrationGroupName == oldGroup.Name)
{
TShock.Config.DefaultRegistrationGroupName = newGroup.Name;
}
TShock.Config.Write(FileTools.ConfigPath);
database.Query("UPDATE Users SET Usergroup = @0 WHERE Usergroup = @1", newName, name);
foreach (var player in TShock.Players.Where(p => p?.Group == oldGroup))
{
player.Group = newGroup;
}
return $"Group \"{name}\" has been renamed to \"{newName}\".";
}
throw new GroupManagerException($"Failed to rename group \"{name}\".");
}
/// <summary>
/// Deletes the specified group.
/// </summary>
/// <param name="name">The group's name.</param>
/// <param name="exceptions">Whether exceptions will be thrown in case something goes wrong.</param>
/// <returns></returns>
public String DeleteGroup(String name, bool exceptions = false)
{
if (!GroupExists(name))
@ -214,12 +294,18 @@ namespace TShockAPI.DB
groups.Remove(TShock.Utils.GetGroup(name));
return "Group " + name + " has been deleted successfully.";
}
else if (exceptions)
throw new GroupManagerException("Failed to delete group '" + name + ".'");
return "";
if (exceptions)
throw new GroupManagerException("Failed to delete group '" + name + ".'");
return "Failed to delete group '" + name + ".'";
}
/// <summary>
/// Enumerates the given permission list and adds permissions for the specified group accordingly.
/// </summary>
/// <param name="name">The group name.</param>
/// <param name="permissions">The permission list.</param>
/// <returns></returns>
public String AddPermissions(String name, List<String> permissions)
{
if (!GroupExists(name))
@ -237,6 +323,12 @@ namespace TShockAPI.DB
return "";
}
/// <summary>
/// Enumerates the given permission list and removes valid permissions for the specified group accordingly.
/// </summary>
/// <param name="name">The group name.</param>
/// <param name="permissions">The permission list.</param>
/// <returns></returns>
public String DeletePermissions(String name, List<String> permissions)
{
if (!GroupExists(name))
@ -254,12 +346,15 @@ namespace TShockAPI.DB
return "";
}
/// <summary>
/// Enumerates the group list and loads permissions for each group appropriately.
/// </summary>
public void LoadPermisions()
{
try
{
List<Group> newGroups = new List<Group>(groups.Count);
Dictionary<string,string> newGroupParents = new Dictionary<string, string>(groups.Count);
Dictionary<string, string> newGroupParents = new Dictionary<string, string>(groups.Count);
using (var reader = database.QueryReader("SELECT * FROM GroupList"))
{
while (reader.Read())
@ -271,7 +366,8 @@ namespace TShockAPI.DB
continue;
}
newGroups.Add(new Group(groupName, null, reader.Get<string>("ChatColor"), reader.Get<string>("Commands")) {
newGroups.Add(new Group(groupName, null, reader.Get<string>("ChatColor"), reader.Get<string>("Commands"))
{
Prefix = reader.Get<string>("Prefix"),
Suffix = reader.Get<string>("Suffix"),
});
@ -360,32 +456,60 @@ namespace TShockAPI.DB
}
}
/// <summary>
/// Represents the base GroupManager exception.
/// </summary>
[Serializable]
public class GroupManagerException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="GroupManagerException"/> with the specified message.
/// </summary>
/// <param name="message">The message.</param>
public GroupManagerException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="GroupManagerException"/> with the specified message and inner exception.
/// </summary>
/// <param name="message">The message.</param>
/// <param name="inner">The inner exception.</param>
public GroupManagerException(string message, Exception inner)
: base(message, inner)
{
}
}
/// <summary>
/// Represents the GroupExists exception.
/// This exception is thrown whenever an attempt to add an existing group into the database is made.
/// </summary>
[Serializable]
public class GroupExistsException : GroupManagerException
{
/// <summary>
/// Initializes a new instance of the <see cref="GroupExistsException"/> with the specified group name.
/// </summary>
/// <param name="name">The group name.</param>
public GroupExistsException(string name)
: base("Group '" + name + "' already exists")
{
}
}
/// <summary>
/// Represents the GroupNotExist exception.
/// This exception is thrown whenever we try to access a group that does not exist.
/// </summary>
[Serializable]
public class GroupNotExistException : GroupManagerException
{
/// <summary>
/// Initializes a new instance of the <see cref="GroupNotExistException"/> with the specified group name.
/// </summary>
/// <param name="name">The group name.</param>
public GroupNotExistException(string name)
: base("Group '" + name + "' does not exist")
{