Move Local Media to Media Records.fh_lua--[[
@Title: Move Local Media to Media Records
@Type: Standard
@Author: Mike Tate
@Contributors:
@Version: 1.1
@Keywords:
@LastUpdated: 18 Dec 2020
@Licence: This plugin is copyright (c) 2020 Mike Tate & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description: Move all Local Media Objects (LMO) to Media Records (including UDF but not Sort Date LMO).
@V1.1: Check if target file exists, cater for all possible Media tags for GEDCOM 5.5.1 FH V7 Lua 3.5 IUP 3.28;
@V1.0: First published version.
@V0.1-V0.2: Prototypes.
]]
if fhGetAppVersion() > 5 then fhSetStringEncoding("UTF-8") end -- Cater for FH V6 Unicode
--[[
@Module: +fh+progbar_v3
@Author: Mike Tate
@Version: 3.0
@LastUpdated: 27 Aug 2020
@Description: Progress Bar library module.
@V3.0: Function Prototype Closure version.
@V1.0: Initial version.
]]
local function progbar_v3()
local fh = {} -- Local environment table
require "iuplua" -- To access GUI window builder
iup.SetGlobal("CUSTOMQUITMESSAGE","YES") -- Needed for IUP 3.28
local tblBars = {} -- Table for optional external attributes
local strBack = "255 255 255" -- Background colour default is white
local strBody = "0 0 0" -- Body text colour default is black
local strFont = nil -- Font dialogue default is current font
local strStop = "255 0 0" -- Stop button colour default is red
local intPosX = iup.CENTER -- Show window default position is central
local intPosY = iup.CENTER
local intMax, intVal, intPercent, intStart, intDelta, intScale, strClock, isBarStop
local lblText, barGauge, lblDelta, btnStop, dlgGauge
local function doFocus() -- Bring the Progress Bar window into Focus
dlgGauge.BringFront="YES" -- If used too often, inhibits other windows scroll bars, etc
end -- local function doFocus
local function doUpdate() -- Update the Progress Gauge and the Delta % with clock
barGauge.Value = intVal
lblDelta.Title = string.format("%4d %% %s ",math.floor(intPercent),strClock)
end -- local function doUpdate
local function doReset() -- Reset all dialogue variables and Update display
intVal = 0 -- Current value of Progress Bar
intPercent= 0.01 -- Percentage of progress
intStart = os.time() -- Start time of progress
intDelta = 0 -- Delta time of progress
intScale = math.ceil( intMax / 1000 ) -- Scale of percentage per second of progress (initial guess is corrected in Step function)
strClock = "00 : 00 : 00" -- Clock delta time display
isBarStop = false -- Stop button pressed signal
doUpdate()
doFocus()
end -- local function doReset
function fh.Start(strTitle,intMaximum) -- Create & start Progress Bar window
if not dlgGauge then
strTitle = strTitle or "" -- Dialogue and button title
intMax = intMaximum or 100 -- Maximun range of Progress Bar, default is 100
local strSize = tostring( math.max( 100, string.len(" Stop "..strTitle) * 8 ) ).."x30" -- Adjust Stop button size to Title
lblText = iup.label { Title=" "; Expand="YES"; Alignment="ACENTER"; Tip="Progress Message"; }
barGauge = iup.progressbar { RasterSize="400x30"; Value=0; Max=intMax; Tip="Progress Bar"; }
lblDelta = iup.label { Title=" "; Expand="YES"; Alignment="ACENTER"; Tip="Percentage and Elapsed Time"; }
btnStop = iup.button { Title=" Stop "..strTitle; RasterSize=strSize; FgColor=strStop; Tip="Stop Progress Button"; action=function() isBarStop = true end; } -- Signal Stop button pressed return iup.CLOSE -- Often caused main GUI to close !!!
dlgGauge = iup.dialog { Title=strTitle.." Progress "; Font=strFont; FgColor=strBody; Background=strBack; DialogFrame="YES"; -- Remove Windows minimize/maximize menu
iup.vbox{ Alignment="ACENTER"; Gap="10"; Margin="10x10";
lblText;
barGauge;
lblDelta;
btnStop;
};
move_cb = function(self,x,y) tblBars.X = x tblBars.Y = y end;
close_cb = btnStop.action; -- Windows Close button = Stop button
}
if type(tblBars.GUI) == "table"
and type(tblBars.GUI.ShowDialogue) == "function" then
dlgGauge.move_cb = nil -- Use GUI library to show & move window
tblBars.GUI.ShowDialogue("Bars",dlgGauge,btnStop,"showxy")
else
dlgGauge:showxy(intPosX,intPosY) -- Show the Progress Bar window
end
doReset() -- Reset the Progress Bar display
end
end -- function Start
function fh.Message(strText) -- Show the Progress Bar message
if dlgGauge then lblText.Title = strText end
end -- function Message
function fh.Step(intStep) -- Step the Progress Bar forward
if dlgGauge then
intVal = intVal + ( intStep or 1 ) -- Default step is 1
local intNew = math.ceil( intVal / intMax * 100 * intScale ) / intScale
if intPercent ~= intNew then -- Update progress once per percent or per second, whichever is smaller
intPercent = math.max( 0.1, intNew ) -- Ensure percentage is greater than zero
if intVal > intMax then intVal = intMax intPercent = 100 end -- Ensure values do not exceed maximum
intNew = os.difftime(os.time(),intStart)
if intDelta < intNew then -- Update clock of elapsed time
intDelta = intNew
intScale = math.ceil( intDelta / intPercent ) -- Scale of seconds per percentage step
local intHour = math.floor( intDelta / 3600 )
local intMins = math.floor( intDelta / 60 - intHour * 60 )
local intSecs = intDelta - intMins * 60 - intHour * 3600
strClock = string.format("%02d : %02d : %02d",intHour,intMins,intSecs)
end
doUpdate() -- Update the Progress Bar display
end
iup.LoopStep()
end
end -- function Step
function fh.Focus() -- Bring the Progress Bar window to front
if dlgGauge then doFocus() end
end -- function Focus
function fh.Reset() -- Reset the Progress Bar display
if dlgGauge then doReset() end
end -- function Reset
function fh.Stop() -- Check if Stop button pressed
iup.LoopStep()
return isBarStop
end -- function Stop
function fh.Close() -- Close the Progress Bar window
isBarStop = false
if dlgGauge then dlgGauge:destroy() dlgGauge = nil end
end -- function Close
function fh.Setup(tblSetup) -- Setup optional table of external attributes
if tblSetup then
tblBars = tblSetup
strBack = tblBars.Back or strBack -- Background colour
strBody = tblBars.Body or strBody -- Body text colour
strFont = tblBars.Font or strFont -- Font dialogue
strStop = tblBars.Stop or strStop -- Stop button colour
intPosX = tblBars.X or intPosX -- Window position
intPosY = tblBars.Y or intPosY
end
end -- function Setup
return fh
end -- local function progbar_v3
local progbar = progbar_v3() -- To access FH progress bars module
require "lfs" -- To access LUA filing system
function FlgFileExists(strFileName) -- Check if file exists
return lfs.attributes(strFileName,"mode") == "file"
end -- function FlgFileExists
function CopyFile(strSource,strTarget) -- Copy media file and modification date-time
local fileSource = assert(io.open(strSource,"rb"))
local fileTarget = assert(io.open(strTarget,"wb"))
local anyContent = ""
repeat -- Copy entire file contents
fileTarget:write(anyContent)
anyContent = fileSource:read(10000)
until anyContent == nil
assert(fileSource:close())
assert(fileTarget:close())
lfs.touch(strTarget,os.time(),lfs.attributes(strSource,"modification"))
end -- function CopyFile
function NoteChildBranch(ptrSource,strTarget) -- Note child branch from source
return NoteChildren(ptrSource,strTarget..fhGetDisplayText(ptrSource))
end -- function NoteChildBranch
function NoteChildren(ptrSource,strTarget) -- Note child fields from source
local ptrFrom = ptrSource:Clone()
ptrFrom:MoveToFirstChildItem(ptrFrom)
while ptrFrom:IsNotNull() do
strTarget = NoteChildBranch(ptrFrom,strTarget)
ptrFrom:MoveNext()
end
return strTarget
end -- function NoteChildren
local ptrFile = fhNewItemPtr() -- V1.1
function CopyChildBranch(ptrSource,ptrTarget,arrTags) -- Copy child branch from source to target
local strTag = fhGetTag(ptrSource)
strTag = arrTags[strTag] or strTag -- Cater for FILE and NOTE tag
if strTag == "TITL" and fhGetAppVersion() > 6 then
ptrTarget = ptrFile:Clone() -- GEDCOM 5.5.1 TITL is per FILE in OBJE -- V1.1
end
local ptrNew = fhCreateItem(strTag,ptrTarget,true)
if ptrNew:IsNotNull() then
fhSetValue_Copy(ptrNew,ptrSource)
if strTag == "FILE" then ptrFile = ptrNew:Clone() end -- GEDCOM 5.5.1 TITL is per FILE in OBJE -- V1.1
else
local ptrNew = fhCreateItem("REFN",ptrTarget,true) -- Cater for UDF and multiple NOTE tags, etc
fhSetValueAsText(ptrNew,fhGetDisplayText(ptrSource))
end
CopyChildren(ptrSource,ptrNew)
end -- function CopyChildBranch
function CopyChildren(ptrSource,ptrTarget,arrTags) -- Copy child fields from source to target
local ptrFrom = ptrSource:Clone()
ptrFrom:MoveToFirstChildItem(ptrFrom)
while ptrFrom:IsNotNull() do
CopyChildBranch(ptrFrom,ptrTarget,arrTags or {})
ptrFrom:MoveNext()
end
end -- function CopyChildren
function IsSortDate(ptrItem) -- Does LMO conform to Sort Date format
return fhGetItemText(ptrItem,"~.TITL") == "Sort Date" and fhGetItemText(ptrItem,"~.FILE") == ""
end -- function IsSortDate
local dicTags = {} -- LMO to Record tag mapping for FH V7
local strFILE = "~.FILE"
if fhGetAppVersion() < 7 then
dicTags = { FILE="_FILE"; NOTE2="_NOTE"; } -- LMO to Record tag mapping for FH V6
strFILE = "~._FILE"
end
local arrRecs = { -- Record type to Name and Keywords mapping
{ Type = "INDI" ; Name = "Individual" ; Keys = "Picture" ; };
{ Type = "FAM" ; Name = "Family" ; Keys = "Picture" ; };
{ Type = "SOUR" ; Name = "Source" ; Keys = "Document" ; };
{ Type = "SUBM" ; Name = "Submitter" ; Keys = "Picture" ; };
}
local strData = fhGetContextInfo("CI_PROJECT_DATA_FOLDER").."\\" -- Project Data folder
function MoveLMO() -- Move each LMO to a Media Record
local dicObje = { }
local arrItem = { }
local intRecs = 0
local ptrItem = fhNewItemPtr()
for _, dicRec in ipairs (arrRecs) do -- Count all records that may contain LMO
ptrItem:MoveToFirstRecord(dicRec.Type)
while ptrItem:IsNotNull() do
intRecs = intRecs + 1
ptrItem:MoveNext()
end
end
progbar.Setup() -- Progress Bar prototype
if intRecs > 100 then
progbar.Start("Moving Local Media to Records",intRecs) -- Start the Progress Bar with number of required Records
end
local intFile = 0
ptrItem:MoveToFirstRecord("OBJE")
while ptrItem:IsNotNull() and math.abs(intFile) < 1000 do -- Search Media records to see if Media were imported or not
local strFile = fhGetItemText(ptrItem,strFILE) -- V1.1
if strFile:lower():match("^media\\") then
if FlgFileExists(strData..strFile) then
intFile = intFile + 1 -- Increment count for relative imported Media links
end
else
if FlgFileExists(strFile) then
intFile = intFile - 1 -- Decrement count for absolute external Media links
end
end
ptrItem:MoveNext()
end
for _, dicRec in ipairs (arrRecs) do -- Search all records that may contain LMO
ptrItem:MoveToFirstRecord(dicRec.Type)
while ptrItem:IsNotNull() do
local strTag = fhGetTag(ptrItem)
if ( strTag == "OBJE2" and not IsSortDate(ptrItem) ) -- Found LMO that is not a Sort Date
or ( strTag == "OBJE" and fhIsUDF(ptrItem) ) then -- Found LMO that is a UDF from Legacy
local strObje = NoteChildren(ptrItem,"") --? ,dicTags)
if #strObje > 9 then
local ptrObje = dicObje[strObje] -- Check if LMO is a duplicate
if not ptrObje then
ptrObje = fhCreateItem("OBJE") -- If not, then create new Media record
if ptrObje:IsNotNull() then
dicObje[strObje] = ptrObje:Clone()
CopyChildren(ptrItem,ptrObje,dicTags) -- Copy child fields and add Keyword
fhSetValueAsText(fhCreateItem("_KEYS",ptrObje),dicRec.Keys)
if intFile > 1 then -- Relative internal links prevail so copy Media file into Media folder
local ptr_File = fhGetItemPtr(ptrObje,strFILE) -- V1.1
local strSource = fhGetValueAsText(ptr_File)
local strTarget = strSource:gsub(".-([^\\]+)$","Media\\%1")
fhSetValueAsText(ptr_File,strTarget) -- Set relative file link
strTarget = strData..strTarget
if FlgFileExists(strSource) and not FlgFileExists(strTarget) then -- V1.1
CopyFile(strSource,strTarget)
end
end
end
end
if ptrObje:IsNotNull() then -- Save pointers to LMO and Record
table.insert(arrItem,{ Loc=ptrItem:Clone(); Rec=ptrObje:Clone(); } )
end
end
end
local intRid = fhGetRecordId(ptrItem) -- Obtain the Record Id
if intRid > 0 then
progbar.Message(dicRec.Name.." Record Id "..intRid) -- Update the Progress Bar per Record
progbar.Step(1)
isStop = progbar.Stop()
if isStop then break end -- Break out of inner loop
end
ptrItem:MoveNextSpecial()
end
if isStop then break end -- Break out of outer loop
end
for _, dicItem in ipairs ( arrItem ) do
local ptrItem = dicItem.Loc -- Delete this Local Media Object item
local ptrObje = dicItem.Rec -- Link this Media Record to parent of LMO
local ptrRoot = fhNewItemPtr()
ptrRoot:MoveToParentItem(ptrItem)
local ptrLink = fhCreateItem("OBJE",ptrRoot)
if fhIsUDF(ptrLink) or ptrLink:IsNull() then -- Cater for LMO UDF from Legacy
ptrRoot:MoveToParentItem(ptrRoot)
ptrLink = fhCreateItem("OBJE",ptrRoot)
end
ptrRoot:MoveToParentItem(ptrItem)
local isOK = fhSetValueAsLink(ptrLink,ptrObje)
local isOK = fhDeleteItem(ptrItem) -- Only children of UDF get deleted (bug)
ptrItem = fhGetItemPtr(ptrRoot,"~.OBJE")
if fhIsUDF(ptrItem) then
isOK = fhDeleteItem(ptrItem) -- Delete the UDF itself
end
progbar.Step(1)
end
progbar.Close() -- Close the Progress Bar
fhMessageBox( "\n " .. #arrItem .. " LMO converted into Media records. \n\n Use 'Edit > Undo Plugin Updates' to undo conversions. \n" )
end -- function MoveLMO
local strMsg = [[
This converts each Local Media Object to a Media Record
( except those that conform to the "Sort Date" format. )
Any UDF or other exceptions are saved in Custom Id fields.
Continue?
]]
if fhMessageBox(strMsg,"MB_OKCANCEL","MB_ICONQUESTION") == "OK" then
MoveLMO()
end
--[[
@Title: Move Local Media to Media Records
@Type: Standard
@Author: Mike Tate
@Contributors:
@Version: 1.1
@Keywords:
@LastUpdated: 18 Dec 2020
@Licence: This plugin is copyright (c) 2020 Mike Tate & contributors and is licensed under the MIT License which is hereby incorporated by reference (see https://pluginstore.family-historian.co.uk/fh-plugin-licence)
@Description: Move all Local Media Objects (LMO) to Media Records (including UDF but not Sort Date LMO).
@V1.1: Check if target file exists, cater for all possible Media tags for GEDCOM 5.5.1 FH V7 Lua 3.5 IUP 3.28;
@V1.0: First published version.
@V0.1-V0.2: Prototypes.
]]
if fhGetAppVersion() > 5 then fhSetStringEncoding("UTF-8") end -- Cater for FH V6 Unicode
--[[
@Module: +fh+progbar_v3
@Author: Mike Tate
@Version: 3.0
@LastUpdated: 27 Aug 2020
@Description: Progress Bar library module.
@V3.0: Function Prototype Closure version.
@V1.0: Initial version.
]]
local function progbar_v3()
local fh = {} -- Local environment table
require "iuplua" -- To access GUI window builder
iup.SetGlobal("CUSTOMQUITMESSAGE","YES") -- Needed for IUP 3.28
local tblBars = {} -- Table for optional external attributes
local strBack = "255 255 255" -- Background colour default is white
local strBody = "0 0 0" -- Body text colour default is black
local strFont = nil -- Font dialogue default is current font
local strStop = "255 0 0" -- Stop button colour default is red
local intPosX = iup.CENTER -- Show window default position is central
local intPosY = iup.CENTER
local intMax, intVal, intPercent, intStart, intDelta, intScale, strClock, isBarStop
local lblText, barGauge, lblDelta, btnStop, dlgGauge
local function doFocus() -- Bring the Progress Bar window into Focus
dlgGauge.BringFront="YES" -- If used too often, inhibits other windows scroll bars, etc
end -- local function doFocus
local function doUpdate() -- Update the Progress Gauge and the Delta % with clock
barGauge.Value = intVal
lblDelta.Title = string.format("%4d %% %s ",math.floor(intPercent),strClock)
end -- local function doUpdate
local function doReset() -- Reset all dialogue variables and Update display
intVal = 0 -- Current value of Progress Bar
intPercent= 0.01 -- Percentage of progress
intStart = os.time() -- Start time of progress
intDelta = 0 -- Delta time of progress
intScale = math.ceil( intMax / 1000 ) -- Scale of percentage per second of progress (initial guess is corrected in Step function)
strClock = "00 : 00 : 00" -- Clock delta time display
isBarStop = false -- Stop button pressed signal
doUpdate()
doFocus()
end -- local function doReset
function fh.Start(strTitle,intMaximum) -- Create & start Progress Bar window
if not dlgGauge then
strTitle = strTitle or "" -- Dialogue and button title
intMax = intMaximum or 100 -- Maximun range of Progress Bar, default is 100
local strSize = tostring( math.max( 100, string.len(" Stop "..strTitle) * 8 ) ).."x30" -- Adjust Stop button size to Title
lblText = iup.label { Title=" "; Expand="YES"; Alignment="ACENTER"; Tip="Progress Message"; }
barGauge = iup.progressbar { RasterSize="400x30"; Value=0; Max=intMax; Tip="Progress Bar"; }
lblDelta = iup.label { Title=" "; Expand="YES"; Alignment="ACENTER"; Tip="Percentage and Elapsed Time"; }
btnStop = iup.button { Title=" Stop "..strTitle; RasterSize=strSize; FgColor=strStop; Tip="Stop Progress Button"; action=function() isBarStop = true end; } -- Signal Stop button pressed return iup.CLOSE -- Often caused main GUI to close !!!
dlgGauge = iup.dialog { Title=strTitle.." Progress "; Font=strFont; FgColor=strBody; Background=strBack; DialogFrame="YES"; -- Remove Windows minimize/maximize menu
iup.vbox{ Alignment="ACENTER"; Gap="10"; Margin="10x10";
lblText;
barGauge;
lblDelta;
btnStop;
};
move_cb = function(self,x,y) tblBars.X = x tblBars.Y = y end;
close_cb = btnStop.action; -- Windows Close button = Stop button
}
if type(tblBars.GUI) == "table"
and type(tblBars.GUI.ShowDialogue) == "function" then
dlgGauge.move_cb = nil -- Use GUI library to show & move window
tblBars.GUI.ShowDialogue("Bars",dlgGauge,btnStop,"showxy")
else
dlgGauge:showxy(intPosX,intPosY) -- Show the Progress Bar window
end
doReset() -- Reset the Progress Bar display
end
end -- function Start
function fh.Message(strText) -- Show the Progress Bar message
if dlgGauge then lblText.Title = strText end
end -- function Message
function fh.Step(intStep) -- Step the Progress Bar forward
if dlgGauge then
intVal = intVal + ( intStep or 1 ) -- Default step is 1
local intNew = math.ceil( intVal / intMax * 100 * intScale ) / intScale
if intPercent ~= intNew then -- Update progress once per percent or per second, whichever is smaller
intPercent = math.max( 0.1, intNew ) -- Ensure percentage is greater than zero
if intVal > intMax then intVal = intMax intPercent = 100 end -- Ensure values do not exceed maximum
intNew = os.difftime(os.time(),intStart)
if intDelta < intNew then -- Update clock of elapsed time
intDelta = intNew
intScale = math.ceil( intDelta / intPercent ) -- Scale of seconds per percentage step
local intHour = math.floor( intDelta / 3600 )
local intMins = math.floor( intDelta / 60 - intHour * 60 )
local intSecs = intDelta - intMins * 60 - intHour * 3600
strClock = string.format("%02d : %02d : %02d",intHour,intMins,intSecs)
end
doUpdate() -- Update the Progress Bar display
end
iup.LoopStep()
end
end -- function Step
function fh.Focus() -- Bring the Progress Bar window to front
if dlgGauge then doFocus() end
end -- function Focus
function fh.Reset() -- Reset the Progress Bar display
if dlgGauge then doReset() end
end -- function Reset
function fh.Stop() -- Check if Stop button pressed
iup.LoopStep()
return isBarStop
end -- function Stop
function fh.Close() -- Close the Progress Bar window
isBarStop = false
if dlgGauge then dlgGauge:destroy() dlgGauge = nil end
end -- function Close
function fh.Setup(tblSetup) -- Setup optional table of external attributes
if tblSetup then
tblBars = tblSetup
strBack = tblBars.Back or strBack -- Background colour
strBody = tblBars.Body or strBody -- Body text colour
strFont = tblBars.Font or strFont -- Font dialogue
strStop = tblBars.Stop or strStop -- Stop button colour
intPosX = tblBars.X or intPosX -- Window position
intPosY = tblBars.Y or intPosY
end
end -- function Setup
return fh
end -- local function progbar_v3
local progbar = progbar_v3() -- To access FH progress bars module
require "lfs" -- To access LUA filing system
function FlgFileExists(strFileName) -- Check if file exists
return lfs.attributes(strFileName,"mode") == "file"
end -- function FlgFileExists
function CopyFile(strSource,strTarget) -- Copy media file and modification date-time
local fileSource = assert(io.open(strSource,"rb"))
local fileTarget = assert(io.open(strTarget,"wb"))
local anyContent = ""
repeat -- Copy entire file contents
fileTarget:write(anyContent)
anyContent = fileSource:read(10000)
until anyContent == nil
assert(fileSource:close())
assert(fileTarget:close())
lfs.touch(strTarget,os.time(),lfs.attributes(strSource,"modification"))
end -- function CopyFile
function NoteChildBranch(ptrSource,strTarget) -- Note child branch from source
return NoteChildren(ptrSource,strTarget..fhGetDisplayText(ptrSource))
end -- function NoteChildBranch
function NoteChildren(ptrSource,strTarget) -- Note child fields from source
local ptrFrom = ptrSource:Clone()
ptrFrom:MoveToFirstChildItem(ptrFrom)
while ptrFrom:IsNotNull() do
strTarget = NoteChildBranch(ptrFrom,strTarget)
ptrFrom:MoveNext()
end
return strTarget
end -- function NoteChildren
local ptrFile = fhNewItemPtr() -- V1.1
function CopyChildBranch(ptrSource,ptrTarget,arrTags) -- Copy child branch from source to target
local strTag = fhGetTag(ptrSource)
strTag = arrTags[strTag] or strTag -- Cater for FILE and NOTE tag
if strTag == "TITL" and fhGetAppVersion() > 6 then
ptrTarget = ptrFile:Clone() -- GEDCOM 5.5.1 TITL is per FILE in OBJE -- V1.1
end
local ptrNew = fhCreateItem(strTag,ptrTarget,true)
if ptrNew:IsNotNull() then
fhSetValue_Copy(ptrNew,ptrSource)
if strTag == "FILE" then ptrFile = ptrNew:Clone() end -- GEDCOM 5.5.1 TITL is per FILE in OBJE -- V1.1
else
local ptrNew = fhCreateItem("REFN",ptrTarget,true) -- Cater for UDF and multiple NOTE tags, etc
fhSetValueAsText(ptrNew,fhGetDisplayText(ptrSource))
end
CopyChildren(ptrSource,ptrNew)
end -- function CopyChildBranch
function CopyChildren(ptrSource,ptrTarget,arrTags) -- Copy child fields from source to target
local ptrFrom = ptrSource:Clone()
ptrFrom:MoveToFirstChildItem(ptrFrom)
while ptrFrom:IsNotNull() do
CopyChildBranch(ptrFrom,ptrTarget,arrTags or {})
ptrFrom:MoveNext()
end
end -- function CopyChildren
function IsSortDate(ptrItem) -- Does LMO conform to Sort Date format
return fhGetItemText(ptrItem,"~.TITL") == "Sort Date" and fhGetItemText(ptrItem,"~.FILE") == ""
end -- function IsSortDate
local dicTags = {} -- LMO to Record tag mapping for FH V7
local strFILE = "~.FILE"
if fhGetAppVersion() < 7 then
dicTags = { FILE="_FILE"; NOTE2="_NOTE"; } -- LMO to Record tag mapping for FH V6
strFILE = "~._FILE"
end
local arrRecs = { -- Record type to Name and Keywords mapping
{ Type = "INDI" ; Name = "Individual" ; Keys = "Picture" ; };
{ Type = "FAM" ; Name = "Family" ; Keys = "Picture" ; };
{ Type = "SOUR" ; Name = "Source" ; Keys = "Document" ; };
{ Type = "SUBM" ; Name = "Submitter" ; Keys = "Picture" ; };
}
local strData = fhGetContextInfo("CI_PROJECT_DATA_FOLDER").."\\" -- Project Data folder
function MoveLMO() -- Move each LMO to a Media Record
local dicObje = { }
local arrItem = { }
local intRecs = 0
local ptrItem = fhNewItemPtr()
for _, dicRec in ipairs (arrRecs) do -- Count all records that may contain LMO
ptrItem:MoveToFirstRecord(dicRec.Type)
while ptrItem:IsNotNull() do
intRecs = intRecs + 1
ptrItem:MoveNext()
end
end
progbar.Setup() -- Progress Bar prototype
if intRecs > 100 then
progbar.Start("Moving Local Media to Records",intRecs) -- Start the Progress Bar with number of required Records
end
local intFile = 0
ptrItem:MoveToFirstRecord("OBJE")
while ptrItem:IsNotNull() and math.abs(intFile) < 1000 do -- Search Media records to see if Media were imported or not
local strFile = fhGetItemText(ptrItem,strFILE) -- V1.1
if strFile:lower():match("^media\\") then
if FlgFileExists(strData..strFile) then
intFile = intFile + 1 -- Increment count for relative imported Media links
end
else
if FlgFileExists(strFile) then
intFile = intFile - 1 -- Decrement count for absolute external Media links
end
end
ptrItem:MoveNext()
end
for _, dicRec in ipairs (arrRecs) do -- Search all records that may contain LMO
ptrItem:MoveToFirstRecord(dicRec.Type)
while ptrItem:IsNotNull() do
local strTag = fhGetTag(ptrItem)
if ( strTag == "OBJE2" and not IsSortDate(ptrItem) ) -- Found LMO that is not a Sort Date
or ( strTag == "OBJE" and fhIsUDF(ptrItem) ) then -- Found LMO that is a UDF from Legacy
local strObje = NoteChildren(ptrItem,"") --? ,dicTags)
if #strObje > 9 then
local ptrObje = dicObje[strObje] -- Check if LMO is a duplicate
if not ptrObje then
ptrObje = fhCreateItem("OBJE") -- If not, then create new Media record
if ptrObje:IsNotNull() then
dicObje[strObje] = ptrObje:Clone()
CopyChildren(ptrItem,ptrObje,dicTags) -- Copy child fields and add Keyword
fhSetValueAsText(fhCreateItem("_KEYS",ptrObje),dicRec.Keys)
if intFile > 1 then -- Relative internal links prevail so copy Media file into Media folder
local ptr_File = fhGetItemPtr(ptrObje,strFILE) -- V1.1
local strSource = fhGetValueAsText(ptr_File)
local strTarget = strSource:gsub(".-([^\\]+)$","Media\\%1")
fhSetValueAsText(ptr_File,strTarget) -- Set relative file link
strTarget = strData..strTarget
if FlgFileExists(strSource) and not FlgFileExists(strTarget) then -- V1.1
CopyFile(strSource,strTarget)
end
end
end
end
if ptrObje:IsNotNull() then -- Save pointers to LMO and Record
table.insert(arrItem,{ Loc=ptrItem:Clone(); Rec=ptrObje:Clone(); } )
end
end
end
local intRid = fhGetRecordId(ptrItem) -- Obtain the Record Id
if intRid > 0 then
progbar.Message(dicRec.Name.." Record Id "..intRid) -- Update the Progress Bar per Record
progbar.Step(1)
isStop = progbar.Stop()
if isStop then break end -- Break out of inner loop
end
ptrItem:MoveNextSpecial()
end
if isStop then break end -- Break out of outer loop
end
for _, dicItem in ipairs ( arrItem ) do
local ptrItem = dicItem.Loc -- Delete this Local Media Object item
local ptrObje = dicItem.Rec -- Link this Media Record to parent of LMO
local ptrRoot = fhNewItemPtr()
ptrRoot:MoveToParentItem(ptrItem)
local ptrLink = fhCreateItem("OBJE",ptrRoot)
if fhIsUDF(ptrLink) or ptrLink:IsNull() then -- Cater for LMO UDF from Legacy
ptrRoot:MoveToParentItem(ptrRoot)
ptrLink = fhCreateItem("OBJE",ptrRoot)
end
ptrRoot:MoveToParentItem(ptrItem)
local isOK = fhSetValueAsLink(ptrLink,ptrObje)
local isOK = fhDeleteItem(ptrItem) -- Only children of UDF get deleted (bug)
ptrItem = fhGetItemPtr(ptrRoot,"~.OBJE")
if fhIsUDF(ptrItem) then
isOK = fhDeleteItem(ptrItem) -- Delete the UDF itself
end
progbar.Step(1)
end
progbar.Close() -- Close the Progress Bar
fhMessageBox( "\n " .. #arrItem .. " LMO converted into Media records. \n\n Use 'Edit > Undo Plugin Updates' to undo conversions. \n" )
end -- function MoveLMO
local strMsg = [[
This converts each Local Media Object to a Media Record
( except those that conform to the "Sort Date" format. )
Any UDF or other exceptions are saved in Custom Id fields.
Continue?
]]
if fhMessageBox(strMsg,"MB_OKCANCEL","MB_ICONQUESTION") == "OK" then
MoveLMO()
end
Source:Move-Local-Media-to-Media-Records-1.fh_lua