Module:CRTModel: Difference between revisions

Fix: sort inputs/outputs by label in Lua instead of SMW query (SMW sort drops subobjects missing the sort property, e.g. unlabeled RF inputs) (via update-page on MediaWiki MCP Server)
Tag: Reverted
Change "Add accessory" button to link to Create_an_accessory with crt_models parameter (via update-page on MediaWiki MCP Server)
 
(17 intermediate revisions by the same user not shown)
Line 60: Line 60:
-- Helper function to extract model code from "Brand Model" format
-- Helper function to extract model code from "Brand Model" format
-- If the accessory starts with the same brand as the CRT, strip it for cleaner display
-- If the accessory starts with the same brand as the CRT, strip it for cleaner display
local function getAccessoryDisplayText(accessoryName, crtBrand)
local function getDisplayTextWithoutBrand(itemName, deviceBrand)
if not accessoryName then return nil end
if not itemName then return nil end
if not crtBrand then return accessoryName end
if not deviceBrand then return itemName end
-- Check if accessory name starts with the CRT brand followed by a space
-- Check if item name starts with the device brand followed by a space
local brandPrefix = crtBrand .. ' '
local brandPrefix = deviceBrand .. ' '
if accessoryName:sub(1, #brandPrefix) == brandPrefix then
if itemName:sub(1, #brandPrefix) == brandPrefix then
-- Return just the model code portion
-- Return just the model code portion
return accessoryName:sub(#brandPrefix + 1)
return itemName:sub(#brandPrefix + 1)
end
end
-- Otherwise return the full name
-- Otherwise return the full name
return accessoryName
return itemName
end
end


-- Helper to query inputs/outputs/control ports for a specific location
-- Helper to query inputs/outputs/control ports for a specific location
-- Results are sorted by label in Lua (SMW sort= can drop subobjects missing the sort property)
local function queryIOByLocation(frame, fullPageName, ioType, location)
local function queryIOByLocation(frame, fullPageName, ioType, location)
local locationProp
local locationProp
Line 94: Line 93:
local signalProp, countProp, connectorProp, notesProp, labelProp, templateName
local signalProp, countProp, connectorProp, notesProp, labelProp, templateName
local extraProps = ''
local extraProps = ''
local extraAliases = {}


if ioType == 'input' then
if ioType == 'input' then
Line 106: Line 104:
' |?Has audio connector=audio_connector\n' ..
' |?Has audio connector=audio_connector\n' ..
' |?Has audio shared with=audio_shared\n'
' |?Has audio shared with=audio_shared\n'
extraAliases = {'audio', 'audio_connector', 'audio_shared'}
elseif ioType == 'output' then
elseif ioType == 'output' then
signalProp = 'Has output signal format'
signalProp = 'Has output signal format'
Line 122: Line 119:
templateName = 'CRT control port row'
templateName = 'CRT control port row'
extraProps = ' |?Has control port direction=direction\n'
extraProps = ' |?Has control port direction=direction\n'
extraAliases = {'direction'}
end
end


-- Query as broadtable so we can sort in Lua
-- (SMW |sort= can silently drop results that lack the sort property)
local queryWikitext =
local queryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 138: Line 132:
' |?' .. notesProp .. '=notes\n' ..
' |?' .. notesProp .. '=notes\n' ..
extraProps ..
extraProps ..
' |format=broadtable\n' ..
' |format=template\n' ..
' |headers=hide\n' ..
' |template=' .. templateName .. '\n' ..
' |named args=yes\n' ..
' |link=none\n' ..
' |link=none\n' ..
'}}'
'}}'


local result = frame:preprocess(queryWikitext)
local result = frame:preprocess(queryWikitext)
if not result or result == '' then return nil end
return trim(result)
 
-- Parse broadtable rows into structured data
local rows = {}
for rowContent in result:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
 
-- Columns: [1]=subobject, [2]=count, [3]=signal, [4]=connector, [5]=label, [6]=notes, [7+]=extras
if #cells >= 3 then
local rowData = {
count = cells[2],
signal = cells[3],
connector = cells[4],
label = cells[5],
notes = cells[6],
}
for i, alias in ipairs(extraAliases) do
rowData[alias] = cells[6 + i]
end
table.insert(rows, rowData)
end
end
 
-- Sort by label: unlabeled entries first, then alphabetical by label
table.sort(rows, function(a, b)
local la = a.label or ''
local lb = b.label or ''
return la < lb
end)
 
-- Render each row through the display template
local parts = {}
for _, row in ipairs(rows) do
local templateArgs = {}
for k, v in pairs(row) do
if v and v ~= '' then
templateArgs[k] = v
end
end
local rendered = frame:expandTemplate{title = templateName, args = templateArgs}
if rendered and trim(rendered) then
table.insert(parts, rendered)
end
end
 
if #parts == 0 then return nil end
return trim(table.concat(parts))
end
end


Line 283: Line 228:
local model_code = trim(args.model_code)
local model_code = trim(args.model_code)
local variants = trim(args.variants)
local variants = trim(args.variants)
local market = trim(args.market)
local series = trim(args.series)
local series = trim(args.series)
local image_main = trim(args.image_main)
local image_main = trim(args.image_main)
Line 351: Line 297:
'infobox-title'
'infobox-title'
)
)
-- Type badge (breadcrumb) — plainlinks hides external icon, $wgNoFollowDomainExceptions handles nofollow
local badgeWikitext = '[[CRT Search|CRT]]'
if crt_type then
local typeUrl = mw.title.new('CRT Search'):fullUrl({crt_type = crt_type})
badgeWikitext = badgeWikitext .. ' › <span class="plainlinks">[' .. typeUrl .. ' ' .. crt_type .. ']</span>'
end
addFullRow(container, badgeWikitext, '', false, 'infobox-type-badge')


if image_main then
if image_main then
Line 396: Line 350:
if variants then
if variants then
addRow(container, 'Variants', variants, false)
addRow(container, 'Variants', variants, false)
end
-- Market row
if market then
addRow(container, 'Market', market, false)
end
end


Line 475: Line 434:
-- Remote control row: use Accessory form, strip brand prefix if it matches CRT brand
-- Remote control row: use Accessory form, strip brand prefix if it matches CRT brand
if remote_control then
if remote_control then
local displayText = getAccessoryDisplayText(remote_control, brand)
local displayText = getDisplayTextWithoutBrand(remote_control, brand)
local remoteWikitext = string.format(
local remoteWikitext = string.format(
'{{#formredlink: target=%s |form=Accessory |existing page link text=%s }}',
'{{#formredlink: target=%s |form=Accessory |existing page link text=%s }}',
Line 614: Line 573:
if range.min and range.max then
if range.min and range.max then
if range.min == range.max then
if range.min == range.max then
-- Single voltage (e.g., "120V")
rangeStr = range.min .. 'V'
rangeStr = range.min .. 'V'
else
else
-- Range (e.g., "100–120V")
rangeStr = range.min .. '–' .. range.max .. 'V'
rangeStr = range.min .. '–' .. range.max .. 'V'
end
end
Line 631: Line 588:
if #rangeStrings > 0 then
if #rangeStrings > 0 then
-- Join multiple ranges with " / " (e.g., "100–120V / 220–240V")
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false)
addRow(container, 'Voltage', table.concat(rangeStrings, ' / '), false)
end
end
Line 659: Line 615:
addSectionRow(container, 'TV Systems')
addSectionRow(container, 'TV Systems')
-- AV system row (baseband video color support)
if av_system then
if av_system then
-- Link each system to its wiki page
local systems = {}
local systems = {}
for system in av_system:gmatch('[^,]+') do
for system in av_system:gmatch('[^,]+') do
Line 674: Line 628:
end
end
-- Tuner system row (RF broadcast standards)
if tuner_system then
if tuner_system then
-- Link each system to its wiki page
local systems = {}
local systems = {}
for system in tuner_system:gmatch('[^,]+') do
for system in tuner_system:gmatch('[^,]+') do
Line 690: Line 642:
end
end


-- Scan Range section: show if we have any scan range specs
-- Scan Range section
if h_scan_min or h_scan_max or v_scan_min or v_scan_max then
if h_scan_min or h_scan_max or v_scan_min or v_scan_max then
addSectionRow(container, 'Scan Range')
addSectionRow(container, 'Scan Range')
-- Horizontal scan row
if h_scan_min or h_scan_max then
if h_scan_min or h_scan_max then
local hDisplay
local hDisplay
if h_scan_min and h_scan_max then
if h_scan_min and h_scan_max then
if h_scan_min == h_scan_max then
if h_scan_min == h_scan_max then
-- Single frequency (e.g., "15.7 kHz")
hDisplay = h_scan_min .. ' kHz'
hDisplay = h_scan_min .. ' kHz'
else
else
-- Range (e.g., "15–100 kHz")
hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz'
hDisplay = h_scan_min .. '–' .. h_scan_max .. ' kHz'
end
end
Line 713: Line 662:
end
end
-- Vertical scan row
if v_scan_min or v_scan_max then
if v_scan_min or v_scan_max then
local vDisplay
local vDisplay
if v_scan_min and v_scan_max then
if v_scan_min and v_scan_max then
if v_scan_min == v_scan_max then
if v_scan_min == v_scan_max then
-- Single frequency (e.g., "60 Hz")
vDisplay = v_scan_min .. ' Hz'
vDisplay = v_scan_min .. ' Hz'
else
else
-- Range (e.g., "50–160 Hz")
vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz'
vDisplay = v_scan_min .. '–' .. v_scan_max .. ' Hz'
end
end
Line 734: Line 680:


-- Inputs, Outputs, and Control Ports sections
-- Inputs, Outputs, and Control Ports sections
-- Dynamically discover locations from semantic data instead of hardcoding
-- Helper closure to render an I/O section with dynamically discovered location-based rows
local function renderIOSection(sectionTitle, ioType)
local function renderIOSection(sectionTitle, ioType)
local locations = discoverLocations(frame, fullPageName, ioType)
local locations = discoverLocations(frame, fullPageName, ioType)
Line 768: Line 711:
renderIOSection('Control Ports', 'control')
renderIOSection('Control Ports', 'control')


-- Accessories section: Query for non-remote accessories linked via Has related CRT model
-- Accessories section
addSectionRow(container, 'Accessories')
 
local addAccessoryUrl = mw.uri.fullUrl(
"Create an accessory",
{
crt_models = pageName
}
)
 
local accessoriesCountWikitext =
local accessoriesCountWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 778: Line 730:
local accessoryCount = frame:preprocess(accessoriesCountWikitext)
local accessoryCount = frame:preprocess(accessoriesCountWikitext)
local hasAccessories = accessoryCount and tonumber(trim(accessoryCount)) and tonumber(trim(accessoryCount)) > 0
local hasAccessories = accessoryCount and tonumber(trim(accessoryCount)) and tonumber(trim(accessoryCount)) > 0
 
local accDiv = container:tag('div')
css(accDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
 
if hasAccessories then
if hasAccessories then
addSectionRow(container, 'Accessories')
-- Query accessories and display grouped by type
local accessoriesQueryWikitext =
local accessoriesQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 790: Line 742:
' |?Has accessory type=type\n' ..
' |?Has accessory type=type\n' ..
' |?Has model code=model\n' ..
' |?Has model code=model\n' ..
' |?Has description=desc\n' ..
' |format=broadtable\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |headers=hide\n' ..
Line 796: Line 747:
'}}'
'}}'
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext)
local accessoriesResult = frame:preprocess(accessoriesQueryWikitext)
 
-- Parse accessories from broadtable result and group by type
local accessoriesByType = {}
local accessoriesByType = {}
local typeOrder = {}
local typeOrder = {}
 
if accessoriesResult then
if accessoriesResult then
for rowContent in accessoriesResult:gmatch('<tr[^>]*>(.-)</tr>') do
for rowContent in accessoriesResult:gmatch('<tr[^>]*>(.-)</tr>') do
Line 808: Line 758:
table.insert(cells, trim(cleanContent))
table.insert(cells, trim(cleanContent))
end
end
 
-- cells[1] = page name, cells[2] = type, cells[3] = model code, cells[4] = description
if #cells >= 2 then
if #cells >= 2 then
local accPageName = cells[1]
local accPageName = cells[1]
local accType = cells[2] or 'Accessory'
local accType = cells[2] or 'Accessory'
local accDesc = cells[4]
 
if accPageName then
if accPageName then
-- Track type order for consistent display
if not accessoriesByType[accType] then
if not accessoriesByType[accType] then
accessoriesByType[accType] = {}
accessoriesByType[accType] = {}
table.insert(typeOrder, accType)
table.insert(typeOrder, accType)
end
end
 
table.insert(accessoriesByType[accType], {
table.insert(accessoriesByType[accType], {
page = accPageName,
page = accPageName
desc = accDesc
})
})
end
end
Line 830: Line 776:
end
end
end
end
 
-- Display as label/value rows grouped by type (like Archival Links)
local accGrid = accDiv:tag('div')
css(accGrid, 'display:grid; grid-template-columns:auto 1fr; gap:3px;')
 
for _, accType in ipairs(typeOrder) do
for _, accType in ipairs(typeOrder) do
local accs = accessoriesByType[accType]
local accs = accessoriesByType[accType]
Line 837: Line 785:
local accLinks = {}
local accLinks = {}
for _, acc in ipairs(accs) do
for _, acc in ipairs(accs) do
local displayText = getAccessoryDisplayText(acc.page, brand)
table.insert(accLinks, '[[' .. acc.page .. ']]')
local linkText = displayText
end
if acc.desc and acc.desc ~= '' then
 
linkText = displayText .. ' ' .. acc.desc
local labelDiv = accGrid:tag('div')
labelDiv:addClass('infobox-label')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(accType)
 
local valueDiv = accGrid:tag('div')
valueDiv:addClass('infobox-value')
css(valueDiv, 'padding:0.2em 0.4em;')
valueDiv:wikitext(table.concat(accLinks, '<br>'))
end
end
 
local addAccDiv = mw.html.create('div')
addAccDiv:css('margin-top', '0.5em'):css('text-align', 'center')
addAccDiv:wikitext('[' .. tostring(addAccessoryUrl) .. ' <span class="infobox-button infobox-button-secondary">Add accessory</span>]')
accDiv:node(addAccDiv)
else
local addAccDiv = mw.html.create('div')
addAccDiv:css('text-align', 'center')
addAccDiv:wikitext('[' .. tostring(addAccessoryUrl) .. ' <span class="infobox-button infobox-button-secondary">Add accessory</span>]')
accDiv:node(addAccDiv)
end
 
-- Parts section
addSectionRow(container, 'Parts')
local addPartUrl = mw.uri.fullUrl(
"Create a part",
{
crt_models = pageName
}
)
local partsCountWikitext =
'{{#ask:\n' ..
' [[Category:Parts]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |format=count\n' ..
'}}'
local partsCount = frame:preprocess(partsCountWikitext)
local hasParts = partsCount and tonumber(trim(partsCount)) and tonumber(trim(partsCount)) > 0
local partsDiv = container:tag('div')
css(partsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
if hasParts then
local partsQueryWikitext =
'{{#ask:\n' ..
' [[Category:Parts]]\n' ..
' [[Has related CRT model::' .. fullPageName .. ']]\n' ..
' |?Has part type=type\n' ..
' |?Has part subtype=subtype\n' ..
' |?Has model code=model\n' ..
' |format=broadtable\n' ..
' |headers=hide\n' ..
' |link=none\n' ..
'}}'
local partsResult = frame:preprocess(partsQueryWikitext)
local partsByType = {}
local partTypeOrder = {}
if partsResult then
for rowContent in partsResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
end
if #cells >= 2 then
local partPageName = cells[1]
local partType = cells[2] or 'Part'
local partSubtype = cells[3]
local groupKey = partType
if partSubtype and partSubtype ~= '' then
groupKey = partSubtype .. ' ' .. partType
end
end
table.insert(accLinks, '[[' .. acc.page .. '|' .. linkText .. ']]')
if partPageName then
if not partsByType[groupKey] then
partsByType[groupKey] = {}
table.insert(partTypeOrder, groupKey)
end
table.insert(partsByType[groupKey], {
page = partPageName
})
end
end
end
end
local partsGrid = partsDiv:tag('div')
css(partsGrid, 'display:grid; grid-template-columns:auto 1fr; gap:3px;')
for _, partType in ipairs(partTypeOrder) do
local parts = partsByType[partType]
if parts and #parts > 0 then
local partLinks = {}
for _, part in ipairs(parts) do
table.insert(partLinks, '[[' .. part.page .. ']]')
end
end
addRow(container, accType, table.concat(accLinks, '<br>'), true)
local labelDiv = partsGrid:tag('div')
labelDiv:addClass('infobox-label')
css(labelDiv, 'padding:0.2em 0.4em; font-weight:bold; text-align:left;')
labelDiv:wikitext(partType)
local valueDiv = partsGrid:tag('div')
valueDiv:addClass('infobox-value')
css(valueDiv, 'padding:0.2em 0.4em;')
valueDiv:wikitext(table.concat(partLinks, '<br>'))
end
end
end
end
local addPartDiv = mw.html.create('div')
addPartDiv:css('margin-top', '0.5em'):css('text-align', 'center')
addPartDiv:wikitext('[' .. tostring(addPartUrl) .. ' <span class="infobox-button infobox-button-secondary">Add part</span>]')
partsDiv:node(addPartDiv)
else
local addPartDiv = mw.html.create('div')
addPartDiv:css('text-align', 'center')
addPartDiv:wikitext('[' .. tostring(addPartUrl) .. ' <span class="infobox-button infobox-button-secondary">Add part</span>]')
partsDiv:node(addPartDiv)
end
end


-- Mods section: Query for mod subobjects and display
-- Mods section
local modsQueryWikitext =
local modsQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 864: Line 931:
local modsResult = frame:preprocess(modsQueryWikitext)
local modsResult = frame:preprocess(modsQueryWikitext)
-- Parse mods from broadtable result
local mods = {}
local mods = {}
Line 905: Line 971:
end
end
-- Display mods section if there are any
if #mods > 0 then
if #mods > 0 then
addSectionRow(container, 'Mods')
addSectionRow(container, 'Mods')
Line 918: Line 983:
local li = ul:tag('li')
local li = ul:tag('li')
-- Determine display text: use description if available, otherwise type
local displayText = mod.description or mod.type
local displayText = mod.description or mod.type
-- Check if mod is not feasible
local isFeasible = mod.feasible ~= 'No'
local isFeasible = mod.feasible ~= 'No'
if isFeasible then
if isFeasible then
-- Normal link for feasible mods using wikitext external link
li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']')
li:wikitext('[' .. mod.url .. ' ' .. displayText .. ']')
else
else
-- Red styling with X for impossible mods
local wrapper = li:tag('span')
local wrapper = li:tag('span')
wrapper:addClass('mod-not-feasible')
wrapper:addClass('mod-not-feasible')
Line 942: Line 1,002:
addSectionRow(container, 'Documents')
addSectionRow(container, 'Documents')
-- Build the upload link with pseudorandom unique identifier
local timestamp = os.time()
local timestamp = os.time()
math.randomseed(timestamp + pageId) -- Seed with timestamp + page ID for better randomness
math.randomseed(timestamp + pageId)
local random = math.random(1000, 9999)
local random = math.random(1000, 9999)
local target = "Document:" .. timestamp .. "-" .. random
local target = "Document:" .. timestamp .. "-" .. random
Line 956: Line 1,015:
)
)
-- Query for document count
local documentsCountWikitext =
local documentsCountWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 966: Line 1,024:
local hasDocuments = documentCount and tonumber(trim(documentCount)) and tonumber(trim(documentCount)) > 0
local hasDocuments = documentCount and tonumber(trim(documentCount)) and tonumber(trim(documentCount)) > 0
-- Create the content div
local docsDiv = container:tag('div')
local docsDiv = container:tag('div')
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
css(docsDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em;')
if hasDocuments then
if hasDocuments then
-- Use #ask to get documents
local docsQueryWikitext =
local docsQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 986: Line 1,042:
local docsResult = frame:preprocess(docsQueryWikitext)
local docsResult = frame:preprocess(docsQueryWikitext)
-- Create UL element
local ul = mw.html.create('ul')
local ul = mw.html.create('ul')
ul:css('margin', '0'):css('padding-left', '1.5em')
ul:css('margin', '0'):css('padding-left', '1.5em')
-- Parse the HTML table - extract data from <td> tags
if docsResult then
if docsResult then
-- Match each table row
for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do
for rowContent in docsResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
local cells = {}
-- Extract content from each <td>
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip any remaining HTML tags and get just the text
local cleanContent = cellContent:gsub('<[^>]+>', '')
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, cleanContent)
table.insert(cells, cleanContent)
end
end
-- cells[1] = page name, cells[2] = type, cells[3] = URL, cells[4] = file, cells[5] = title
if #cells >= 2 then
if #cells >= 2 then
local docType = trim(cells[2])
local docType = trim(cells[2])
Line 1,009: Line 1,059:
local docTitle = trim(cells[5])
local docTitle = trim(cells[5])
-- Determine what to link to (URL or file)
local linkTarget = nil
local linkTarget = nil
if docUrl then
if docUrl then
linkTarget = docUrl
linkTarget = docUrl
elseif docFile then
elseif docFile then
-- Extract filename from "File:..." format if present
local filename = docFile:match('File:(.+)') or docFile
local filename = docFile:match('File:(.+)') or docFile
linkTarget = tostring(mw.uri.fullUrl('File:' .. filename))
linkTarget = tostring(mw.uri.fullUrl('File:' .. filename))
end
end


-- Always link to the wiki page
local docPageName = trim(cells[1])
local docPageName = trim(cells[1])
if docPageName then
if docPageName then
-- Determine display text priority: custom title > type > default
local displayText = 'Document'
local displayText = 'Document'
if docTitle then
if docTitle then
Line 1,031: Line 1,077:
end
end
-- Create list item with link to wiki page
local li = ul:tag('li')
local li = ul:tag('li')
li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]')
li:wikitext('[[' .. docPageName .. '|' .. displayText .. ']]')
Line 1,041: Line 1,086:
docsDiv:node(ul)
docsDiv:node(ul)
-- Add upload link as a button
local uploadDiv = mw.html.create('div')
local uploadDiv = mw.html.create('div')
uploadDiv:css('margin-top', '0.5em'):css('text-align', 'center')
uploadDiv:css('margin-top', '0.5em'):css('text-align', 'center')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add a document</span>]')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add document</span>]')
docsDiv:node(uploadDiv)
docsDiv:node(uploadDiv)
else
else
-- Just show the upload link as a button
local uploadDiv = mw.html.create('div')
local uploadDiv = mw.html.create('div')
uploadDiv:css('text-align', 'center')
uploadDiv:css('text-align', 'center')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add a document</span>]')
uploadDiv:wikitext('[' .. tostring(uploadUrl) .. ' <span class="infobox-button infobox-button-secondary">Add document</span>]')
docsDiv:node(uploadDiv)
docsDiv:node(uploadDiv)
end
end


