initial commit
This commit is contained in:
623
Core/Service/Groups/Core.lua
Normal file
623
Core/Service/Groups/Core.lua
Normal file
@@ -0,0 +1,623 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local _, TSM = ...
|
||||
local Groups = TSM:NewPackage("Groups")
|
||||
local Database = TSM.Include("Util.Database")
|
||||
local TempTable = TSM.Include("Util.TempTable")
|
||||
local Table = TSM.Include("Util.Table")
|
||||
local SmartMap = TSM.Include("Util.SmartMap")
|
||||
local String = TSM.Include("Util.String")
|
||||
local Log = TSM.Include("Util.Log")
|
||||
local ItemString = TSM.Include("Util.ItemString")
|
||||
local private = {
|
||||
db = nil,
|
||||
itemDB = nil,
|
||||
itemStringMap = nil,
|
||||
itemStringMapReader = nil,
|
||||
baseItemStringItemIteratorQuery = nil,
|
||||
groupListCache = {},
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- New Modules Functions
|
||||
-- ============================================================================
|
||||
|
||||
function Groups.OnInitialize()
|
||||
private.db = Database.NewSchema("GROUPS")
|
||||
:AddStringField("groupPath")
|
||||
:AddStringField("orderStr")
|
||||
:AddBooleanField("hasAuctioningOperation")
|
||||
:AddBooleanField("hasCraftingOperation")
|
||||
:AddBooleanField("hasMailingOperation")
|
||||
:AddBooleanField("hasShoppingOperation")
|
||||
:AddBooleanField("hasSniperOperation")
|
||||
:AddBooleanField("hasVendoringOperation")
|
||||
:AddBooleanField("hasWarehousingOperation")
|
||||
:AddIndex("groupPath")
|
||||
:Commit()
|
||||
private.itemDB = Database.NewSchema("GROUP_ITEMS")
|
||||
:AddUniqueStringField("itemString")
|
||||
:AddStringField("groupPath")
|
||||
:AddSmartMapField("baseItemString", ItemString.GetBaseMap(), "itemString")
|
||||
:AddIndex("groupPath")
|
||||
:AddIndex("baseItemString")
|
||||
:Commit()
|
||||
private.itemStringMapReader = private.itemStringMap:CreateReader()
|
||||
Groups.RebuildDatabase()
|
||||
private.baseItemStringItemIteratorQuery = private.itemDB:NewQuery()
|
||||
:Select("itemString", "groupPath")
|
||||
:Equal("baseItemString", Database.BoundQueryParam())
|
||||
end
|
||||
|
||||
function Groups.RebuildDatabase()
|
||||
wipe(private.groupListCache)
|
||||
|
||||
-- convert ignoreRandomEnchants to ignoreItemVariations
|
||||
for _, info in pairs(TSM.db.profile.userData.groups) do
|
||||
if info.ignoreRandomEnchants ~= nil then
|
||||
info.ignoreItemVariations = info.ignoreRandomEnchants
|
||||
info.ignoreRandomEnchants = nil
|
||||
end
|
||||
end
|
||||
|
||||
for groupPath, groupInfo in pairs(TSM.db.profile.userData.groups) do
|
||||
if type(groupPath) == "string" and not strmatch(groupPath, TSM.CONST.GROUP_SEP..TSM.CONST.GROUP_SEP) then
|
||||
-- check the contents of groupInfo
|
||||
for _, moduleName in TSM.Operations.ModuleIterator() do
|
||||
groupInfo[moduleName] = groupInfo[moduleName] or {}
|
||||
if groupPath == TSM.CONST.ROOT_GROUP_PATH then
|
||||
-- root group should have override flag set
|
||||
groupInfo[moduleName].override = true
|
||||
end
|
||||
end
|
||||
for key in pairs(groupInfo) do
|
||||
if TSM.Operations.ModuleExists(key) then
|
||||
-- this is a set of module operations
|
||||
local operations = groupInfo[key]
|
||||
while #operations > TSM.Operations.GetMaxNumber(key) do
|
||||
-- remove extra operations
|
||||
tremove(operations)
|
||||
end
|
||||
for key2 in pairs(operations) do
|
||||
if key2 == "override" then
|
||||
-- ensure the override field is either true or nil
|
||||
operations.override = operations.override and true or nil
|
||||
elseif type(key2) ~= "number" or key2 <= 0 or key2 > #operations then
|
||||
-- this is an invalid key
|
||||
Log.Err("Removing invalid operations key (%s, %s): %s", groupPath, key, tostring(key2))
|
||||
operations[key2] = nil
|
||||
end
|
||||
end
|
||||
for i = #operations, 1, -1 do
|
||||
if type(operations[i]) ~= "string" or operations[i] == "" or not TSM.Operations.Exists(key, operations[i]) then
|
||||
-- remove operations which no longer exist
|
||||
-- we used to have a bunch of placeholder "" operations, so don't log for those
|
||||
if operations[i] ~= "" then
|
||||
Log.Err("Removing invalid operation from group (%s): %s, %s", groupPath, key, tostring(operations[i]))
|
||||
end
|
||||
tremove(operations, i)
|
||||
end
|
||||
end
|
||||
elseif key ~= "ignoreItemVariations" then
|
||||
-- invalid key
|
||||
Log.Err("Removing invalid groupInfo key (%s): %s", groupPath, tostring(key))
|
||||
groupInfo[key] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
-- remove invalid group paths
|
||||
Log.Err("Removing invalid group path: %s", tostring(groupPath))
|
||||
TSM.db.profile.userData.groups[groupPath] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if not TSM.db.profile.userData.groups[TSM.CONST.ROOT_GROUP_PATH] then
|
||||
-- set the override flag for all top-level groups and then create it
|
||||
for groupPath, moduleOperations in pairs(TSM.db.profile.userData.groups) do
|
||||
if not strfind(groupPath, TSM.CONST.GROUP_SEP) then
|
||||
for _, moduleName in TSM.Operations.ModuleIterator() do
|
||||
moduleOperations[moduleName].override = true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- create the root group manually with default operations
|
||||
TSM.db.profile.userData.groups[TSM.CONST.ROOT_GROUP_PATH] = {}
|
||||
for _, moduleName in TSM.Operations.ModuleIterator() do
|
||||
assert(TSM.Operations.Exists(moduleName, "#Default"))
|
||||
TSM.db.profile.userData.groups[TSM.CONST.ROOT_GROUP_PATH][moduleName] = { "#Default", override = true }
|
||||
end
|
||||
end
|
||||
|
||||
for _, groupPath in Groups.GroupIterator() do
|
||||
local parentPath = TSM.Groups.Path.GetParent(groupPath)
|
||||
if not TSM.db.profile.userData.groups[parentPath] then
|
||||
-- the parent group doesn't exist, so remove this group
|
||||
Log.Err("Removing group with non-existent parent: %s", tostring(groupPath))
|
||||
TSM.db.profile.userData.groups[groupPath] = nil
|
||||
else
|
||||
for _, moduleName in TSM.Operations.ModuleIterator() do
|
||||
if not Groups.HasOperationOverride(groupPath, moduleName) then
|
||||
private.InheritParentOperations(groupPath, moduleName)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- fix up any invalid items
|
||||
local newPaths = TempTable.Acquire()
|
||||
for itemString, groupPath in pairs(TSM.db.profile.userData.items) do
|
||||
local newItemString = ItemString.Get(itemString)
|
||||
if not newItemString then
|
||||
-- this itemstring is invalid
|
||||
Log.Err("Itemstring (%s) is invalid", tostring(itemString))
|
||||
TSM.db.profile.userData.items[itemString] = nil
|
||||
elseif groupPath == TSM.CONST.ROOT_GROUP_PATH or not TSM.db.profile.userData.groups[groupPath] then
|
||||
-- this group doesn't exist
|
||||
Log.Err("Group (%s) doesn't exist, so removing item (%s)", groupPath, itemString)
|
||||
TSM.db.profile.userData.items[itemString] = nil
|
||||
elseif newItemString ~= itemString then
|
||||
-- remove this invalid itemstring from this group
|
||||
Log.Err("Itemstring changed (%s -> %s), so removing it from group (%s)", itemString, newItemString, groupPath)
|
||||
TSM.db.profile.userData.items[itemString] = nil
|
||||
-- add this new item to this group if it's not already in one
|
||||
if not TSM.db.profile.userData.items[newItemString] then
|
||||
newPaths[newItemString] = groupPath
|
||||
Log.Err("Adding to group instead (%s)", groupPath)
|
||||
end
|
||||
end
|
||||
end
|
||||
for itemString, groupPath in pairs(newPaths) do
|
||||
TSM.db.profile.userData.items[itemString] = groupPath
|
||||
end
|
||||
TempTable.Release(newPaths)
|
||||
|
||||
-- populate our database
|
||||
private.itemDB:TruncateAndBulkInsertStart()
|
||||
for itemString, groupPath in pairs(TSM.db.profile.userData.items) do
|
||||
private.itemDB:BulkInsertNewRow(itemString, groupPath)
|
||||
end
|
||||
private.itemDB:BulkInsertEnd()
|
||||
private.itemStringMap:SetCallbacksPaused(true)
|
||||
for key in private.itemStringMap:Iterator() do
|
||||
private.itemStringMap:ValueChanged(key)
|
||||
end
|
||||
private.RebuildDB()
|
||||
private.itemStringMap:SetCallbacksPaused(false)
|
||||
end
|
||||
|
||||
function Groups.TranslateItemString(itemString)
|
||||
return private.itemStringMapReader[itemString]
|
||||
end
|
||||
|
||||
function Groups.GetAutoBaseItemStringSmartMap()
|
||||
return private.itemStringMap
|
||||
end
|
||||
|
||||
function Groups.GetItemDBForJoin()
|
||||
return private.itemDB
|
||||
end
|
||||
|
||||
function Groups.CreateQuery()
|
||||
return private.db:NewQuery()
|
||||
:OrderBy("orderStr", true)
|
||||
end
|
||||
|
||||
function Groups.Create(groupPath)
|
||||
private.CreateGroup(groupPath)
|
||||
private.RebuildDB()
|
||||
end
|
||||
|
||||
function Groups.Move(groupPath, newGroupPath)
|
||||
assert(not TSM.db.profile.userData.groups[newGroupPath], "Target group already exists")
|
||||
assert(groupPath ~= TSM.CONST.ROOT_GROUP_PATH, "Can't move root group")
|
||||
assert(TSM.db.profile.userData.groups[groupPath], "Group doesn't exist")
|
||||
local newParentPath = TSM.Groups.Path.GetParent(newGroupPath)
|
||||
assert(newParentPath and TSM.db.profile.userData.groups[newParentPath], "Parent of target is invalid")
|
||||
|
||||
local changes = TempTable.Acquire()
|
||||
private.itemDB:SetQueryUpdatesPaused(true)
|
||||
|
||||
-- get a list of group path changes for this group and all its subgroups
|
||||
local gsubEscapedNewGroupPath = gsub(newGroupPath, "%%", "%%%%")
|
||||
for path in pairs(TSM.db.profile.userData.groups) do
|
||||
if path == groupPath or TSM.Groups.Path.IsChild(path, groupPath) then
|
||||
changes[path] = gsub(path, "^"..String.Escape(groupPath), gsubEscapedNewGroupPath)
|
||||
end
|
||||
end
|
||||
|
||||
for oldPath, newPath in pairs(changes) do
|
||||
-- move the group
|
||||
assert(TSM.db.profile.userData.groups[oldPath] and not TSM.db.profile.userData.groups[newPath])
|
||||
TSM.db.profile.userData.groups[newPath] = TSM.db.profile.userData.groups[oldPath]
|
||||
TSM.db.profile.userData.groups[oldPath] = nil
|
||||
|
||||
-- move the items
|
||||
local query = private.itemDB:NewQuery()
|
||||
:Equal("groupPath", oldPath)
|
||||
for _, row in query:Iterator() do
|
||||
local itemString = row:GetField("itemString")
|
||||
assert(TSM.db.profile.userData.items[itemString])
|
||||
TSM.db.profile.userData.items[itemString] = newPath
|
||||
row:SetField("groupPath", newPath)
|
||||
:Update()
|
||||
end
|
||||
query:Release()
|
||||
end
|
||||
|
||||
-- update the operations all groups which were moved
|
||||
for _, moduleName in TSM.Operations.ModuleIterator() do
|
||||
if not Groups.HasOperationOverride(newGroupPath, moduleName) then
|
||||
private.InheritParentOperations(newGroupPath, moduleName)
|
||||
private.UpdateChildGroupOperations(newGroupPath, moduleName)
|
||||
end
|
||||
end
|
||||
|
||||
TempTable.Release(changes)
|
||||
private.RebuildDB()
|
||||
private.itemDB:SetQueryUpdatesPaused(false)
|
||||
end
|
||||
|
||||
function Groups.Delete(groupPath)
|
||||
assert(groupPath ~= TSM.CONST.ROOT_GROUP_PATH and TSM.db.profile.userData.groups[groupPath])
|
||||
local parentPath = TSM.Groups.Path.GetParent(groupPath)
|
||||
assert(parentPath)
|
||||
if parentPath == TSM.CONST.ROOT_GROUP_PATH then
|
||||
parentPath = nil
|
||||
end
|
||||
|
||||
-- delete this group and all subgroups
|
||||
for path in pairs(TSM.db.profile.userData.groups) do
|
||||
if path == groupPath or TSM.Groups.Path.IsChild(path, groupPath) then
|
||||
-- delete this group
|
||||
TSM.db.profile.userData.groups[path] = nil
|
||||
end
|
||||
end
|
||||
-- remove all items from our DB
|
||||
private.itemDB:SetQueryUpdatesPaused(true)
|
||||
local query = private.itemDB:NewQuery()
|
||||
:Or()
|
||||
:Equal("groupPath", groupPath)
|
||||
:Matches("groupPath", "^"..String.Escape(groupPath)..TSM.CONST.GROUP_SEP)
|
||||
:End()
|
||||
local updateMapItems = TempTable.Acquire()
|
||||
for _, row in query:Iterator() do
|
||||
local itemString = row:GetField("itemString")
|
||||
assert(TSM.db.profile.userData.items[itemString])
|
||||
TSM.db.profile.userData.items[itemString] = nil
|
||||
private.itemDB:DeleteRow(row)
|
||||
updateMapItems[itemString] = true
|
||||
end
|
||||
query:Release()
|
||||
private.itemStringMap:SetCallbacksPaused(true)
|
||||
for itemString in private.itemStringMap:Iterator() do
|
||||
if updateMapItems[itemString] or updateMapItems[ItemString.GetBaseFast(itemString)] then
|
||||
-- either this item itself was removed from a group, or the base item was - in either case trigger an update
|
||||
private.itemStringMap:ValueChanged(itemString)
|
||||
end
|
||||
end
|
||||
private.itemStringMap:SetCallbacksPaused(false)
|
||||
TempTable.Release(updateMapItems)
|
||||
private.RebuildDB()
|
||||
private.itemDB:SetQueryUpdatesPaused(false)
|
||||
end
|
||||
|
||||
function Groups.Exists(groupPath)
|
||||
return TSM.db.profile.userData.groups[groupPath] and true or false
|
||||
end
|
||||
|
||||
function Groups.SetItemGroup(itemString, groupPath)
|
||||
assert(not groupPath or (groupPath ~= TSM.CONST.ROOT_GROUP_PATH and TSM.db.profile.userData.groups[groupPath]))
|
||||
|
||||
local row = private.itemDB:GetUniqueRow("itemString", itemString)
|
||||
local updateMap = false
|
||||
if row then
|
||||
if groupPath then
|
||||
row:SetField("groupPath", groupPath)
|
||||
:Update()
|
||||
row:Release()
|
||||
else
|
||||
private.itemDB:DeleteRow(row)
|
||||
row:Release()
|
||||
-- we just removed an item from a group, so update the map
|
||||
updateMap = true
|
||||
end
|
||||
else
|
||||
assert(groupPath)
|
||||
private.itemDB:NewRow()
|
||||
:SetField("itemString", itemString)
|
||||
:SetField("groupPath", groupPath)
|
||||
:Create()
|
||||
-- we just added a new item to a group, so update the map
|
||||
updateMap = true
|
||||
end
|
||||
TSM.db.profile.userData.items[itemString] = groupPath
|
||||
if updateMap then
|
||||
private.itemStringMap:SetCallbacksPaused(true)
|
||||
private.itemStringMap:ValueChanged(itemString)
|
||||
if itemString == ItemString.GetBaseFast(itemString) then
|
||||
-- this is a base item string, so need to also update all other items whose base item is equal to this item
|
||||
for mapItemString in private.itemStringMap:Iterator() do
|
||||
if ItemString.GetBaseFast(mapItemString) == itemString then
|
||||
private.itemStringMap:ValueChanged(mapItemString)
|
||||
end
|
||||
end
|
||||
end
|
||||
private.itemStringMap:SetCallbacksPaused(false)
|
||||
end
|
||||
end
|
||||
|
||||
function Groups.BulkCreateFromImport(groupName, items, groups, groupOperations, moveExistingItems)
|
||||
-- create all the groups
|
||||
assert(not TSM.db.profile.userData.groups[groupName])
|
||||
for relGroupPath in pairs(groups) do
|
||||
local groupPath = relGroupPath == "" and groupName or TSM.Groups.Path.Join(groupName, relGroupPath)
|
||||
if not TSM.db.profile.userData.groups[groupPath] then
|
||||
private.CreateGroup(groupPath)
|
||||
end
|
||||
end
|
||||
for relGroupPath, moduleOperations in pairs(groupOperations) do
|
||||
local groupPath = relGroupPath == "" and groupName or TSM.Groups.Path.Join(groupName, relGroupPath)
|
||||
for moduleName, operations in pairs(moduleOperations) do
|
||||
if operations.override then
|
||||
TSM.db.profile.userData.groups[groupPath][moduleName] = operations
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
end
|
||||
end
|
||||
end
|
||||
local numItems = 0
|
||||
for itemString, relGroupPath in pairs(items) do
|
||||
if moveExistingItems or not Groups.IsItemInGroup(itemString) then
|
||||
local groupPath = relGroupPath == "" and groupName or TSM.Groups.Path.Join(groupName, relGroupPath)
|
||||
Groups.SetItemGroup(itemString, groupPath)
|
||||
numItems = numItems + 1
|
||||
end
|
||||
end
|
||||
private.RebuildDB()
|
||||
return numItems
|
||||
end
|
||||
|
||||
function Groups.GetPathByItem(itemString)
|
||||
itemString = TSM.Groups.TranslateItemString(itemString)
|
||||
assert(itemString)
|
||||
local groupPath = private.itemDB:GetUniqueRowField("itemString", itemString, "groupPath") or TSM.CONST.ROOT_GROUP_PATH
|
||||
assert(TSM.db.profile.userData.groups[groupPath])
|
||||
return groupPath
|
||||
end
|
||||
|
||||
function Groups.IsItemInGroup(itemString)
|
||||
return private.itemDB:HasUniqueRow("itemString", itemString)
|
||||
end
|
||||
|
||||
function Groups.ItemByBaseItemStringIterator(baseItemString)
|
||||
private.baseItemStringItemIteratorQuery:BindParams(baseItemString)
|
||||
return private.baseItemStringItemIteratorQuery:Iterator()
|
||||
end
|
||||
|
||||
function Groups.ItemIterator(groupPathFilter, includeSubGroups)
|
||||
assert(groupPathFilter ~= TSM.CONST.ROOT_GROUP_PATH)
|
||||
local query = private.itemDB:NewQuery()
|
||||
:Select("itemString", "groupPath")
|
||||
if groupPathFilter then
|
||||
if includeSubGroups then
|
||||
query:StartsWith("groupPath", groupPathFilter)
|
||||
query:Custom(private.GroupPathQueryFilter, groupPathFilter)
|
||||
else
|
||||
query:Equal("groupPath", groupPathFilter)
|
||||
end
|
||||
end
|
||||
return query:IteratorAndRelease()
|
||||
end
|
||||
|
||||
function private.GroupPathQueryFilter(row, groupPathFilter)
|
||||
return row:GetField("groupPath") == groupPathFilter or strmatch(row:GetField("groupPath"), "^"..String.Escape(groupPathFilter)..TSM.CONST.GROUP_SEP)
|
||||
end
|
||||
|
||||
function Groups.GetNumItems(groupPathFilter)
|
||||
assert(groupPathFilter ~= TSM.CONST.ROOT_GROUP_PATH)
|
||||
return private.itemDB:NewQuery()
|
||||
:Equal("groupPath", groupPathFilter)
|
||||
:CountAndRelease()
|
||||
end
|
||||
|
||||
function Groups.GroupIterator()
|
||||
if #private.groupListCache == 0 then
|
||||
for groupPath in pairs(TSM.db.profile.userData.groups) do
|
||||
if groupPath ~= TSM.CONST.ROOT_GROUP_PATH then
|
||||
tinsert(private.groupListCache, groupPath)
|
||||
end
|
||||
end
|
||||
Groups.SortGroupList(private.groupListCache)
|
||||
end
|
||||
return ipairs(private.groupListCache)
|
||||
end
|
||||
|
||||
function Groups.SortGroupList(list)
|
||||
Table.Sort(list, private.GroupSortFunction)
|
||||
end
|
||||
|
||||
function Groups.SetOperationOverride(groupPath, moduleName, override)
|
||||
assert(TSM.db.profile.userData.groups[groupPath])
|
||||
assert(groupPath ~= TSM.CONST.ROOT_GROUP_PATH)
|
||||
if override == (TSM.db.profile.userData.groups[groupPath][moduleName].override and true or false) then
|
||||
return
|
||||
end
|
||||
|
||||
if not override then
|
||||
TSM.db.profile.userData.groups[groupPath][moduleName].override = nil
|
||||
private.InheritParentOperations(groupPath, moduleName)
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
else
|
||||
wipe(TSM.db.profile.userData.groups[groupPath][moduleName])
|
||||
TSM.db.profile.userData.groups[groupPath][moduleName].override = true
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
end
|
||||
private.RebuildDB()
|
||||
end
|
||||
|
||||
function Groups.HasOperationOverride(groupPath, moduleName)
|
||||
return TSM.db.profile.userData.groups[groupPath][moduleName].override
|
||||
end
|
||||
|
||||
function Groups.OperationIterator(groupPath, moduleName)
|
||||
return ipairs(TSM.db.profile.userData.groups[groupPath][moduleName])
|
||||
end
|
||||
|
||||
function Groups.AppendOperation(groupPath, moduleName, operationName)
|
||||
assert(TSM.Operations.Exists(moduleName, operationName))
|
||||
local groupOperations = TSM.db.profile.userData.groups[groupPath][moduleName]
|
||||
assert(groupOperations.override and #groupOperations < TSM.Operations.GetMaxNumber(moduleName))
|
||||
tinsert(groupOperations, operationName)
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
private.RebuildDB()
|
||||
end
|
||||
|
||||
function Groups.RemoveOperation(groupPath, moduleName, operationIndex)
|
||||
local groupOperations = TSM.db.profile.userData.groups[groupPath][moduleName]
|
||||
assert(groupOperations.override and groupOperations[operationIndex])
|
||||
tremove(groupOperations, operationIndex)
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
private.RebuildDB()
|
||||
end
|
||||
|
||||
function Groups.RemoveOperationByName(groupPath, moduleName, operationName)
|
||||
local groupOperations = TSM.db.profile.userData.groups[groupPath][moduleName]
|
||||
assert(groupOperations.override)
|
||||
assert(Table.RemoveByValue(groupOperations, operationName) > 0)
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
private.RebuildDB()
|
||||
end
|
||||
|
||||
function Groups.RemoveOperationFromAllGroups(moduleName, operationName)
|
||||
-- just blindly remove from all groups - no need to check for override
|
||||
Table.RemoveByValue(TSM.db.profile.userData.groups[TSM.CONST.ROOT_GROUP_PATH][moduleName], operationName)
|
||||
for _, groupPath in Groups.GroupIterator() do
|
||||
Table.RemoveByValue(TSM.db.profile.userData.groups[groupPath][moduleName], operationName)
|
||||
end
|
||||
private.RebuildDB()
|
||||
end
|
||||
|
||||
function Groups.SwapOperation(groupPath, moduleName, fromIndex, toIndex)
|
||||
local groupOperations = TSM.db.profile.userData.groups[groupPath][moduleName]
|
||||
groupOperations[fromIndex], groupOperations[toIndex] = groupOperations[toIndex], groupOperations[fromIndex]
|
||||
private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
end
|
||||
|
||||
function Groups.OperationRenamed(moduleName, oldName, newName)
|
||||
-- just blindly rename in all groups - no need to check for override
|
||||
for _, info in pairs(TSM.db.profile.userData.groups) do
|
||||
for i = 1, #info[moduleName] do
|
||||
if info[moduleName][i] == oldName then
|
||||
info[moduleName][i] = newName
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.RebuildDB()
|
||||
private.db:TruncateAndBulkInsertStart()
|
||||
for groupPath in pairs(TSM.db.profile.userData.groups) do
|
||||
local orderStr = gsub(groupPath, TSM.CONST.GROUP_SEP, "\001")
|
||||
orderStr = strlower(orderStr)
|
||||
local hasAuctioningOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Auctioning", groupPath) do
|
||||
hasAuctioningOperation = true
|
||||
end
|
||||
local hasCraftingOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Crafting", groupPath) do
|
||||
hasCraftingOperation = true
|
||||
end
|
||||
local hasMailingOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Mailing", groupPath) do
|
||||
hasMailingOperation = true
|
||||
end
|
||||
local hasShoppingOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Shopping", groupPath) do
|
||||
hasShoppingOperation = true
|
||||
end
|
||||
local hasSniperOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Sniper", groupPath) do
|
||||
hasSniperOperation = true
|
||||
end
|
||||
local hasVendoringOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Vendoring", groupPath) do
|
||||
hasVendoringOperation = true
|
||||
end
|
||||
local hasWarehousingOperation = false
|
||||
for _ in TSM.Operations.GroupOperationIterator("Warehousing", groupPath) do
|
||||
hasWarehousingOperation = true
|
||||
end
|
||||
private.db:BulkInsertNewRow(groupPath, orderStr, hasAuctioningOperation, hasCraftingOperation, hasMailingOperation, hasShoppingOperation, hasSniperOperation, hasVendoringOperation, hasWarehousingOperation)
|
||||
end
|
||||
private.db:BulkInsertEnd()
|
||||
wipe(private.groupListCache)
|
||||
end
|
||||
|
||||
function private.CreateGroup(groupPath)
|
||||
assert(not TSM.db.profile.userData.groups[groupPath])
|
||||
local parentPath = TSM.Groups.Path.GetParent(groupPath)
|
||||
assert(parentPath)
|
||||
if parentPath ~= TSM.CONST.ROOT_GROUP_PATH and not TSM.db.profile.userData.groups[parentPath] then
|
||||
-- recursively create the parent group first
|
||||
private.CreateGroup(parentPath)
|
||||
end
|
||||
TSM.db.profile.userData.groups[groupPath] = {}
|
||||
for _, moduleName in TSM.Operations.ModuleIterator() do
|
||||
TSM.db.profile.userData.groups[groupPath][moduleName] = {}
|
||||
-- assign all parent operations to this group
|
||||
for _, operationName in ipairs(TSM.db.profile.userData.groups[parentPath][moduleName]) do
|
||||
tinsert(TSM.db.profile.userData.groups[groupPath][moduleName], operationName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function private.GroupSortFunction(a, b)
|
||||
return strlower(gsub(a, TSM.CONST.GROUP_SEP, "\001")) < strlower(gsub(b, TSM.CONST.GROUP_SEP, "\001"))
|
||||
end
|
||||
|
||||
function private.InheritParentOperations(groupPath, moduleName)
|
||||
local parentGroupPath = TSM.Groups.Path.GetParent(groupPath)
|
||||
local override = TSM.db.profile.userData.groups[groupPath][moduleName].override
|
||||
wipe(TSM.db.profile.userData.groups[groupPath][moduleName])
|
||||
TSM.db.profile.userData.groups[groupPath][moduleName].override = override
|
||||
for _, operationName in ipairs(TSM.db.profile.userData.groups[parentGroupPath][moduleName]) do
|
||||
tinsert(TSM.db.profile.userData.groups[groupPath][moduleName], operationName)
|
||||
end
|
||||
end
|
||||
|
||||
function private.UpdateChildGroupOperations(groupPath, moduleName)
|
||||
for _, childGroupPath in Groups.GroupIterator() do
|
||||
if TSM.Groups.Path.IsChild(childGroupPath, groupPath) and not Groups.HasOperationOverride(childGroupPath, moduleName) then
|
||||
private.InheritParentOperations(childGroupPath, moduleName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Item String Smart Map
|
||||
-- ============================================================================
|
||||
|
||||
do
|
||||
private.itemStringMap = SmartMap.New("string", "string", function(itemString)
|
||||
if Groups.IsItemInGroup(itemString) then
|
||||
-- this item is in a group, so just return it
|
||||
return itemString
|
||||
end
|
||||
local baseItemString = ItemString.GetBaseFast(itemString)
|
||||
-- return the base item if it's in a group; otherwise return the original item
|
||||
return Groups.IsItemInGroup(baseItemString) and baseItemString or itemString
|
||||
end)
|
||||
end
|
||||
869
Core/Service/Groups/ImportExport.lua
Normal file
869
Core/Service/Groups/ImportExport.lua
Normal file
@@ -0,0 +1,869 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local _, TSM = ...
|
||||
local ImportExport = TSM.Groups:NewPackage("ImportExport")
|
||||
local L = TSM.Include("Locale").GetTable()
|
||||
local TempTable = TSM.Include("Util.TempTable")
|
||||
local Table = TSM.Include("Util.Table")
|
||||
local Log = TSM.Include("Util.Log")
|
||||
local String = TSM.Include("Util.String")
|
||||
local ItemString = TSM.Include("Util.ItemString")
|
||||
local CustomPrice = TSM.Include("Service.CustomPrice")
|
||||
local AceSerializer = LibStub("AceSerializer-3.0")
|
||||
local LibDeflate = LibStub("LibDeflate")
|
||||
local LibSerialize = LibStub("LibSerialize")
|
||||
local private = {
|
||||
isOperationSettingsTable = {},
|
||||
importContext = {
|
||||
groupName = nil,
|
||||
items = nil,
|
||||
groups = nil,
|
||||
groupOperations = nil,
|
||||
operations = nil,
|
||||
customSources = nil,
|
||||
numChangedOperations = nil,
|
||||
filteredGroups = {},
|
||||
},
|
||||
}
|
||||
local MAGIC_STR = "TSM_EXPORT"
|
||||
local VERSION = 1
|
||||
local EXPORT_OPERATION_MODULES = {
|
||||
Auctioning = true,
|
||||
Crafting = true,
|
||||
Shopping = true,
|
||||
Sniper = true,
|
||||
Vendoring = true,
|
||||
Warehousing = true,
|
||||
}
|
||||
local EXPORT_CUSTOM_STRINGS = {
|
||||
Auctioning = {
|
||||
postCap = true,
|
||||
keepQuantity = true,
|
||||
maxExpires = true,
|
||||
undercut = true,
|
||||
minPrice = true,
|
||||
maxPrice = true,
|
||||
normalPrice = true,
|
||||
cancelRepostThreshold = true,
|
||||
stackSize = TSM.IsWowClassic() or nil,
|
||||
},
|
||||
Crafting = {
|
||||
minRestock = true,
|
||||
maxRestock = true,
|
||||
minProfit = true,
|
||||
craftPriceMethod = true,
|
||||
},
|
||||
Shopping = {
|
||||
restockQuantity = true,
|
||||
maxPrice = true,
|
||||
},
|
||||
Sniper = {
|
||||
belowPrice = true,
|
||||
},
|
||||
Vendoring = {
|
||||
vsMarketValue = true,
|
||||
vsMaxMarketValue = true,
|
||||
vsDestroyValue = true,
|
||||
vsMaxDestroyValue = true,
|
||||
},
|
||||
Warehousing = {},
|
||||
}
|
||||
local SERIALIZE_OPTIONS = {
|
||||
stable = true,
|
||||
filter = function(tbl, key, value)
|
||||
return not private.isOperationSettingsTable[tbl] or not TSM.Operations.IsCommonKey(key)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Module Functions
|
||||
-- ============================================================================
|
||||
|
||||
function ImportExport.GenerateExport(exportGroupPath, includeSubGroups, excludeOperations, excludeCustomSources)
|
||||
assert(exportGroupPath ~= TSM.CONST.ROOT_GROUP_PATH)
|
||||
local exportGroupName = TSM.Groups.Path.GetName(exportGroupPath)
|
||||
|
||||
-- collect the items and sub groups
|
||||
local items = TempTable.Acquire()
|
||||
local groups = TempTable.Acquire()
|
||||
local groupOperations = TempTable.Acquire()
|
||||
local operations = TempTable.Acquire()
|
||||
local customSources = TempTable.Acquire()
|
||||
for moduleName in pairs(EXPORT_OPERATION_MODULES) do
|
||||
operations[moduleName] = {}
|
||||
end
|
||||
assert(not next(private.isOperationSettingsTable))
|
||||
for _, groupPath in TSM.Groups.GroupIterator() do
|
||||
local relGroupPath = nil
|
||||
if TSM.Groups.Path.IsChild(groupPath, exportGroupPath) then
|
||||
relGroupPath = TSM.Groups.Path.GetRelative(groupPath, exportGroupPath)
|
||||
if not includeSubGroups[relGroupPath] then
|
||||
relGroupPath = nil
|
||||
end
|
||||
elseif groupPath == exportGroupPath then
|
||||
relGroupPath = TSM.CONST.ROOT_GROUP_PATH
|
||||
end
|
||||
if relGroupPath then
|
||||
groups[relGroupPath] = true
|
||||
for _, itemString in TSM.Groups.ItemIterator(groupPath) do
|
||||
items[itemString] = relGroupPath
|
||||
end
|
||||
if not excludeOperations then
|
||||
groupOperations[relGroupPath] = {}
|
||||
for moduleName in pairs(EXPORT_OPERATION_MODULES) do
|
||||
groupOperations[relGroupPath][moduleName] = {
|
||||
-- always override at the top-level
|
||||
override = TSM.Groups.HasOperationOverride(groupPath, moduleName) or groupPath == exportGroupPath or nil,
|
||||
}
|
||||
for _, operationName, operationSettings in TSM.Operations.GroupOperationIterator(moduleName, groupPath) do
|
||||
tinsert(groupOperations[relGroupPath][moduleName], operationName)
|
||||
operations[moduleName][operationName] = operationSettings
|
||||
private.isOperationSettingsTable[operationSettings] = true
|
||||
if not excludeCustomSources then
|
||||
for key in pairs(EXPORT_CUSTOM_STRINGS[moduleName]) do
|
||||
private.GetCustomSources(operationSettings[key], customSources)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local serialized = LibSerialize:SerializeEx(SERIALIZE_OPTIONS, MAGIC_STR, VERSION, exportGroupName, items, groups, groupOperations, operations, customSources)
|
||||
local compressed = LibDeflate:EncodeForPrint(LibDeflate:CompressDeflate(serialized))
|
||||
local numItems = Table.Count(items)
|
||||
local numSubGroups = Table.Count(groups) - 1
|
||||
local numOperations = 0
|
||||
for _, moduleOperations in pairs(operations) do
|
||||
numOperations = numOperations + Table.Count(moduleOperations)
|
||||
end
|
||||
local numCustomSources = Table.Count(customSources)
|
||||
|
||||
wipe(private.isOperationSettingsTable)
|
||||
TempTable.Release(customSources)
|
||||
TempTable.Release(operations)
|
||||
TempTable.Release(groupOperations)
|
||||
TempTable.Release(groups)
|
||||
TempTable.Release(items)
|
||||
|
||||
return compressed, numItems, numSubGroups, numOperations, numCustomSources
|
||||
end
|
||||
|
||||
function ImportExport.ProcessImport(str)
|
||||
return private.DecodeNewImport(str) or private.DecodeOldImport(str) or private.DecodeOldGroupOrItemListImport(str)
|
||||
end
|
||||
|
||||
function ImportExport.GetImportTotals()
|
||||
local numExistingItems = 0
|
||||
for itemString, groupPath in pairs(private.importContext.items) do
|
||||
if not private.importContext.filteredGroups[groupPath] then
|
||||
if TSM.Groups.IsItemInGroup(itemString) then
|
||||
numExistingItems = numExistingItems + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
wipe(private.importContext.customSources)
|
||||
local numOperations, numExistingOperations = 0, 0
|
||||
for moduleName, moduleOperations in pairs(private.importContext.operations) do
|
||||
local usedOperations = TempTable.Acquire()
|
||||
for groupPath, operations in pairs(private.importContext.groupOperations) do
|
||||
if not private.importContext.filteredGroups[groupPath] then
|
||||
for _, operationName in ipairs(operations[moduleName]) do
|
||||
usedOperations[operationName] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for operationName, operationSettings in pairs(moduleOperations) do
|
||||
if usedOperations[operationName] then
|
||||
numOperations = numOperations + 1
|
||||
if TSM.Operations.Exists(moduleName, operationName) then
|
||||
numExistingOperations = numExistingOperations + 1
|
||||
end
|
||||
for key in pairs(EXPORT_CUSTOM_STRINGS[moduleName]) do
|
||||
private.GetCustomSources(operationSettings[key], private.importContext.customSources)
|
||||
end
|
||||
end
|
||||
end
|
||||
TempTable.Release(usedOperations)
|
||||
end
|
||||
local numExistingCustomSources = 0
|
||||
for name in pairs(private.importContext.customSources) do
|
||||
if TSM.db.global.userData.customPriceSources[name] then
|
||||
numExistingCustomSources = numExistingCustomSources + 1
|
||||
end
|
||||
end
|
||||
local numItems = 0
|
||||
for _, groupPath in pairs(private.importContext.items) do
|
||||
if not private.importContext.filteredGroups[groupPath] then
|
||||
numItems = numItems + 1
|
||||
end
|
||||
end
|
||||
local numGroups = 0
|
||||
for groupPath in pairs(private.importContext.groups) do
|
||||
if not private.importContext.filteredGroups[groupPath] then
|
||||
numGroups = numGroups + 1
|
||||
end
|
||||
end
|
||||
return numItems, numGroups, numExistingItems, numOperations, numExistingOperations, numExistingCustomSources
|
||||
end
|
||||
|
||||
function ImportExport.PendingImportGroupIterator()
|
||||
assert(private.importContext.groupName)
|
||||
return pairs(private.importContext.groups)
|
||||
end
|
||||
|
||||
function ImportExport.GetPendingImportGroupName()
|
||||
assert(private.importContext.groupName)
|
||||
return private.importContext.groupName
|
||||
end
|
||||
|
||||
function ImportExport.SetGroupFiltered(groupPath, isFiltered)
|
||||
private.importContext.filteredGroups[groupPath] = isFiltered or nil
|
||||
end
|
||||
|
||||
function ImportExport.CommitImport(moveExistingItems, includeOperations, replaceOperations)
|
||||
assert(private.importContext.groupName)
|
||||
local numOperations, numCustomSources = 0, 0
|
||||
if includeOperations and next(private.importContext.operations) then
|
||||
-- remove filtered operations
|
||||
for moduleName, moduleOperations in pairs(private.importContext.operations) do
|
||||
local usedOperations = TempTable.Acquire()
|
||||
for groupPath, operations in pairs(private.importContext.groupOperations) do
|
||||
if not private.importContext.filteredGroups[groupPath] then
|
||||
for _, operationName in ipairs(operations[moduleName]) do
|
||||
usedOperations[operationName] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
for operationName in pairs(moduleOperations) do
|
||||
if not usedOperations[operationName] then
|
||||
moduleOperations[operationName] = nil
|
||||
end
|
||||
end
|
||||
TempTable.Release(usedOperations)
|
||||
end
|
||||
if not replaceOperations then
|
||||
-- remove existing operations and custom sources from the import context
|
||||
for moduleName, moduleOperations in pairs(private.importContext.operations) do
|
||||
for operationName in pairs(moduleOperations) do
|
||||
if TSM.Operations.Exists(moduleName, operationName) then
|
||||
moduleOperations[operationName] = nil
|
||||
end
|
||||
end
|
||||
if not next(moduleOperations) then
|
||||
private.importContext.operations[moduleName] = nil
|
||||
end
|
||||
end
|
||||
for name in pairs(private.importContext.customSources) do
|
||||
if TSM.db.global.userData.customPriceSources[name] then
|
||||
private.importContext.customSources[name] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
if next(private.importContext.customSources) then
|
||||
-- regenerate the list of custom sources in case some operations were filtered out
|
||||
wipe(private.importContext.customSources)
|
||||
for moduleName, moduleOperations in pairs(private.importContext.operations) do
|
||||
for _, operationSettings in pairs(moduleOperations) do
|
||||
for key in pairs(EXPORT_CUSTOM_STRINGS[moduleName]) do
|
||||
private.GetCustomSources(operationSettings[key], private.importContext.customSources)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- create the custom sources
|
||||
numCustomSources = Table.Count(private.importContext.customSources)
|
||||
CustomPrice.BulkCreateCustomPriceSourcesFromImport(private.importContext.customSources, replaceOperations)
|
||||
end
|
||||
-- create the operations
|
||||
for _, moduleOperations in pairs(private.importContext.operations) do
|
||||
numOperations = numOperations + Table.Count(moduleOperations)
|
||||
end
|
||||
TSM.Operations.BulkCreateFromImport(private.importContext.operations, replaceOperations)
|
||||
end
|
||||
if not includeOperations then
|
||||
wipe(private.importContext.groupOperations)
|
||||
end
|
||||
-- filter the groups
|
||||
for groupPath in pairs(private.importContext.filteredGroups) do
|
||||
private.importContext.groups[groupPath] = nil
|
||||
private.importContext.groupOperations[groupPath] = nil
|
||||
end
|
||||
for itemString, groupPath in pairs(private.importContext.items) do
|
||||
if private.importContext.filteredGroups[groupPath] then
|
||||
private.importContext.items[itemString] = nil
|
||||
end
|
||||
end
|
||||
-- create the groups
|
||||
local numItems = TSM.Groups.BulkCreateFromImport(private.importContext.groupName, private.importContext.items, private.importContext.groups, private.importContext.groupOperations, moveExistingItems)
|
||||
|
||||
-- print the message
|
||||
Log.PrintfUser(L["Imported group (%s) with %d items, %d operations, and %d custom sources."], private.importContext.groupName, numItems, numOperations, numCustomSources)
|
||||
ImportExport.ClearImportContext()
|
||||
end
|
||||
|
||||
function ImportExport.ClearImportContext()
|
||||
private.importContext.groupName = nil
|
||||
private.importContext.items = nil
|
||||
private.importContext.groups = nil
|
||||
private.importContext.groupOperations = nil
|
||||
private.importContext.operations = nil
|
||||
private.importContext.customSources = nil
|
||||
wipe(private.importContext.filteredGroups)
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.GetCustomSources(str, result)
|
||||
for _, name, customSourceStr in CustomPrice.DependantCustomSourceIterator(str) do
|
||||
if not result[name] then
|
||||
result[name] = customSourceStr
|
||||
private.GetCustomSources(customSourceStr, result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function private.DecodeNewImport(str)
|
||||
-- decode and decompress (if it's not a new import, the decode should fail)
|
||||
str = LibDeflate:DecodeForPrint(str)
|
||||
if not str then
|
||||
Log.Info("Not a valid new import string")
|
||||
return false
|
||||
end
|
||||
local numExtraBytes = nil
|
||||
str, numExtraBytes = LibDeflate:DecompressDeflate(str)
|
||||
if not str then
|
||||
Log.Err("Failed to decompress new import string")
|
||||
return false
|
||||
elseif numExtraBytes > 0 then
|
||||
Log.Err("Import string had extra bytes")
|
||||
return false
|
||||
end
|
||||
|
||||
-- deserialize and validate the data
|
||||
local success, magicStr, version, groupName, items, groups, groupOperations, operations, customSources = LibSerialize:Deserialize(str)
|
||||
if not success then
|
||||
Log.Err("Failed to deserialize new import string")
|
||||
return false
|
||||
elseif magicStr ~= MAGIC_STR then
|
||||
Log.Err("Invalid magic string: "..tostring(magicStr))
|
||||
return false
|
||||
elseif version ~= VERSION then
|
||||
Log.Err("Invalid version: "..tostring(version))
|
||||
return false
|
||||
elseif type(groupName) ~= "string" or groupName == "" or strmatch(groupName, TSM.CONST.GROUP_SEP) then
|
||||
Log.Err("Invalid groupName: "..tostring(groupName))
|
||||
return false
|
||||
elseif type(items) ~= "table" then
|
||||
Log.Err("Invalid items type: "..tostring(items))
|
||||
return false
|
||||
elseif type(groups) ~= "table" then
|
||||
Log.Err("Invalid groups type: "..tostring(groups))
|
||||
return false
|
||||
elseif type(groupOperations) ~= "table" then
|
||||
Log.Err("Invalid groupOperations type: "..tostring(groupOperations))
|
||||
return false
|
||||
elseif type(operations) ~= "table" then
|
||||
Log.Err("Invalid operations type: "..tostring(operations))
|
||||
return false
|
||||
elseif type(customSources) ~= "table" then
|
||||
Log.Err("Invalid customSources type: "..tostring(customSources))
|
||||
return false
|
||||
end
|
||||
|
||||
-- validate the groups table
|
||||
for groupPath, trueValue in pairs(groups) do
|
||||
if not private.IsValidGroupPath(groupPath) then
|
||||
Log.Err("Invalid groupPath (%s)", tostring(groupPath))
|
||||
return false
|
||||
elseif trueValue ~= true then
|
||||
Log.Err("Invalid true value (%s)", tostring(trueValue))
|
||||
return false
|
||||
end
|
||||
end
|
||||
for groupPath in pairs(groups) do
|
||||
local parentPath = TSM.Groups.Path.Split(groupPath)
|
||||
while parentPath do
|
||||
if not groups[parentPath] then
|
||||
Log.Err("Orphaned group (%s)", groupPath)
|
||||
return false
|
||||
end
|
||||
parentPath = TSM.Groups.Path.Split(parentPath)
|
||||
end
|
||||
end
|
||||
|
||||
-- validate the items table
|
||||
local numInvalidItems = 0
|
||||
for itemString, groupPath in pairs(items) do
|
||||
if not private.IsValidGroupPath(groupPath) then
|
||||
Log.Err("Invalid groupPath (%s, %s)", tostring(itemString), tostring(groupPath))
|
||||
return false
|
||||
elseif not groups[groupPath] then
|
||||
Log.Err("Invalid item group (%s, %s)", itemString, groupPath)
|
||||
return false
|
||||
end
|
||||
local newItemString = type(itemString) == "string" and ItemString.Get(itemString) or nil
|
||||
if itemString ~= newItemString then
|
||||
-- just remove this one item and continue
|
||||
Log.Warn("Invalid itemString (%s, %s)", tostring(itemString), tostring(newItemString))
|
||||
items[itemString] = nil
|
||||
numInvalidItems = numInvalidItems + 1
|
||||
end
|
||||
end
|
||||
if not next(items) and numInvalidItems > 0 then
|
||||
Log.Err("All items were invalid")
|
||||
return false
|
||||
end
|
||||
|
||||
-- validate the customSources table
|
||||
for name, customSourceStr in pairs(customSources) do
|
||||
if type(name) ~= "string" or name == "" or gsub(name, "([a-z]+)", "") ~= "" then
|
||||
Log.Err("Invalid name (%s)", tostring(name))
|
||||
return false
|
||||
elseif type(str) ~= "string" then
|
||||
Log.Err("Invalid str (%s)", tostring(customSourceStr))
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- validate the operations table
|
||||
local numChangedOperations = private.ValidateOperationsTable(operations, true)
|
||||
if not numChangedOperations then
|
||||
return false
|
||||
end
|
||||
|
||||
-- validate the groupOperations table
|
||||
if not private.ValidateGroupOperationsTable(groupOperations, groups, operations, true) then
|
||||
return false
|
||||
end
|
||||
|
||||
if numInvalidItems > 0 then
|
||||
Log.PrintfUser(L["NOTE: The import contained %d invalid items which were ignored."], numInvalidItems)
|
||||
end
|
||||
if numChangedOperations > 0 then
|
||||
Log.PrintfUser(L["NOTE: The import contained %d operations with at least one invalid setting which was reset."], numChangedOperations)
|
||||
end
|
||||
|
||||
Log.Info("Decoded new import string")
|
||||
private.importContext.groupName = private.DedupImportGroupName(groupName)
|
||||
private.importContext.items = items
|
||||
private.importContext.groups = groups
|
||||
private.importContext.groupOperations = groupOperations
|
||||
private.importContext.operations = operations
|
||||
private.importContext.customSources = customSources
|
||||
return true
|
||||
end
|
||||
|
||||
function private.DecodeOldImport(str)
|
||||
if strsub(str, 1, 1) ~= "^" then
|
||||
Log.Info("Not an old import string")
|
||||
return false
|
||||
end
|
||||
|
||||
local isValid, data = AceSerializer:Deserialize(str)
|
||||
if not isValid then
|
||||
Log.Err("Failed to deserialize")
|
||||
return false
|
||||
elseif type(data) ~= "table" then
|
||||
Log.Err("Invalid data type (%s)", tostring(data))
|
||||
return false
|
||||
elseif data.operations ~= nil and type(data.operations) ~= "table" then
|
||||
Log.Err("Invalid operations type (%s)", tostring(data.operations))
|
||||
return false
|
||||
elseif data.groupExport ~= nil and type(data.groupExport) ~= "string" then
|
||||
Log.Err("Invalid groupExport type (%s)", tostring(data.groupExport))
|
||||
return false
|
||||
elseif data.groupOperations ~= nil and type(data.groupOperations) ~= "table" then
|
||||
Log.Err("Invalid groupOperations type (%s)", tostring(data.groupOperations))
|
||||
return false
|
||||
elseif not data.operations and not data.groupExport then
|
||||
Log.Err("Doesn't contain operations or groupExport")
|
||||
return false
|
||||
end
|
||||
local operations, numChangedOperations = nil, 0
|
||||
if data.operations then
|
||||
numChangedOperations = private.ValidateOperationsTable(data.operations, false)
|
||||
if not numChangedOperations then
|
||||
return false
|
||||
end
|
||||
operations = data.operations
|
||||
else
|
||||
operations = {}
|
||||
end
|
||||
local items, groups, numInvalidItems = nil, nil, nil
|
||||
if data.groupExport then
|
||||
items, groups, numInvalidItems = private.DecodeGroupExportHelper(data.groupExport)
|
||||
if not items then
|
||||
Log.Err("No items found")
|
||||
return false
|
||||
end
|
||||
else
|
||||
items = {}
|
||||
groups = {}
|
||||
numInvalidItems = 0
|
||||
end
|
||||
local groupOperations = nil
|
||||
if data.groupOperations then
|
||||
Log.Info("Parsing group operations")
|
||||
local changeGroupPaths = TempTable.Acquire()
|
||||
for groupPath in pairs(data.groupOperations) do
|
||||
-- We export a "," in a group path as "``"
|
||||
local newGroupPath = type(groupPath) == "string" and gsub(groupPath, "``", ",")
|
||||
if newGroupPath and newGroupPath ~= groupPath then
|
||||
changeGroupPaths[groupPath] = newGroupPath
|
||||
if data.groupOperations[newGroupPath] then
|
||||
Log.Err("Duplicated group operations (%s, %s)", tostring(groupPath), tostring(newGroupPath))
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
for groupPath, newGroupPath in pairs(changeGroupPaths) do
|
||||
data.groupOperations[newGroupPath] = data.groupOperations[groupPath]
|
||||
data.groupOperations[groupPath] = nil
|
||||
end
|
||||
TempTable.Release(changeGroupPaths)
|
||||
if not private.ValidateGroupOperationsTable(data.groupOperations, groups, operations, false) then
|
||||
Log.Err("Invalid group operations")
|
||||
return false
|
||||
end
|
||||
groupOperations = data.groupOperations
|
||||
else
|
||||
groupOperations = {}
|
||||
end
|
||||
|
||||
-- check if there's a common top-level group within the import
|
||||
local commonTopLevelGroup = private.GetCommonTopLevelGroup(items, groups, groupOperations)
|
||||
if commonTopLevelGroup then
|
||||
private.UpdateTopLevelGroup(commonTopLevelGroup, items, groups, groupOperations)
|
||||
end
|
||||
|
||||
if numInvalidItems > 0 then
|
||||
Log.PrintfUser(L["NOTE: The import contained %d invalid items which were ignored."], numInvalidItems)
|
||||
end
|
||||
if numChangedOperations > 0 then
|
||||
Log.PrintfUser(L["NOTE: The import contained %d operations with at least one invalid setting which was reset."], numChangedOperations)
|
||||
end
|
||||
|
||||
Log.Info("Decoded old import string")
|
||||
private.importContext.groupName = private.DedupImportGroupName(commonTopLevelGroup or L["Imported Group"])
|
||||
private.importContext.items = items
|
||||
private.importContext.groups = groups
|
||||
private.importContext.groupOperations = groupOperations
|
||||
private.importContext.operations = operations
|
||||
private.importContext.customSources = {}
|
||||
return true
|
||||
end
|
||||
|
||||
function private.DecodeOldGroupOrItemListImport(str)
|
||||
local items, groups, numInvalidItems = private.DecodeGroupExportHelper(str)
|
||||
if not items then
|
||||
Log.Err("No items found")
|
||||
return false
|
||||
end
|
||||
local groupOperations = {}
|
||||
|
||||
-- check if there's a common top-level group within the import
|
||||
local commonTopLevelGroup = private.GetCommonTopLevelGroup(items, groups, groupOperations)
|
||||
if commonTopLevelGroup then
|
||||
private.UpdateTopLevelGroup(commonTopLevelGroup, items, groups, groupOperations)
|
||||
end
|
||||
|
||||
if numInvalidItems > 0 then
|
||||
Log.PrintfUser(L["NOTE: The import contained %d invalid items which were ignored."], numInvalidItems)
|
||||
end
|
||||
|
||||
Log.Info("Decoded old group or item list")
|
||||
private.importContext.groupName = private.DedupImportGroupName(commonTopLevelGroup or L["Imported Group"])
|
||||
private.importContext.items = items
|
||||
private.importContext.groups = groups
|
||||
private.importContext.groupOperations = groupOperations
|
||||
private.importContext.operations = {}
|
||||
private.importContext.customSources = {}
|
||||
return true
|
||||
end
|
||||
|
||||
function private.DecodeGroupExportHelper(str)
|
||||
local items, groups, numInvalidItems = nil, nil, 0
|
||||
if strmatch(str, "^[ip0-9%-:;]+$") then
|
||||
-- this is likely a list of itemStrings separated by semicolons instead of commas, so attempt to fix it
|
||||
str = gsub(str, ";", ",")
|
||||
end
|
||||
if strmatch(str, "^[0-9,]+$") then
|
||||
-- this is likely a list of itemIds separated by commas, so attempt to fix it
|
||||
str = gsub(str, "[0-9]+", "i:%1")
|
||||
end
|
||||
local relativePath = TSM.CONST.ROOT_GROUP_PATH
|
||||
for part in String.SplitIterator(str, ",") do
|
||||
part = strtrim(part)
|
||||
local groupPath = strmatch(part, "^group:(.+)$")
|
||||
local itemString = strmatch(part, "^[ip]?:?[0-9%-:]+$")
|
||||
local newItemString = itemString and ItemString.Get(itemString) or nil
|
||||
if newItemString and newItemString ~= itemString then
|
||||
itemString = newItemString
|
||||
numInvalidItems = numInvalidItems + 1
|
||||
end
|
||||
assert(not groupPath or not itemString)
|
||||
if groupPath then
|
||||
-- We export a "," in a group path as "``"
|
||||
groupPath = gsub(groupPath, "``", ",")
|
||||
if not private.IsValidGroupPath(groupPath) then
|
||||
Log.Err("Invalid groupPath (%s)", tostring(groupPath))
|
||||
return
|
||||
end
|
||||
relativePath = groupPath
|
||||
groups = groups or {}
|
||||
-- create the groups all the way up to the root
|
||||
while groupPath do
|
||||
groups[groupPath] = true
|
||||
groupPath = TSM.Groups.Path.GetParent(groupPath)
|
||||
end
|
||||
elseif itemString then
|
||||
items = items or {}
|
||||
groups = groups or {}
|
||||
groups[relativePath] = true
|
||||
items[itemString] = relativePath
|
||||
else
|
||||
Log.Err("Unknown part: %s", part)
|
||||
return
|
||||
end
|
||||
end
|
||||
return items, groups, numInvalidItems
|
||||
end
|
||||
|
||||
function private.ValidateOperationsTable(operations, strict)
|
||||
local numChangedOperations = 0
|
||||
for moduleName, moduleOperations in pairs(operations) do
|
||||
local isInvalidModuleName, isNotExportOperationModule = private.IsValidOperationModule(moduleName)
|
||||
if not isInvalidModuleName then
|
||||
Log.Err("Invalid module name")
|
||||
return nil
|
||||
elseif isNotExportOperationModule then
|
||||
if strict then
|
||||
Log.Err("Invalid moduleName (%s)", tostring(moduleName))
|
||||
return nil
|
||||
else
|
||||
Log.Warn("Ignoring module (%s)", moduleName)
|
||||
operations[moduleName] = nil
|
||||
wipe(moduleOperations)
|
||||
end
|
||||
elseif type(moduleOperations) ~= "table" then
|
||||
Log.Err("Invalid moduleOperations type (%s)", tostring(moduleOperations))
|
||||
return nil
|
||||
end
|
||||
for operationName, operationSettings in pairs(moduleOperations) do
|
||||
if type(operationName) ~= "string" or not TSM.Operations.IsValidName(operationName) then
|
||||
Log.Err("Invalid operationName (%s)", tostring(operationName))
|
||||
return nil
|
||||
elseif type(operationSettings) ~= "table" then
|
||||
Log.Err("Invalid operationSettings type (%s)", tostring(operationSettings))
|
||||
return nil
|
||||
end
|
||||
-- sanitize the operation settings
|
||||
if TSM.Operations.SanitizeSettings(moduleName, operationName, operationSettings, true, true) then
|
||||
numChangedOperations = numChangedOperations + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return numChangedOperations
|
||||
end
|
||||
|
||||
function private.ValidateGroupOperationsTable(groupOperations, groups, operations, strict)
|
||||
for groupPath, groupsOperationsTable in pairs(groupOperations) do
|
||||
if not private.IsValidGroupPath(groupPath) then
|
||||
Log.Err("Invalid groupPath (%s)", tostring(groupPath))
|
||||
return false
|
||||
elseif not groups[groupPath] then
|
||||
if strict then
|
||||
Log.Err("Invalid group (%s)", groupPath)
|
||||
return false
|
||||
else
|
||||
Log.Info("Creating group with operations (%s)", groupPath)
|
||||
groups[groupPath] = true
|
||||
end
|
||||
end
|
||||
if not strict then
|
||||
groupsOperationsTable.ignoreItemVariations = nil
|
||||
end
|
||||
for moduleName, moduleOperations in pairs(groupsOperationsTable) do
|
||||
local isInvalidModuleName, isNotExportOperationModule = private.IsValidOperationModule(moduleName)
|
||||
if not isInvalidModuleName then
|
||||
Log.Err("Invalid module name")
|
||||
return false
|
||||
elseif isNotExportOperationModule then
|
||||
if strict then
|
||||
Log.Err("Invalid moduleName (%s)", tostring(moduleName))
|
||||
return false
|
||||
else
|
||||
Log.Warn("Ignoring module (%s)", moduleName)
|
||||
groupsOperationsTable[moduleName] = nil
|
||||
wipe(moduleOperations)
|
||||
end
|
||||
elseif type(moduleOperations) ~= "table" then
|
||||
Log.Err("Invalid moduleOperations type (%s)", tostring(moduleOperations))
|
||||
return false
|
||||
elseif moduleOperations.override ~= nil and moduleOperations.override ~= true then
|
||||
Log.Err("Invalid moduleOperations override type (%s)", tostring(moduleOperations.override))
|
||||
return false
|
||||
elseif groupPath == TSM.CONST.ROOT_GROUP_PATH and not moduleOperations.override then
|
||||
if strict then
|
||||
Log.Err("Top-level group does not have override set")
|
||||
return false
|
||||
else
|
||||
Log.Info("Setting override for top-level group")
|
||||
moduleOperations.override = true
|
||||
end
|
||||
end
|
||||
local numOperations = #moduleOperations
|
||||
if numOperations > TSM.Operations.GetMaxNumber(moduleName) then
|
||||
Log.Err("Too many operations (%s, %s, %d)", groupPath, moduleName, numOperations)
|
||||
return false
|
||||
end
|
||||
for k, v in pairs(moduleOperations) do
|
||||
if k == "override" then
|
||||
-- pass
|
||||
elseif type(k) ~= "number" or k < 1 or k > numOperations then
|
||||
Log.Err("Unknown key (%s, %s, %s, %s)", groupPath, moduleName, tostring(k), tostring(v))
|
||||
return false
|
||||
elseif type(v) ~= "string" then
|
||||
Log.Err("Invalid value (%s, %s, %s, %s)", groupPath, moduleName, k, tostring(v))
|
||||
return false
|
||||
end
|
||||
end
|
||||
-- some old imports had "" operations attached to groups, so remove them
|
||||
for i = #moduleOperations, 1, -1 do
|
||||
if moduleOperations[i] == "" then
|
||||
tremove(moduleOperations, i)
|
||||
end
|
||||
end
|
||||
for _, operationName in ipairs(moduleOperations) do
|
||||
if type(operationName) ~= "string" or not TSM.Operations.IsValidName(operationName) then
|
||||
Log.Err("Invalid operationName (%s)", tostring(operationName))
|
||||
return false
|
||||
elseif not operations[moduleName][operationName] then
|
||||
Log.Err("Unknown operation (%s)", operationName)
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function private.DedupImportGroupName(groupName)
|
||||
if TSM.Groups.Exists(groupName) then
|
||||
local num = 1
|
||||
while TSM.Groups.Exists(groupName.." "..num) do
|
||||
num = num + 1
|
||||
end
|
||||
groupName = groupName.." "..num
|
||||
end
|
||||
return groupName
|
||||
end
|
||||
|
||||
function private.IsValidGroupPath(groupPath)
|
||||
return type(groupPath) == "string" and not strmatch(groupPath, "^`") and not strmatch(groupPath, "`$") and not strmatch(groupPath, "``")
|
||||
end
|
||||
|
||||
function private.IsValidOperationModule(moduleName)
|
||||
if type(moduleName) ~= "string" then
|
||||
Log.Err("Invalid moduleName (%s)", tostring(moduleName))
|
||||
return false
|
||||
elseif not TSM.Operations.ModuleExists(moduleName) then
|
||||
Log.Err("Invalid moduleName (%s)", tostring(moduleName))
|
||||
return false
|
||||
elseif not EXPORT_OPERATION_MODULES[moduleName] then
|
||||
return true, true
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function private.GetCommonTopLevelGroup(items, groups, groupOperations)
|
||||
local commonTopLevelGroup = nil
|
||||
|
||||
-- check the items
|
||||
for _, groupPath in pairs(items) do
|
||||
if groupPath == TSM.CONST.ROOT_GROUP_PATH then
|
||||
return nil
|
||||
end
|
||||
local topLevelGroup = TSM.Groups.Path.GetTopLevel(groupPath)
|
||||
if not commonTopLevelGroup then
|
||||
commonTopLevelGroup = topLevelGroup
|
||||
elseif topLevelGroup ~= commonTopLevelGroup then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- check the groups
|
||||
for groupPath in pairs(groups) do
|
||||
if groupPath ~= TSM.CONST.ROOT_GROUP_PATH then
|
||||
local topLevelGroup = TSM.Groups.Path.GetTopLevel(groupPath)
|
||||
if not commonTopLevelGroup then
|
||||
commonTopLevelGroup = topLevelGroup
|
||||
elseif topLevelGroup ~= commonTopLevelGroup then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- check the groupOperations
|
||||
for groupPath in pairs(groupOperations) do
|
||||
if groupPath == TSM.CONST.ROOT_GROUP_PATH then
|
||||
return nil
|
||||
end
|
||||
local topLevelGroup = TSM.Groups.Path.GetTopLevel(groupPath)
|
||||
if not commonTopLevelGroup then
|
||||
commonTopLevelGroup = topLevelGroup
|
||||
elseif topLevelGroup ~= commonTopLevelGroup then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
return commonTopLevelGroup
|
||||
end
|
||||
|
||||
function private.UpdateTopLevelGroup(topLevelGroup, items, groups, groupOperations)
|
||||
-- update items
|
||||
for itemString, groupPath in pairs(items) do
|
||||
items[itemString] = TSM.Groups.Path.GetRelative(groupPath, topLevelGroup)
|
||||
end
|
||||
|
||||
-- update groups
|
||||
local newGroups = TempTable.Acquire()
|
||||
groups[TSM.CONST.ROOT_GROUP_PATH] = nil
|
||||
for groupPath in pairs(groups) do
|
||||
newGroups[TSM.Groups.Path.GetRelative(groupPath, topLevelGroup)] = true
|
||||
end
|
||||
wipe(groups)
|
||||
for groupPath in pairs(newGroups) do
|
||||
groups[groupPath] = true
|
||||
end
|
||||
TempTable.Release(newGroups)
|
||||
|
||||
-- update groupOperations
|
||||
local newGroupOperations = TempTable.Acquire()
|
||||
for groupPath, groupOperationsTable in pairs(groupOperations) do
|
||||
newGroupOperations[TSM.Groups.Path.GetRelative(groupPath, topLevelGroup)] = groupOperationsTable
|
||||
end
|
||||
wipe(groupOperations)
|
||||
for groupPath, groupOperationsTable in pairs(newGroupOperations) do
|
||||
groupOperations[groupPath] = groupOperationsTable
|
||||
end
|
||||
TempTable.Release(newGroupOperations)
|
||||
|
||||
-- set override on new top-level group
|
||||
if groupOperations[TSM.CONST.ROOT_GROUP_PATH] then
|
||||
for _, moduleOperations in pairs(groupOperations[TSM.CONST.ROOT_GROUP_PATH]) do
|
||||
moduleOperations.override = true
|
||||
end
|
||||
end
|
||||
end
|
||||
81
Core/Service/Groups/Path.lua
Normal file
81
Core/Service/Groups/Path.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local _, TSM = ...
|
||||
local Path = TSM.Groups:NewPackage("Path")
|
||||
local String = TSM.Include("Util.String")
|
||||
local private = {}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Module Functions
|
||||
-- ============================================================================
|
||||
|
||||
function Path.GetName(groupPath)
|
||||
local _, name = private.SplitPath(groupPath)
|
||||
return name
|
||||
end
|
||||
|
||||
function Path.GetParent(groupPath)
|
||||
local parentPath = private.SplitPath(groupPath)
|
||||
return parentPath
|
||||
end
|
||||
|
||||
function Path.Split(groupPath)
|
||||
return private.SplitPath(groupPath)
|
||||
end
|
||||
|
||||
function Path.Join(...)
|
||||
if select(1, ...) == TSM.CONST.ROOT_GROUP_PATH then
|
||||
return Path.Join(select(2, ...))
|
||||
end
|
||||
return strjoin(TSM.CONST.GROUP_SEP, ...)
|
||||
end
|
||||
|
||||
function Path.IsChild(groupPath, parentPath)
|
||||
if parentPath == TSM.CONST.ROOT_GROUP_PATH then
|
||||
return groupPath ~= TSM.CONST.ROOT_GROUP_PATH
|
||||
end
|
||||
return strmatch(groupPath, "^"..String.Escape(parentPath)..TSM.CONST.GROUP_SEP) and true or false
|
||||
end
|
||||
|
||||
function Path.Format(groupPath)
|
||||
if not groupPath then return end
|
||||
local result = gsub(groupPath, TSM.CONST.GROUP_SEP, "->")
|
||||
return result
|
||||
end
|
||||
|
||||
function Path.GetRelative(groupPath, prefixGroupPath)
|
||||
if groupPath == prefixGroupPath then
|
||||
return TSM.CONST.ROOT_GROUP_PATH
|
||||
end
|
||||
local relativePath, numSubs = gsub(groupPath, "^"..String.Escape(prefixGroupPath)..TSM.CONST.GROUP_SEP, "")
|
||||
assert(numSubs == 1 and relativePath)
|
||||
return relativePath
|
||||
end
|
||||
|
||||
function Path.GetTopLevel(groupPath)
|
||||
assert(groupPath ~= TSM.CONST.ROOT_GROUP_PATH)
|
||||
return strmatch(groupPath, "^([^"..TSM.CONST.GROUP_SEP.."]+)")
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.SplitPath(groupPath)
|
||||
local parentPath, groupName = strmatch(groupPath, "^(.+)"..TSM.CONST.GROUP_SEP.."([^"..TSM.CONST.GROUP_SEP.."]+)$")
|
||||
if parentPath then
|
||||
return parentPath, groupName
|
||||
elseif groupPath ~= TSM.CONST.ROOT_GROUP_PATH then
|
||||
return TSM.CONST.ROOT_GROUP_PATH, groupPath
|
||||
else
|
||||
return nil, groupPath
|
||||
end
|
||||
end
|
||||
99
Core/Service/Groups/Sync.lua
Normal file
99
Core/Service/Groups/Sync.lua
Normal file
@@ -0,0 +1,99 @@
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
-- TradeSkillMaster --
|
||||
-- https://tradeskillmaster.com --
|
||||
-- All Rights Reserved - Detailed license information included with addon. --
|
||||
-- ------------------------------------------------------------------------------ --
|
||||
|
||||
local _, TSM = ...
|
||||
local GroupsSync = TSM.Groups:NewPackage("Sync")
|
||||
local L = TSM.Include("Locale").GetTable()
|
||||
local TempTable = TSM.Include("Util.TempTable")
|
||||
local Math = TSM.Include("Util.Math")
|
||||
local Log = TSM.Include("Util.Log")
|
||||
local Sync = TSM.Include("Service.Sync")
|
||||
local private = {}
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- New Modules Functions
|
||||
-- ============================================================================
|
||||
|
||||
function GroupsSync.OnInitialize()
|
||||
Sync.RegisterRPC("CREATE_PROFILE", private.RPCCreateProfile)
|
||||
end
|
||||
|
||||
function GroupsSync.SendCurrentProfile(targetPlayer)
|
||||
local profileName = TSM.db:GetCurrentProfile()
|
||||
local data = TempTable.Acquire()
|
||||
data.groups = TempTable.Acquire()
|
||||
for groupPath, moduleOperations in pairs(TSM.db:Get("profile", profileName, "userData", "groups")) do
|
||||
data.groups[groupPath] = {}
|
||||
for _, module in TSM.Operations.ModuleIterator() do
|
||||
local operations = moduleOperations[module]
|
||||
if operations.override then
|
||||
data.groups[groupPath][module] = operations
|
||||
end
|
||||
end
|
||||
end
|
||||
data.items = TSM.db:Get("profile", profileName, "userData", "items")
|
||||
data.operations = TSM.db:Get("profile", profileName, "userData", "operations")
|
||||
local result, estimatedTime = Sync.CallRPC("CREATE_PROFILE", targetPlayer, private.RPCCreateProfileResultHandler, profileName, UnitName("player"), data)
|
||||
if result then
|
||||
estimatedTime = max(Math.Round(estimatedTime, 60), 60)
|
||||
Log.PrintfUser(L["Sending your '%s' profile to %s. Please keep both characters online until this completes. This will take approximately: %s"], profileName, targetPlayer, SecondsToTime(estimatedTime))
|
||||
else
|
||||
Log.PrintUser(L["Failed to send profile. Ensure both characters are online and try again."])
|
||||
end
|
||||
TempTable.Release(data.groups)
|
||||
TempTable.Release(data)
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- Private Helper Functions
|
||||
-- ============================================================================
|
||||
|
||||
function private.CopyTable(srcTbl, dstTbl)
|
||||
for k, v in pairs(srcTbl) do
|
||||
dstTbl[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function private.RPCCreateProfile(profileName, playerName, data)
|
||||
assert(TSM.db:IsValidProfileName(profileName))
|
||||
if TSM.db:ProfileExists(profileName) then
|
||||
return false, L["A profile with that name already exists on the target account. Rename it first and try again."]
|
||||
end
|
||||
|
||||
-- create and switch to the new profile
|
||||
local currentProfile = TSM.db:GetCurrentProfile()
|
||||
TSM.db:SetProfile(profileName)
|
||||
|
||||
-- copy all the data into this profile
|
||||
private.CopyTable(data.groups, TSM.db.profile.userData.groups)
|
||||
private.CopyTable(data.items, TSM.db.profile.userData.items)
|
||||
TSM.Operations.ReplaceProfileOperations(data.operations)
|
||||
|
||||
-- switch back to our previous profile
|
||||
TSM.db:SetProfile(currentProfile)
|
||||
|
||||
Log.PrintfUser(L["Added '%s' profile which was received from %s."], profileName, playerName)
|
||||
|
||||
return true, profileName, UnitName("player")
|
||||
end
|
||||
|
||||
function private.RPCCreateProfileResultHandler(success, ...)
|
||||
if success == nil then
|
||||
Log.PrintUser(L["Failed to send profile."].." "..L["Ensure both characters are online and try again."])
|
||||
return
|
||||
elseif not success then
|
||||
local errMsg = ...
|
||||
Log.PrintUser(L["Failed to send profile."].." "..errMsg)
|
||||
return
|
||||
end
|
||||
|
||||
local profileName, targetPlayer = ...
|
||||
Log.PrintfUser(L["Successfully sent your '%s' profile to %s!"], profileName, targetPlayer)
|
||||
end
|
||||
Reference in New Issue
Block a user