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 ## Upcoming Changes
* API: Added hooks for item, projectile and tile bans (@deadsurgeon42) * 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) * 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 saving when one player is one the server and another one joins (@MarioE)
* Fixed /spawnmob not spawning negative IDs (@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) * 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) * 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 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 ## 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 OpenTerraria API to 1.3.5.3 (@DeathCradle)
* Updated Terraria Server API to 1.3.5.3 (@WhiteXZ, @hakusaro) * Updated Terraria Server API to 1.3.5.3 (@WhiteXZ, @hakusaro)
* Updated TShock core components to 1.3.5.3 (@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.", "add <name> <permissions...> - Adds a new group.",
"addperm <group> <permissions...> - Adds permissions to a group.", "addperm <group> <permissions...> - Adds permissions to a group.",
"color <group> <rrr,ggg,bbb> - Changes a group's chat color.", "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.", "del <group> - Deletes a group.",
"delperm <group> <permissions...> - Removes permissions from a group.", "delperm <group> <permissions...> - Removes permissions from a group.",
"list [page] - Lists groups.", "list [page] - Lists groups.",
@ -3074,6 +3075,29 @@ namespace TShockAPI
} }
#endregion #endregion
return; 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": case "del":
#region Delete group #region Delete group
{ {

View file

@ -26,6 +26,9 @@ using MySql.Data.MySqlClient;
namespace TShockAPI.DB namespace TShockAPI.DB
{ {
/// <summary>
/// Represents the GroupManager, which is in charge of group management.
/// </summary>
public class GroupManager : IEnumerable<Group> public class GroupManager : IEnumerable<Group>
{ {
private IDbConnection database; private IDbConnection database;
@ -36,17 +39,17 @@ namespace TShockAPI.DB
database = db; database = db;
var table = new SqlTable("GroupList", var table = new SqlTable("GroupList",
new SqlColumn("GroupName", MySqlDbType.VarChar, 32) {Primary = true}, new SqlColumn("GroupName", MySqlDbType.VarChar, 32) { Primary = true },
new SqlColumn("Parent", MySqlDbType.VarChar, 32), new SqlColumn("Parent", MySqlDbType.VarChar, 32),
new SqlColumn("Commands", MySqlDbType.Text), new SqlColumn("Commands", MySqlDbType.Text),
new SqlColumn("ChatColor", MySqlDbType.Text), new SqlColumn("ChatColor", MySqlDbType.Text),
new SqlColumn("Prefix", MySqlDbType.Text), new SqlColumn("Prefix", MySqlDbType.Text),
new SqlColumn("Suffix", MySqlDbType.Text) new SqlColumn("Suffix", MySqlDbType.Text)
); );
var creator = new SqlTableCreator(db, var creator = new SqlTableCreator(db,
db.GetSqlType() == SqlType.Sqlite db.GetSqlType() == SqlType.Sqlite
? (IQueryBuilder) new SqliteQueryCreator() ? (IQueryBuilder)new SqliteQueryCreator()
: new MysqlQueryCreator()); : new MysqlQueryCreator());
if (creator.EnsureTableStructure(table)) if (creator.EnsureTableStructure(table))
{ {
// Add default groups if they don't exist // Add default groups if they don't exist
@ -85,7 +88,11 @@ namespace TShockAPI.DB
AddGroup(name, parent, permissions, Group.defaultChatColor); 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) public bool GroupExists(string group)
{ {
if (group == "superadmin") if (group == "superadmin")
@ -99,11 +106,20 @@ namespace TShockAPI.DB
return GetEnumerator(); return GetEnumerator();
} }
/// <summary>
/// Gets the enumerator.
/// </summary>
/// <returns>The enumerator.</returns>
public IEnumerator<Group> GetEnumerator() public IEnumerator<Group> GetEnumerator()
{ {
return groups.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) public Group GetGroupByName(string name)
{ {
var ret = groups.Where(g => g.Name == name); var ret = groups.Where(g => g.Name == name);
@ -139,8 +155,8 @@ namespace TShockAPI.DB
} }
string query = (TShock.Config.StorageType.ToLower() == "sqlite") string query = (TShock.Config.StorageType.ToLower() == "sqlite")
? "INSERT OR IGNORE INTO GroupList (GroupName, Parent, Commands, ChatColor) VALUES (@0, @1, @2, @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"; : "INSERT IGNORE INTO GroupList SET GroupName=@0, Parent=@1, Commands=@2, ChatColor=@3";
if (database.Query(query, name, parentname, permissions, chatcolor) == 1) if (database.Query(query, name, parentname, permissions, chatcolor) == 1)
{ {
groups.Add(group); groups.Add(group);
@ -200,6 +216,70 @@ namespace TShockAPI.DB
group.Suffix = suffix; 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) public String DeleteGroup(String name, bool exceptions = false)
{ {
if (!GroupExists(name)) if (!GroupExists(name))
@ -214,12 +294,18 @@ namespace TShockAPI.DB
groups.Remove(TShock.Utils.GetGroup(name)); groups.Remove(TShock.Utils.GetGroup(name));
return "Group " + name + " has been deleted successfully."; 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) public String AddPermissions(String name, List<String> permissions)
{ {
if (!GroupExists(name)) if (!GroupExists(name))
@ -237,6 +323,12 @@ namespace TShockAPI.DB
return ""; 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) public String DeletePermissions(String name, List<String> permissions)
{ {
if (!GroupExists(name)) if (!GroupExists(name))
@ -254,12 +346,15 @@ namespace TShockAPI.DB
return ""; return "";
} }
/// <summary>
/// Enumerates the group list and loads permissions for each group appropriately.
/// </summary>
public void LoadPermisions() public void LoadPermisions()
{ {
try try
{ {
List<Group> newGroups = new List<Group>(groups.Count); 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")) using (var reader = database.QueryReader("SELECT * FROM GroupList"))
{ {
while (reader.Read()) while (reader.Read())
@ -271,7 +366,8 @@ namespace TShockAPI.DB
continue; 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"), Prefix = reader.Get<string>("Prefix"),
Suffix = reader.Get<string>("Suffix"), Suffix = reader.Get<string>("Suffix"),
}); });
@ -360,32 +456,60 @@ namespace TShockAPI.DB
} }
} }
/// <summary>
/// Represents the base GroupManager exception.
/// </summary>
[Serializable] [Serializable]
public class GroupManagerException : Exception 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) public GroupManagerException(string message)
: base(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) public GroupManagerException(string message, Exception inner)
: base(message, 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] [Serializable]
public class GroupExistsException : GroupManagerException 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) public GroupExistsException(string name)
: base("Group '" + name + "' already exists") : 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] [Serializable]
public class GroupNotExistException : GroupManagerException 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) public GroupNotExistException(string name)
: base("Group '" + name + "' does not exist") : base("Group '" + name + "' does not exist")
{ {