-- Links section: Query for link subobjects and display by type
-- Links section
local linksQueryWikitext =
local linksQueryWikitext =
'{{#ask:\n' ..
'{{#ask:\n' ..
Line 1,069: Line 1,112:
local linksResult = frame:preprocess(linksQueryWikitext)
local linksResult = frame:preprocess(linksQueryWikitext)
-- Parse links and organize by type
local linksByType = {
local linksByType = {
Manufacturer = {},
Manufacturer = {},
Line 1,078: Line 1,120:
if linksResult then
if linksResult then
-- Match each table row
for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do
for rowContent in linksResult:gmatch('<tr[^>]*>(.-)</tr>') do
local cells = {}
local cells = {}
-- Extract content from each <td>
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
for cellContent in rowContent:gmatch('<td[^>]*>(.-)</td>') do
-- Strip any remaining HTML tags and get just the text
local cleanContent = cellContent:gsub('<[^>]+>', '')
local cleanContent = cellContent:gsub('<[^>]+>', '')
table.insert(cells, trim(cleanContent))
table.insert(cells, trim(cleanContent))
end
end
-- cells[1] = page/subobject, cells[2] = type, cells[3] = source, cells[4] = URL
if #cells >= 4 then
if #cells >= 4 then
local linkType = cells[2]
local linkType = cells[2]
Line 1,104: Line 1,142:
end
end
-- Display links section if there are any links
if hasLinks then
if hasLinks then
addSectionRow(container, 'Archival Links')
addSectionRow(container, 'Archival Links')
-- Add links in order: Database, Manufacturer, Retailer
local linkTypeOrder = {'Database', 'Manufacturer', 'Retailer'}
local linkTypeOrder = {'Database', 'Manufacturer', 'Retailer'}
for _, linkType in ipairs(linkTypeOrder) do
for _, linkType in ipairs(linkTypeOrder) do
if #linksByType[linkType] > 0 then
if #linksByType[linkType] > 0 then
-- If multiple links of the same type, combine them in one row
local linksHtml = {}
local linksHtml = {}
for _, link in ipairs(linksByType[linkType]) do
for _, link in ipairs(linksByType[linkType]) do
Line 1,125: Line 1,160:
addSectionRow(container, 'Contribute')
addSectionRow(container, 'Contribute')
-- Build the edit link URL
local editUrl = mw.uri.fullUrl(
local editUrl = mw.uri.fullUrl(
"Special:FormStart",
"Special:FormStart",
Line 1,134: Line 1,168:
)
)
-- Create the edit button div
local editDiv = container:tag('div')
local editDiv = container:tag('div')
css(editDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;')
css(editDiv, 'grid-column: 1 / -1; padding:0.2em 0.4em; text-align:center;')
-- Add edit button with primary action styling (using class for night mode compatibility)
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span class="infobox-button infobox-button-primary">Edit this data</span>]')
editDiv:wikitext('[' .. tostring(editUrl) .. ' <span class="infobox-button infobox-button-primary">Edit this data</span>]')


-- Return templatestyles tag + container HTML
return styleTag .. tostring(container)
return styleTag .. tostring(container)
end
end


return p
return p
MediaWiki Appliance - Powered by TurnKey Linux