Module:Probox Explained

---- This module implements --local getArgs = require('Module:Arguments').getArgslocal TableTools = require('Module:TableTools')local p =

--function GetPageLang (frame) local pagelang = frame:callParserFunction if pagelang

nil then pagelang = "/en" end return pagelang end

local function stringStarts(String,Start) return string.sub(String,1,string.len(Start))

Startend

local function stringToLowerCase(value) return mw.ustring.lower(value)end local function stringSpacesToUnderscores(value) return mw.ustring.gsub(value, " ", "_")end

local function stringFirstCharToUpper(str) return (str:gsub("^%l", string.upper))end

local function addTemplates(frame, data, args) -- adds additional template to the top of the page, above the infobox local page_template -- local tmplt_args = local template_div = mw.html.create('div') for k,v in pairs(data.templates) do -- if there is a matching arg, and it has a table of template possibilities if (args[k] and string.len(args[k]) > 0 and type(data.templates[k])

"table") then --convert args to lowercase before checking them against cats for tmplt_key, tmplt_val in pairs(data.templates[k]) do if data.templates.passArg

true then template_div :cssText("") :wikitext(frame:expandTemplate) --status is special casing. need to pass generic arg keys :done break elseif (stringToLowerCase(args[k])

tmplt_key and mw.title.new("Template:" .. tmplt_val).exists) then template_div :cssText("margin-bottom:1em;") :wikitext(frame:expandTemplate) :done end --convert args to lowercase and subs spaces for underscores --make sure specified template exists -- if (type(tmplt_val)

"string" and stringToLowerCase(args[k])

tmplt_key and mw.title.new("Template:" .. tmplt_val).exists) then

end end end page_template = tostring(template_div) return page_templateend

local function addCategories(frame, data, args, open_roles) -- will also look for numbered categories local cat_list = local base_cat = data.categories.base -- table.insert(cat_list, base_cat) --always need a base category -- -- adding role categories if data.categories.default then table.insert(cat_list, data.categories.default) end if data.categories.roles then role_cat = data.categories.roles for k,v in pairs(open_roles) do table.insert(cat_list, base_cat .. k:gsub("^%l", string.upper) .. role_cat) end end for k,v in pairs(data.categories) do -- if there is a matching arg, and it has a table of category possibilities if (args[k] and string.len(args[k]) > 0) then --convert args to lowercase before checking them against cats if type(data.categories[k])

"table" then for cat_key, cat_val in pairs(data.categories[k]) do if stringSpacesToUnderscores(stringToLowerCase(args[k]))

cat_key then --convert args to lowercase and subs spaces for underscores before checking them against cats table.insert(cat_list, base_cat .. cat_val) break end end elseif type(data.categories[k])

"string" then --concat the value of the cat field with the default cat directly table.insert(cat_list, base_cat .. data.categories[k]) end end end local page_categories = "" -- testing local pagelang = mw.text.trim(frame:callParserFunction)-- local pagelang = "/" .. mw.text.trim(frame:expandTemplate)

if #cat_list > 0 then if pagelang

nil then page_categories = "" .. tostring(table.concat(cat_list, " ")) .. "" else page_categories = "" .. tostring(table.concat(cat_list, pagelang .. " ")) .. pagelang .. "" end end if(args["status"]

"selected" or args["status"]

"SELECTED") then maintainer = args["grantee"] or args["organization"] or args["creator"] or args["grantee1"] or args["grantee2"] --if username is given as wikilink, extract clean username maintainer = mw.ustring.match(maintainer, "%[%[[Uu]ser:(.+)|") or maintainer page_categories = page_categories.. "" category_per_grantee = mw.title.new("Category:"..maintainer) if category_per_grantee.exists then page_categories = page_categories.. "" end end return page_categories end

local function makeTextField(field, field_div) -- makes a formatted text field for output, based on parameter -- values or on default values provided for that field -- type in the stylesheet field_div:cssText(field.style) if field.vtype2

"body" then field_div :tag('span') :cssText(field.style2)--inconsistent use of styles 2/3 here :wikitext(field.title)--we probably aren't using this for most things now, after Heather's redesign :done :tag('span') :cssText(field.style3) :wikitext(field.values[1]) :done elseif field.vtype2

"title" then field_div :tag('span') :cssText(field.style3) :wikitext(field.values[1]) :done elseif field.vtype2

"link" then field_div :tag('span') :cssText(field.style3) :wikitext("" .. field.title .."") :done end field_div:done return field_divend

local function makeImageField(field, field_div) -- makes a formatted image field for output, based on parameter values -- provided in the calling template or its parent template, or on default -- values provided in the stylesheet field_div:cssText(field.style) field_div :tag('span') :cssText(field.style3) if field.vtype2

"thumb" then field_div :wikitext("right|" .. field.width .."") elseif field.vtype2

"thumb2" then field_div :wikitext("center|" .. field.width .."") elseif field.vtype2

"link" then field_div :wikitext("" .. field.alignment .. "|" .. field.width .."|link=" .. field.link .. "") elseif field.vtype2

