Módulo:Controle de autoridade

require('strict')

local conf = require( 'Module:Controle de autoridade/conf' ) -- configuration module

local function isNilOrEmpty( thing )
    if thing == nil or thing == '' then
        return true
    end
    return nil
end

local function splitLccn( id )
    if id:match( '^%l%l?%l?%d%d%d%d%d%d%d%d%d?%d?$' ) then
        id = id:gsub( '^(%l+)(%d+)(%d%d%d%d%d%d)$', '%1/%2/%3' )
    end
    if id:match( '^%l%l?%l?/%d%d%d?%d?/%d+$' ) then
         return mw.text.split( id, '/' )
    end
    return false
end

local function getIdFromWikidata( item, property )
    local id = nil
    if item.claims[property] == nil then
        return id
    end
    for _, statement in pairs( item.claims[property] ) do
        if statement.mainsnak.datavalue then
            id = statement.mainsnak.datavalue.value
            break
        end
    end
    return id
end

local function getLink( property, val, mask )
    local link, returnVal = '', {}
  
    returnVal.isError = false
  
    if mw.ustring.find( val, '//' ) then
        link = val
    else
        if type(property) == 'number' and property > 0 then
            local entityObject = mw.wikibase.getEntity('P'..property)
            local dataType
          
            if entityObject then dataType = entityObject.datatype
            else returnVal.isError = true end
          
            if dataType == 'external-id' then
                local formatterURL = nil
                if property == 3746 then --Wildflowers of Israel ID's 2nd formatterURL is in English
                    formatterURL = entityObject:getBestStatements('P1630')[2]
                end
                if formatterURL == nil then formatterURL = entityObject:getBestStatements('P1630')[1] end
                if formatterURL then
                    if formatterURL.mainsnak.datavalue and formatterURL.mainsnak.datavalue.value then --nil check for ABA
                        link = formatterURL.mainsnak.datavalue.value
                    end
                end
            elseif dataType == 'url' then
                local subjectItem = entityObject:getBestStatements('P1629')[1]
                if subjectItem then
                    local officialWebsite = mw.wikibase.getEntity(subjectItem.mainsnak.datavalue.value.id):getBestStatements('P856')[1]
                    if officialWebsite then    link = officialWebsite.mainsnak.datavalue.value end
                end
            elseif dataType == 'string' then
                local formatterURL = entityObject:getBestStatements('P1630')[1]
                if formatterURL then
                    link = formatterURL.mainsnak.datavalue.value
                else
                    local subjectItem = entityObject:getBestStatements('P1629')[1]
                    if subjectItem then
                        local officialWebsite = mw.wikibase.getEntity(subjectItem.mainsnak.datavalue.value.id):getBestStatements('P856')[1]
                        if officialWebsite then    link = officialWebsite.mainsnak.datavalue.value end
                    end
                end
            else
                returnVal.isError = true
            end
        elseif type(property) == 'string' then
            link = property
        end
      
        local valurl = val
        valurl = mw.ustring.gsub(valurl,'%%','%%%%')
        link = mw.ustring.gsub(link, '$1', valurl)
    end
  
    link = mw.ustring.gsub(link, '^[Hh][Tt][Tt][Pp]([Ss]?)://', 'http%1://') -- fix wikidata URL
    link = mw.ustring.gsub(link, ' ' , '')
    val = mw.ustring.match(val, '([^=/]*)/?$') -- get display name from end of URL
    if mw.ustring.find( link, '//' ) then
        if mask == 'y' then
            returnVal.text = '<span class="plainlinks">['..link..' ID]</span>'
        else
            returnVal.text = '<span class="plainlinks">['..link..' '..mw.text.encode(mw.uri.decode(val, 'PATH'),'%[%]')..']</span>'
        end
    elseif link == '' then
        returnVal.text = val
    else
        returnVal.text = '[['..link..'|'..val..']]'
    end
    return returnVal
end

local function createRow( id, label, rawValue, link, withUid )
    if link then
        local outStr = '*<span style="white-space:nowrap;">' .. label .. ' <span'
        if withUid then outStr = outStr..' class="uid"' end
        return outStr..'>' .. link .. '</span></span>\n'
    else
        return '* ' .. mw.text.tag('span', {class='error'}, 'O identificador ' .. id .. ' ' .. rawValue .. ' é inválido.') .. '\n'
    end
