mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-22 12:05:51 -04:00
Validate perbuild and pervisit permissions for world-changing commands, and regardless of whether level is loaded or not. Fixes #309
This commit is contained in:
parent
89a8823981
commit
20a3aa6b90
@ -48,10 +48,7 @@ namespace MCGalaxy.Commands.Bots {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AddBot(Player p, string botName) {
|
void AddBot(Player p, string botName) {
|
||||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, p.level.name, "add bots to this level")) return;
|
||||||
Player.Message(p, "Hence, you cannot add bots to this map.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BotExists(p.level, botName)) {
|
if (BotExists(p.level, botName)) {
|
||||||
Player.Message(p, "A bot with that name already exists."); return;
|
Player.Message(p, "A bot with that name already exists."); return;
|
||||||
@ -77,10 +74,7 @@ namespace MCGalaxy.Commands.Bots {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RemoveBot(Player p, string botName) {
|
void RemoveBot(Player p, string botName) {
|
||||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, p.level.name, "remove bots from this level")) return;
|
||||||
Player.Message(p, "Hence, you cannot remove bots from this map.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (botName.CaselessEq("all")) {
|
if (botName.CaselessEq("all")) {
|
||||||
PlayerBot.RemoveLoadedBots(p.level, false);
|
PlayerBot.RemoveLoadedBots(p.level, false);
|
||||||
@ -97,6 +91,7 @@ namespace MCGalaxy.Commands.Bots {
|
|||||||
void SetBotText(Player p, string botName, string text) {
|
void SetBotText(Player p, string botName, string text) {
|
||||||
PlayerBot bot = Matcher.FindBots(p, botName);
|
PlayerBot bot = Matcher.FindBots(p, botName);
|
||||||
if (bot == null) return;
|
if (bot == null) return;
|
||||||
|
if (!LevelInfo.ValidateAction(p, p.level.name, "set bot text of that bot")) return;
|
||||||
|
|
||||||
if (text == null) {
|
if (text == null) {
|
||||||
Player.Message(p, "Removed text shown when bot {0} %Sclicked on", bot.ColoredName);
|
Player.Message(p, "Removed text shown when bot {0} %Sclicked on", bot.ColoredName);
|
||||||
|
@ -35,11 +35,7 @@ namespace MCGalaxy.Commands.Bots {
|
|||||||
string[] args = message.SplitSpaces();
|
string[] args = message.SplitSpaces();
|
||||||
PlayerBot bot = Matcher.FindBots(p, args[0]);
|
PlayerBot bot = Matcher.FindBots(p, args[0]);
|
||||||
if (bot == null) return;
|
if (bot == null) return;
|
||||||
|
if (!LevelInfo.ValidateAction(p, p.level.name, "change AI of bots in this level")) return;
|
||||||
if (p != null && !bot.level.BuildAccess.CheckDetailed(p)) {
|
|
||||||
Player.Message(p, "Hence, you cannot change the AI of bots on this map.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.Length == 1) {
|
if (args.Length == 1) {
|
||||||
bot.Instructions.Clear();
|
bot.Instructions.Clear();
|
||||||
|
@ -27,10 +27,7 @@ namespace MCGalaxy.Commands.Bots {
|
|||||||
|
|
||||||
public override void Use(Player p, string message) {
|
public override void Use(Player p, string message) {
|
||||||
if (message.Length == 0) { Help(p); return; }
|
if (message.Length == 0) { Help(p); return; }
|
||||||
if (!p.level.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, p.level.name, "summon that bot")) return;
|
||||||
Player.Message(p, "Hence, you cannot summon bots on this map.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerBot bot = Matcher.FindBots(p, message);
|
PlayerBot bot = Matcher.FindBots(p, message);
|
||||||
if (bot == null) return;
|
if (bot == null) return;
|
||||||
|
@ -68,6 +68,7 @@ namespace MCGalaxy.Commands.CPE {
|
|||||||
Player.Message(p, "Set server's default terrain to " + path);
|
Player.Message(p, "Set server's default terrain to " + path);
|
||||||
UpdateGlobally(p, false);
|
UpdateGlobally(p, false);
|
||||||
} else if (scope == "level") {
|
} else if (scope == "level") {
|
||||||
|
if (!LevelInfo.ValidateAction(p, p.level.name, "set texture of this level")) return;
|
||||||
p.level.Config.Terrain = url;
|
p.level.Config.Terrain = url;
|
||||||
Player.Message(p, "Set level's terrain to " + path);
|
Player.Message(p, "Set level's terrain to " + path);
|
||||||
UpdateLevel(p);
|
UpdateLevel(p);
|
||||||
@ -76,6 +77,7 @@ namespace MCGalaxy.Commands.CPE {
|
|||||||
Player.Message(p, "Set server's default texture pack to " + path);
|
Player.Message(p, "Set server's default texture pack to " + path);
|
||||||
UpdateGlobally(p, true);
|
UpdateGlobally(p, true);
|
||||||
} else if (scope == "levelzip") {
|
} else if (scope == "levelzip") {
|
||||||
|
if (!LevelInfo.ValidateAction(p, p.level.name, "set texture pack of this level")) return;
|
||||||
p.level.Config.TexturePack = url;
|
p.level.Config.TexturePack = url;
|
||||||
Player.Message(p, "Set level's texture pack to " + path);
|
Player.Message(p, "Set level's texture pack to " + path);
|
||||||
UpdateLevel(p);
|
UpdateLevel(p);
|
||||||
|
@ -33,10 +33,7 @@ namespace MCGalaxy.Commands.Chatting {
|
|||||||
public override void Use(Player p, string message) { UseBotOrPlayer(p, message, "color"); }
|
public override void Use(Player p, string message) { UseBotOrPlayer(p, message, "color"); }
|
||||||
|
|
||||||
protected override void SetBotData(Player p, PlayerBot bot, string colName) {
|
protected override void SetBotData(Player p, PlayerBot bot, string colName) {
|
||||||
if (p != null && !bot.level.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, bot.level.name, "change color of that bot")) return;
|
||||||
Player.Message(p, "Hence, you cannot change the color of that bot.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string color = colName.Length == 0 ? "&1" : Matcher.FindColor(p, colName);
|
string color = colName.Length == 0 ? "&1" : Matcher.FindColor(p, colName);
|
||||||
if (color == null) return;
|
if (color == null) return;
|
||||||
|
@ -35,10 +35,7 @@ namespace MCGalaxy.Commands {
|
|||||||
if (isBot) {
|
if (isBot) {
|
||||||
if (!CheckExtraPerm(p, 2)) return;
|
if (!CheckExtraPerm(p, 2)) return;
|
||||||
|
|
||||||
if (p != null && !bot.level.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, bot.level.name, "change the " + type + " of that bot")) return;
|
||||||
Player.Message(p, "Hence, you cannot change the " + type + " of that bot.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetBotData(p, bot, args.Length > 2 ? args[2] : "");
|
SetBotData(p, bot, args.Length > 2 ? args[2] : "");
|
||||||
} else {
|
} else {
|
||||||
if (p != who && !CheckExtraPerm(p, 1)) return;
|
if (p != who && !CheckExtraPerm(p, 1)) return;
|
||||||
|
@ -38,6 +38,7 @@ namespace MCGalaxy.Commands.Maintenance {
|
|||||||
lvl = Matcher.FindLevels(p, args[1]);
|
lvl = Matcher.FindLevels(p, args[1]);
|
||||||
if (lvl == null) return;
|
if (lvl == null) return;
|
||||||
}
|
}
|
||||||
|
if (!LevelInfo.ValidateAction(p, lvl.name, "change BlockDB state of this level")) return;
|
||||||
|
|
||||||
if (args[0].CaselessEq("clear")) {
|
if (args[0].CaselessEq("clear")) {
|
||||||
Player.Message(p, "Clearing &cALL %Sblock changes for {0}%S...", lvl.ColoredName);
|
Player.Message(p, "Clearing &cALL %Sblock changes for {0}%S...", lvl.ColoredName);
|
||||||
|
@ -50,6 +50,8 @@ namespace MCGalaxy.Commands.World {
|
|||||||
Player.Message(p, "Cannot use level scope from {0}.", src);
|
Player.Message(p, "Cannot use level scope from {0}.", src);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!LevelInfo.ValidateAction(p, p.level.name, "change block properties in this level")) return null;
|
||||||
return p.level.Props;
|
return p.level.Props;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,15 +37,18 @@ namespace MCGalaxy.Commands.World {
|
|||||||
if (args.Length < 2) {
|
if (args.Length < 2) {
|
||||||
Player.Message(p, "You did not specify the destination level name."); return;
|
Player.Message(p, "You did not specify the destination level name."); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string src = args[0], dst = args[1];
|
|
||||||
if (p != null && !p.group.CanExecute("newlvl")) {
|
if (p != null && !p.group.CanExecute("newlvl")) {
|
||||||
Player.Message(p, "You cannot use /copylvl as you cannot use /newlvl."); return;
|
Player.Message(p, "You cannot use /copylvl as you cannot use /newlvl."); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string src = args[0];
|
||||||
src = Matcher.FindMaps(p, src);
|
src = Matcher.FindMaps(p, src);
|
||||||
if (src == null) return;
|
if (src == null) return;
|
||||||
|
if (!LevelInfo.ValidateAction(p, src, "copy this level")) return;
|
||||||
|
|
||||||
|
string dst = args[1];
|
||||||
if (!Formatter.ValidName(p, dst, "level")) return;
|
if (!Formatter.ValidName(p, dst, "level")) return;
|
||||||
if (LevelInfo.MapExists(dst)) { Player.Message(p, "The level \"" + dst + "\" already exists."); return; }
|
if (LevelInfo.MapExists(dst)) { Player.Message(p, "Level \"" + dst + "\" already exists."); return; }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LevelActions.CopyLevel(src, dst);
|
LevelActions.CopyLevel(src, dst);
|
||||||
|
@ -36,25 +36,13 @@ namespace MCGalaxy.Commands.World {
|
|||||||
string map = Matcher.FindMaps(p, message);
|
string map = Matcher.FindMaps(p, message);
|
||||||
if (map == null) return;
|
if (map == null) return;
|
||||||
|
|
||||||
Level lvl = LevelInfo.FindExact(map);
|
if (map.CaselessEq(ServerConfig.MainLevel)) { Player.Message(p, "Cannot delete the main level."); return; }
|
||||||
if (lvl != null && p != null && !lvl.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, map, "delete this level")) return;
|
||||||
Player.Message(p, "Hence you cannot delete this level."); return;
|
|
||||||
}
|
|
||||||
if (lvl == Server.mainLevel) { Player.Message(p, "Cannot delete the main level."); return; }
|
|
||||||
|
|
||||||
LevelPermission perbuild = GetPerBuildPermission(map);
|
|
||||||
if (p != null && perbuild > p.Rank) {
|
|
||||||
Player.Message(p, "%cYou can't delete levels with a perbuild rank higher than yours!"); return;
|
|
||||||
}
|
|
||||||
Player.Message(p, "Created backup.");
|
Player.Message(p, "Created backup.");
|
||||||
LevelActions.Delete(map.ToLower());
|
LevelActions.Delete(map.ToLower());
|
||||||
}
|
}
|
||||||
|
|
||||||
LevelPermission GetPerBuildPermission(string level) {
|
|
||||||
string value = LevelInfo.FindOfflineProperty(level, "perbuild");
|
|
||||||
return Group.ParsePermOrName(value, LevelPermission.Guest);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Help(Player p) {
|
public override void Help(Player p) {
|
||||||
Player.Message(p, "%T/DeleteLvl [map]");
|
Player.Message(p, "%T/DeleteLvl [map]");
|
||||||
Player.Message(p, "%HCompletely deletes [map] (portals, MBs, everything");
|
Player.Message(p, "%HCompletely deletes [map] (portals, MBs, everything");
|
||||||
|
@ -30,9 +30,7 @@ namespace MCGalaxy.Commands.World {
|
|||||||
public override void Use(Player p, string message) {
|
public override void Use(Player p, string message) {
|
||||||
int totalFixed = 0;
|
int totalFixed = 0;
|
||||||
Level lvl = p.level;
|
Level lvl = p.level;
|
||||||
if (!lvl.BuildAccess.CheckDetailed(p)) {
|
if (!LevelInfo.ValidateAction(p, lvl.name, "use %T/fixgrass %Son this level")) return;
|
||||||
Player.Message(p, "Hence you cannot use %T/fixgrass %Son this map"); return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.Length == 0) {
|
if (message.Length == 0) {
|
||||||
FixDirtAndGrass(p, lvl, ref totalFixed);
|
FixDirtAndGrass(p, lvl, ref totalFixed);
|
||||||
|
@ -42,9 +42,12 @@ namespace MCGalaxy.Commands.World {
|
|||||||
} else {
|
} else {
|
||||||
if (!CheckExtraPerm(p, 1)) return;
|
if (!CheckExtraPerm(p, 1)) return;
|
||||||
if (!Formatter.ValidName(p, message, "level")) return;
|
if (!Formatter.ValidName(p, message, "level")) return;
|
||||||
|
if (!LevelInfo.ValidateAction(p, ServerConfig.MainLevel, "set main to another level")) return;
|
||||||
|
|
||||||
string map = Matcher.FindMaps(p, message);
|
string map = Matcher.FindMaps(p, message);
|
||||||
if (map == null) return;
|
if (map == null) return;
|
||||||
|
if (!LevelInfo.ValidateAction(p, map, "set main to this level")) return;
|
||||||
|
|
||||||
Server.SetMainLevel(map);
|
Server.SetMainLevel(map);
|
||||||
SrvProperties.Save();
|
SrvProperties.Save();
|
||||||
Player.Message(p, "Set main level to {0}", Server.mainLevel.ColoredName);
|
Player.Message(p, "Set main level to {0}", Server.mainLevel.ColoredName);
|
||||||
|
@ -50,7 +50,7 @@ namespace MCGalaxy.Commands.World {
|
|||||||
string map = Matcher.FindMaps(p, args[0]);
|
string map = Matcher.FindMaps(p, args[0]);
|
||||||
if (map == null) return;
|
if (map == null) return;
|
||||||
|
|
||||||
PrintMapInfo(p, GetConfig(map));
|
PrintMapInfo(p, LevelInfo.GetConfig(map, out lvl));
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
lvl = Matcher.FindLevels(p, args[0]);
|
lvl = Matcher.FindLevels(p, args[0]);
|
||||||
@ -63,20 +63,11 @@ namespace MCGalaxy.Commands.World {
|
|||||||
if (!CheckExtraPerm(p, 1)) return;
|
if (!CheckExtraPerm(p, 1)) return;
|
||||||
if (opt.CaselessEq("realmowner") && !CheckExtraPerm(p, 2)) return;
|
if (opt.CaselessEq("realmowner") && !CheckExtraPerm(p, 2)) return;
|
||||||
|
|
||||||
|
if (!LevelInfo.ValidateAction(p, lvl.name, "change map settings of this level")) return;
|
||||||
if (SetMapOption(p, lvl, opt, value)) return;
|
if (SetMapOption(p, lvl, opt, value)) return;
|
||||||
Player.Message(p, "Could not find option entered.");
|
Player.Message(p, "Could not find option entered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static LevelConfig GetConfig(string map) {
|
|
||||||
Level lvl = LevelInfo.FindExact(map);
|
|
||||||
if (lvl != null) return lvl.Config;
|
|
||||||
|
|
||||||
string propsPath = LevelInfo.PropertiesPath(map);
|
|
||||||
LevelConfig cfg = new LevelConfig();
|
|
||||||
LevelConfig.Load(propsPath, cfg);
|
|
||||||
return cfg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsMapOption(string[] args) {
|
static bool IsMapOption(string[] args) {
|
||||||
string opt = LevelOptions.Map(args[0].ToLower());
|
string opt = LevelOptions.Map(args[0].ToLower());
|
||||||
if (!ValidOption(opt)) return false;
|
if (!ValidOption(opt)) return false;
|
||||||
|
@ -44,9 +44,11 @@ namespace MCGalaxy.Commands.World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!LevelInfo.ValidateAction(p, lvl.name, "pause physics on this level")) return;
|
||||||
bool enabled = lvl.physPause;
|
bool enabled = lvl.physPause;
|
||||||
lvl.PhysicsEnabled = enabled;
|
lvl.PhysicsEnabled = enabled;
|
||||||
lvl.physPause = !lvl.physPause;
|
lvl.physPause = !lvl.physPause;
|
||||||
|
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
Chat.MessageGlobal("Physics on {0} %Swere re-enabled.", lvl.ColoredName);
|
Chat.MessageGlobal("Physics on {0} %Swere re-enabled.", lvl.ColoredName);
|
||||||
} else {
|
} else {
|
||||||
|
@ -38,6 +38,8 @@ namespace MCGalaxy.Commands.World {
|
|||||||
level = Matcher.FindLevels(p, args[0]);
|
level = Matcher.FindLevels(p, args[0]);
|
||||||
if (level == null) return;
|
if (level == null) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!LevelInfo.ValidateAction(p, level.name, "set physics of this level")) return;
|
||||||
SetPhysics(level, state);
|
SetPhysics(level, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ namespace MCGalaxy.Commands.World {
|
|||||||
|
|
||||||
if (LevelInfo.MapExists(newName)) { Player.Message(p, "Level already exists."); return; }
|
if (LevelInfo.MapExists(newName)) { Player.Message(p, "Level already exists."); return; }
|
||||||
if (lvl == Server.mainLevel) { Player.Message(p, "Cannot rename the main level."); return; }
|
if (lvl == Server.mainLevel) { Player.Message(p, "Cannot rename the main level."); return; }
|
||||||
|
if (!LevelInfo.ValidateAction(p, lvl.name, "rename this level")) return;
|
||||||
|
|
||||||
List<Player> players = lvl.getPlayers();
|
List<Player> players = lvl.getPlayers();
|
||||||
lvl.Unload();
|
lvl.Unload();
|
||||||
|
@ -42,6 +42,7 @@ namespace MCGalaxy.Commands.World {
|
|||||||
public static bool DoResize(Player p, string[] args) {
|
public static bool DoResize(Player p, string[] args) {
|
||||||
Level lvl = Matcher.FindLevels(p, args[0]);
|
Level lvl = Matcher.FindLevels(p, args[0]);
|
||||||
if (lvl == null) return true;
|
if (lvl == null) return true;
|
||||||
|
if (!LevelInfo.ValidateAction(p, lvl.name, "resize this level")) return false;
|
||||||
|
|
||||||
ushort x = 0, y = 0, z = 0;
|
ushort x = 0, y = 0, z = 0;
|
||||||
if (!CmdNewLvl.CheckMapAxis(p, args[1], "Width", ref x)) return false;
|
if (!CmdNewLvl.CheckMapAxis(p, args[1], "Width", ref x)) return false;
|
||||||
|
@ -44,6 +44,7 @@ namespace MCGalaxy.Commands.World {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!LevelInfo.ValidateAction(p, lvl.name, "restore a backup of this level")) return;
|
||||||
if (File.Exists(LevelInfo.BackupPath(lvl.name, args[0]))) {
|
if (File.Exists(LevelInfo.BackupPath(lvl.name, args[0]))) {
|
||||||
try {
|
try {
|
||||||
DoRestore(lvl, args[0]);
|
DoRestore(lvl, args[0]);
|
||||||
|
@ -26,6 +26,7 @@ namespace MCGalaxy.Commands.World {
|
|||||||
public override void Use(Player p, string message) {
|
public override void Use(Player p, string message) {
|
||||||
if (message.Length > 0) { Help(p); return; }
|
if (message.Length > 0) { Help(p); return; }
|
||||||
|
|
||||||
|
if (!LevelInfo.ValidateAction(p, p.level.name, "set spawn of this level")) return;
|
||||||
Player.Message(p, "Spawn location set to your current position.");
|
Player.Message(p, "Spawn location set to your current position.");
|
||||||
p.level.spawnx = (ushort)p.Pos.BlockX;
|
p.level.spawnx = (ushort)p.Pos.BlockX;
|
||||||
p.level.spawny = (ushort)p.Pos.BlockY;
|
p.level.spawny = (ushort)p.Pos.BlockY;
|
||||||
|
@ -89,31 +89,35 @@ namespace MCGalaxy {
|
|||||||
return "levels/level properties/" + name + ".properties";
|
return "levels/level properties/" + name + ".properties";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static LevelConfig GetConfig(string map, out Level lvl) {
|
||||||
|
lvl = FindExact(map);
|
||||||
|
if (lvl != null) return lvl.Config;
|
||||||
|
|
||||||
public static string FindOfflineProperty(string name, string propKey) {
|
string propsPath = PropertiesPath(map);
|
||||||
string path = PropertiesPath(name);
|
LevelConfig cfg = new LevelConfig();
|
||||||
if (!File.Exists(path)) return null;
|
LevelConfig.Load(propsPath, cfg);
|
||||||
|
return cfg;
|
||||||
string[] lines = null;
|
|
||||||
try {
|
|
||||||
lines = File.ReadAllLines(path);
|
|
||||||
} catch {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string line in lines) {
|
internal static bool ValidateAction(Player p, string map, string action) {
|
||||||
try {
|
if (p == null) return true;
|
||||||
if (line.Length == 0 || line[0] == '#') continue;
|
LevelAccessController visit, build;
|
||||||
int index = line.IndexOf(" = ");
|
Level lvl = null;
|
||||||
if (index == -1) continue;
|
LevelConfig cfg = GetConfig(map, out lvl);
|
||||||
|
|
||||||
string key = line.Substring(0, index).ToLower();
|
if (lvl != null) {
|
||||||
if (key == propKey) return line.Substring(index + 3);
|
visit = lvl.VisitAccess;
|
||||||
} catch (Exception e) {
|
build = lvl.BuildAccess;
|
||||||
Logger.LogError(e);
|
} else {
|
||||||
|
visit = new LevelAccessController(cfg, map, true);
|
||||||
|
build = new LevelAccessController(cfg, map, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!visit.CheckDetailed(p) || !build.CheckDetailed(p)) {
|
||||||
|
Player.Message(p, "Hence, you cannot {0}.", action);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return null;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user