"badge" then if mw.ustring.find(field.values[1], "http", 1, true) then field_div :wikitext("" .. field.alignment .. "|" .. field.width .."|link=" .. field.values[1] .. " " .. "[" .. field.values[1] .. " " .. field.title .. "]") end elseif field.vtype2

"ui_button" then field_div :addClass(field.class) :wikitext(field.title) :done end field_div:done return field_divend

local function makeParticipantField(field, ftype) local field_div = mw.html.create('div') local title_span if field.icon then title_span = "left" .. "|18px " .. field.title else title_span = field.title end field_div :cssText(field.style) :tag('span') :cssText(field.style2) :wikitext(title_span) :done if ftype

"filled" then local i = 1 for k,v in ipairs(field.values) do if (i > 1 and field.icon) then --only insert extra padding if has icon field.style3 = "padding-left:25px; display:block" end if field.vtype2 then --ideally all configs should at least have this field for participants. FIXME if field.vtype2

"username" then v = "• " .. "" .. v .. "" elseif field.vtype2

"email" then v = "• " .. v end else v = "• " .. "" .. v .. "" end field_div :tag('span') :cssText(field.style3) :wikitext(v)-- :wikitext("• " .. "" .. v .. "") :done i = i + 1 end end field_div:allDone return field_divend

local function makeSectionDiv(sec_fields, sec_style) local sec_div = mw.html.create('div'):cssText(sec_style) sec_fields = TableTools.compressSparseArray(sec_fields) for findex, sec_field in ipairs(sec_fields) do -- should put this at the end of the function, and just append the other stuff sec_div:node(sec_field) end return sec_divend

local function makeParticipantsSection(frame, args, data, filled_role_data, open_roles) local filled_role_fields = for role, val_table in pairs(filled_role_data) do local field = data.fields[role] field.title = mw.text.trim(frame:expandTemplate) field.values = for val_num, val_text in ipairs(filled_role_data[role]) do field.values[#field.values + 1] = val_text end local filled_field_div = makeParticipantField(field, "filled") filled_role_fields[field.rank] = filled_field_div end local sec_div = makeSectionDiv(filled_role_fields, data.styles.section["participants"]) if (data.fields.more_participants and args.more_participants and stringToLowerCase(args.more_participants))

"yes" then -- really need this here? -- if (args.portal

"Idealab" or args.portal

"Research") then -- beware, exceptions everywhere sec_div:tag('span'):cssText("font-style:italic; color: #888888"):wikitext(mw.text.trim(frame:expandTemplate)):done-- elseif args.portal

"Patterns" then-- sec_div:tag('span'):cssText("font-style:italic; color: #888888"):wikitext("a learning pattern for..."):done-- else for role, val in pairs(open_roles) do -- should make these ordered using compressSparseArray, as above local field = data.fields[role] field.title = mw.text.trim(frame:expandTemplate) if field.icon then field.icon = field.icon_inactive end local open_field_div = makeParticipantField(field, "open") sec_div:node(open_field_div) end end sec_div:allDone return sec_divend

local function makeSectionFields(args, field) -- ui button is separate local field_div = mw.html.create('div'):cssText(field.style) --why declare this here? if field.vtype

"image" then if (field.isRequired

true or (args[field.arg] and string.len(args[field.arg]) > 0)) then --should move this up, may not just apply to images field_div = makeImageField(field, field_div) end elseif field.vtype

"text" then field_div = makeTextField(field, field_div) else end return field_div -- make sure div is 'done'end

local function makeSection(frame, args, data, box_sec) -- return a div for a section of the box including child divs -- for each content field in that section -- local sec_div = mw.html.create('div'):cssText(data.styles.section[box_sec]) local sec_fields = for k,v in pairs(data.fields) do if data.fields[k].section

box_sec then local field = data.fields[k] field.title = mw.text.trim(frame:expandTemplate) field.values = if (args[k] and string.len(args[k]) > 0) then field.values[1] = args[k] --does not accept numbered args if field.toLowerCase

true then -- special casing to make IEG status=SELECTED to display in lowercase field.values[1] = stringToLowerCase(field.values[1]) end local field_div = makeSectionFields(args, field) sec_fields[field.rank] = field_div elseif field.isRequired

true then if field.vtype

"text" then field.values[1] = mw.text.trim(frame:expandTemplate) else field.values[1] = field.default end local field_div = makeSectionFields(args, field) sec_fields[field.rank] = field_div else --don't make a section for this field end end end local sec_div = makeSectionDiv(sec_fields, data.styles.section[box_sec]) return sec_divend

local function makeInfobox(frame, args, data, filled_role_data, open_roles) -- builds the infobox. Some content sections are required, others -- are optional. Optional sections are defined in the stylesheet. local box = mw.html.create('div'):cssText(data.styles.box.outer) local inner_box = mw.html.create('div'):cssText(data.styles.box.inner) if data.sections.above

true then local sec_top = makeSection(frame, args, data, "above") box:node(sec_top) end if data.sections.nav

true then local sec_nav = makeSection(frame, args, data, "nav") box:node(sec_nav) end local sec_head = makeSection(frame, args, data, "head") inner_box:node(sec_head) local sec_main = makeSection(frame, args, data, "main") inner_box:node(sec_main) if data.sections.participants

true then local sec_participants = makeParticipantsSection(frame, args, data, filled_role_data, open_roles) inner_box:node(sec_participants) end if data.sections.cta

true then local sec_cta = makeSection(frame, args, data, "cta") sec_cta:addClass("noprint") -- no much use in print outs inner_box:node(sec_cta) inner_box:tag('div'):cssText("clear:both;"):done --clears buttons in the cta sections end inner_box:allDone box:node(inner_box) if data.sections.below

true then local sec_bottom = makeSection(frame, args, data, "below") box:node(sec_bottom) end box:allDone return boxend

local function orderStringtoNumber(array, val, num) if num > table.getn(array) then array[#array+1] = val else table.insert(array, num, val) end return arrayend local function isJoinable(args, data) if args.more_participants

"NO" then data.fields.join = nil if data.fields.endorse then data.fields.endorse.style = "display:inline; float:right;" end end return dataend

local function deepCopyTable(data) -- the deep copy is a workaround step to avoid the restrictions placed on -- tables imported through loadData if type(data) ~= 'table' then return data end local res = for k,v in pairs(data) do if type(v)

'table' then v = deepCopyTable(v) end res[k] = v end return resend local function getPortalData(args) -- loads the relevant stylesheet, if a sub-template was called with a portal -- argument and a stylesheet exists with the same name. For example, calling -- would load the Module:Probox/Idealab -- stylesheet. local data_readOnly = local data_writable = if (args.portal and mw.title.makeTitle('Module', 'Probox/' .. args.portal).exists) then data_readOnly = mw.loadData("Module:Probox/" .. args.portal) else data_readOnly = mw.loadData("Module:Probox/Default") end -- data_writable = TableTools.shallowClone(data_readOnly) data_writable = deepCopyTable(data_readOnly) return data_writableend

local function TCTlookup(args) local tct_path = tostring(args.translations) --mw.log(mw.title.getCurrentTitle.subpageText) local tct_subpage = mw.title.getCurrentTitle.subpageText if tct_subpage

"en" then tct_path = tct_path .. "/" .. tct_subpage elseif (mw.title.new("Template:" .. args.translations .. "/" .. tct_subpage).exists and mw.language.isSupportedLanguage(tct_subpage)) then tct_path = tct_path .. "/" .. tct_subpage else tct_path = tct_path .. "/" .. "en" end -- mw.log(tct_path) return tct_pathend

local function getRoleArgs(args, available_roles) -- returns: -- 1) a table of ordered values for valid role params, -- even if numbered nonsequentially -- 2) a table of all roles with at least 1 empty param, -- plus the default volunteer role local filled_role_data = local open_roles = if available_roles.default then -- some boxes have default role to join open_roles[available_roles.default] = true end for rd_key, rd_val in pairs(available_roles) do for a_key, a_val in pairs(args) do if stringStarts(a_key, rd_key) then if string.len(a_val)