end

local function copyTable(inTable)
    if type(inTable) ~= 'table' then return inTable end
    local outTable = setmetatable({}, getmetatable(inTable))
    for key, value in pairs (inTable) do outTable[copyTable(key)] = copyTable(value) end
    return outTable
end

local p = {}

function p.authorityControl( frame )
    local resolve = require( 'Module:ResolveEntityId' )
    local parentArgs = copyTable(frame:getParent().args)
    local currentTitle = mw.title.getCurrentTitle()
    local currentEntityId = mw.wikibase.getEntityIdForCurrentPage()
  
    local stringArgs = false
    local fromTitleCount, firstRow, rowCount = 1, 0, 0
    local outString, errors = '', ''
    local tFroms = {} --non-sequential table of unique froms
    local iFroms = 0 --integer size of tFroms, b/c Lua
  
  
    --Cleanup args
    for k, v in pairs( frame:getParent().args ) do
        if type(k) == 'string' then
            --make args case insensitive
            local lowerk = mw.ustring.lower(k)
            if isNilOrEmpty( parentArgs[lowerk] ) then
                parentArgs[k] = nil
                parentArgs[lowerk] = v
            end
            --remap abc to abc1
            if mw.ustring.find(lowerk,'%d$') == nil then --if no number at end of param
                if isNilOrEmpty( parentArgs[lowerk..'1'] ) then
                    parentArgs[lowerk] = nil
                    lowerk = lowerk..'1'
                    parentArgs[lowerk] = v
                end
            end
            if v and v ~= '' then
                --remap 'for' to 'title'
                if mw.ustring.sub(lowerk,1,3) == 'for' then
                    local forTitle = mw.ustring.gsub(lowerk,'^for','title',1)
                    if isNilOrEmpty( parentArgs[forTitle] ) then
                        parentArgs[lowerk] = nil
                        lowerk = forTitle
                        parentArgs[lowerk] = v
                    end
                end
                --find highest from or title param
                if mw.ustring.sub(lowerk,1,4) == 'from' then
                    local fromNumber = tonumber(mw.ustring.sub(lowerk,5,-1))
                    if fromNumber and fromNumber >= fromTitleCount then fromTitleCount = fromNumber end
                    --look for duplicate froms while we're here
                    if mw.ustring.find(v, '^Q%d') then
                        if tFroms[v] then

                            tFroms[v] = tFroms[v] + 1
                        else
                            tFroms[v] = 1
                            iFroms = iFroms + 1
                        end
                        if iFroms == 2 then

                        end
                    end
                elseif mw.ustring.sub(lowerk,1,5) == 'title' then
                    local titleNumber = tonumber(mw.ustring.sub(lowerk,4,-1))
                    if titleNumber and titleNumber >= fromTitleCount then fromTitleCount = titleNumber end
                elseif mw.ustring.lower(v) ~= 'no' then
                    stringArgs = true

                end
            end
        end
    end
  
    --Check for unknown parameters
    --create knowns list
    local acceptableArgs = { from = true, } --master list of l/c acceptable args
    for _, d in pairs( conf.databases ) do
        if d[1] ~= 'Wikidata' then --made obsolete by from
            acceptableArgs[mw.ustring.lower(d[1])] = true
        end
    end
    for _, a in pairs( conf.aliases ) do
        acceptableArgs[mw.ustring.lower(a[1])] = true
    end
    --create trimmed parents list
    local baseParentArgs = {} --condensed list of l/c parent args w/o trailing #s
    for k, v in pairs( parentArgs ) do
        if type(k) == 'string' then --ignore unnamed params, which have keys of type 'number'
            local lowerk = mw.ustring.lower(k)
            local base = mw.ustring.gsub(lowerk, '[%d]*$', '')
            baseParentArgs[base] = true
        end
    end
    --compare lists and spit out unknowns
    local unknownParams = {}
    for k, v in pairs( baseParentArgs ) do
        if acceptableArgs[k] == nil then

            unknownParams[#unknownParams + 1] = k
        end
    end
    --warn if unknown(s) present
    if #unknownParams > 0 then
        if frame:preprocess( '{{REVISIONID}}' ) == '' then
            errors = errors..'<div class="hatnote" style="color:red">'..
                     '<strong>Atenção:</strong> parâmetros desconhecidos <strong>'..table.concat(unknownParams, ', ')..'</strong>.<br />'..
                     'Corrige-os ou considere liga-los à Wikidata.<br />'..
                     'Esta mensagem só se mostra na previsão.</div>'
        end
    end
  
  
    --Setup navbox
local navboxParams = {
        name  = 'Controle de autoridade',
        bodyclass = 'hlist',
        listclass = 'plainlinks',
        groupstyle = 'width: 12%; text-align:center;',
    }
  
    for f = 1, fromTitleCount, 1
    do
        local elements, title = {}, nil
        --cleanup parameters
        if parentArgs['from'..f] == '' then parentArgs['from'..f] = nil end
        if parentArgs['title'..f] == '' then parentArgs['title'..f] = nil end
        --remap aliases
        for _, a in pairs( conf.aliases ) do
            local alias, name = mw.ustring.lower(a[1]), mw.ustring.lower(a[2])
            if parentArgs[alias..f] and parentArgs[name..f] == nil then
                parentArgs[name..f] = parentArgs[alias..f]
                parentArgs[alias..f] = nil
            end
        end
        --Fetch Wikidata item
        local from = resolve._entityid(frame, parentArgs['from'..f])
        local item = mw.wikibase.getEntity(from)
        local label = nil
        if type(item) == 'table' then
            local statements = item:getBestStatements('P225')[1] --taxon name
            if statements then
                local datavalue = statements.mainsnak.datavalue
                if datavalue then
                    label = datavalue.value
                end
            end
            label = label or item:getLabel()
        else
            if parentArgs['from'..f] then

                errors = errors .. mw.text.tag('strong', {class='error'}, 'Erro: "' ..
                         parentArgs['from'..f] .. '" não é um ID de entidade Wikidata válido.<br />')              
            end
        end
        if label and label ~= '' then
            title = mw.title.new(label)
        end
        if title == nil and parentArgs['title'..f] then
            title = mw.title.new(parentArgs['title'..f])
        end
        if title == nil and f == 1 then
            title = currentTitle
        end
      
        if title then
            if isNilOrEmpty( parentArgs['wikidata'..f] ) and
               (title.namespace == 0) then
                if parentArgs['from'..f] then
                    parentArgs['wikidata'..f] = parentArgs['from'..f]
                elseif item then
                    parentArgs['wikidata'..f] = item.id
                end
            end
            if title.namespace == 0 or stringArgs then --only in mainspace or if manual overrides exist
                local sourceCount = 0
                for _, params in pairs( conf.databases ) do
                    params[1] = mw.ustring.lower(params[1])
                    local propId = params[3]
                    --Wikidata fallback if requested
                    if (item and item.claims) and
                       (type(propId) == 'string' or (type(propId) == 'number' and propId > 0)) then
                        local wikidataId = getIdFromWikidata( item, 'P' .. propId )
                        local v = parentArgs[params[1]..f]
                        if wikidataId and isNilOrEmpty(v) then
                                parentArgs[params[1]..f] = wikidataId
                        end
                    end
                    local val = parentArgs[params[1]..f]
                    if val and type(val) ~= 'string' then
                    	errors = errors .. mw.text.tag('strong', {class='error'}, 'Erro: "' ..
                    	    params[1] .. '"' .. (propId and ' (wikidata:P' .. propId .. ')' or '') ..
                    	    ' não é um identificar válido<br/>')
                    elseif val and val ~= '' and mw.ustring.lower(val) ~= 'no' then
                      
                        if params[1] == 'issn' and parentArgs['worldcatid'..f] and parentArgs['worldcatid'..f] ~= '' then -- 'issn' is the first element following the 'wikidata' item
                            table.insert( elements, createRow( params[1], '', parentArgs['worldcatid'..f], '[//www.worldcat.org/identities/' .. parentArgs['worldcatid'..f] .. ' WorldCat]', false ) ) --Validation?
                        elseif params[1] == 'viaf' and parentArgs[params[1]..f] and string.match( parentArgs[params[1]..f], '^%d+$' ) and not parentArgs['worldcatid'..f] then -- Hackishly copy the validation code; this should go away when we move to using P1793 and P1630
                            table.insert( elements, createRow( params[1], '', parentArgs[params[1]], '[//www.worldcat.org/identities/containsVIAFID/' .. parentArgs[params[1]..f] .. ' WorldCat]', false ) )
                        elseif params[1] == 'lccn' and parentArgs[params[1]..f] and parentArgs[params[1]..f] ~= '' and not parentArgs['viaf'..f] and not parentArgs['worldcatid'..f] then
                            local lccnParts = splitLccn( parentArgs[params[1]..f] )
                                if lccnParts and lccnParts[1] ~= 'sh' then
                                table.insert( elements, createRow( params[1], '', parentArgs[params[1]..f], '[//www.worldcat.org/identities/lccn-' .. lccnParts[1] .. lccnParts[2] .. '-' .. lccnParts[3] .. ' WorldCat]', false ) )
                                end
                        end
                      
                        if type(propId) == 'number' then
                            if propId < 0 then propId = -propId end --allow link
                            if propId > 0 then --link
                                table.insert( elements, createRow( params[1], params[2]..':', val, getLink( propId, val, params[4] ).text, true ) )
                            else --propId == 0; no link
                                table.insert( elements, createRow( params[1], params[2]..':', val, val, true ) )
                            end
                        else
                            table.insert( elements, createRow( params[1], params[2]..':', val, getLink( propId, val, params[4] ).text, true ) )
                        end
                        if params[1] ~= 'wikidata' and params[1] ~= 'wikispecies' then
                            sourceCount = sourceCount + 1
                        end
                    end
                end
              

              
                --Generate navbox title
                if sourceCount > 0 then --TODO: don't count Wikispecies towards 'taxon ID count'
                    rowCount = rowCount + 1
                    if firstRow == 0 then firstRow = f end
                    --set title from wikidata if it doesn't exist
                    if isNilOrEmpty( parentArgs['title'..f] ) then
                        parentArgs['noTitle'..f] = true
                        parentArgs['title'..f] = title.text
                    end
                    --if it exists now, set row heading to title
                    if not isNilOrEmpty( parentArgs['title'..f] ) then

                    else
                        navboxParams['group'..f] = ''
                    end
                    navboxParams['list'..f] = table.concat( elements )
                elseif currentEntityId then

                end
              
                --Categorize
              
            end
        end
    end --for f = 1, fromTitleCount, 1
  
    if rowCount > 0 then
        local Navbox = require('Module:Navbox')
        if rowCount > 1 then
            --remove duplicates and move page title to top
            local rowIDs = {}
            for f = 1,fromTitleCount,1
            do
                if not isNilOrEmpty( parentArgs['title'..f] ) then
                    if rowIDs[parentArgs['wikidata'..f]] then --remove duplicate
                        navboxParams['group'..f] = nil
                        navboxParams['list'..f] = nil
                    else
                        rowIDs[parentArgs['wikidata'..f]] = true
                        if f > firstRow and (parentArgs['title'..f] == currentTitle.text or
                           parentArgs['wikidata'..f] == currentEntityId) then --move item linked to page to top
                            if navboxParams['group'..f] and
                               navboxParams['group'..f] ~= '' and
                               navboxParams['list'..f] and
                               navboxParams['list'..f] ~= '' then
                                local tempGroup, tempList = navboxParams['group'..f], navboxParams['list'..f]
                                navboxParams['group'..f], navboxParams['list'..f] = navboxParams['group'..firstRow], navboxParams['list'..firstRow]
                                navboxParams['group'..firstRow], navboxParams['list'..firstRow] = tempGroup, tempList
                            end
                        end
                    end
                end
            end
            --adjust navbox for number of rows
            navboxParams['title'] = '[[Ajuda:Controle de autoridade|Controle de autoridade]]'
            if rowCount > 2 then
                navboxParams['navbar'] = 'plain'
            else
                navboxParams['state'] = 'off'
                navboxParams['navbar'] = 'off'
            end
        elseif parentArgs['noTitle'..firstRow] then
            navboxParams['group'..firstRow] = '[[Ajuda:Controle de autoridade|Controle de autoridade]]'
        else
            navboxParams['group'..firstRow] = '[[Ajuda:Controle de autoridade|Controle de autoridade]]<br />' .. navboxParams['group'..firstRow]
        end
      
        --return navbox
        outString = Navbox._navbox(navboxParams)
    end --if rowCount > 0
  
    --Add categories
    if string.sub(currentTitle.subpageText,1,9) == 'testcases' then parentArgs['demo'] = true end

    return outString .. errors
end

return p