0 then open_roles[rd_key] = true else if not filled_role_data[rd_key] then filled_role_data[rd_key] = end local arg_num = tonumber(a_key:match('^' .. rd_key .. '([1-9]%d*)$')) if arg_num then filled_role_data[rd_key] = orderStringtoNumber(filled_role_data[rd_key], a_val, arg_num) else table.insert(filled_role_data[rd_key], 1, a_val) end end end end end return filled_role_data, open_rolesend

function p.main(frame) local args = getArgs(frame,) local data = getPortalData(args) data = isJoinable(args, data) if not (args.translations and mw.title.new("Template:" .. args.translations).exists) then args.translations = "Probox/Default/Content" end -- mw.log(args.translations) -- if the TCT content index is under translation, check for translations in the subpage language if mw.title.new("Template:" .. args.translations .. "/en").exists then args.translations = TCTlookup(args) end if data.sections.cta

true then args.talk = tostring(mw.title.getCurrentTitle.talkPageTitle) -- expensive end local filled_role_data, open_roles = getRoleArgs(args, data.roles) local box = makeInfobox(frame, args, data, filled_role_data, open_roles) local infobox = tostring(box) -- only add cats if not in Template or User or Meta namespace if (data.categories and (mw.title.getCurrentTitle.nsText ~= "Template" and mw.title.getCurrentTitle.nsText ~= "User" and mw.title.getCurrentTitle.nsText ~= "Meta") and not args.noindex) then -- FIXME specify namespace in config, so that categories only appear if template is translcuded in that namespace local page_categories = addCategories(frame, data, args, open_roles) infobox = infobox .. page_categories end if data.templates then local top_template = addTemplates(frame, data, args) infobox = top_template .. infobox end return infoboxend

return p