Module:Infobox television/sandbox2

require("strict")

--- @module

local p = {}

-- local maintenance_categories = {

-- non_matching_title = "Category:Pages using infobox television with non-matching title",

-- unnecessary_title_parameter = "Category:Pages using infobox television with unnecessary name parameter",

-- unlinked_values = "%s",

-- temp = "%s",

-- }

local maintenance_categories = {

-- alt_name = {

-- category = "%s",

-- warning = "",

-- },

dates_incorrectly_formatted = {

category = "Category:Pages using infobox television with nonstandard dates",

warning = {

future_dates = "the infobox does not support dates set in the future. Remove the date and only add a date once the program has aired.",

start_date = "start dates should use the {{tl|Start date}} template.",

end_date = "end dates should use the {{tl|End date}} template.",

multiple_dates = "do not use multiple date parameters in the same parameter. Each date should be placed in its own parameter.",

}

},

dates_missing = {

category = "Category:Pages using infobox television with missing dates",

warning = {

start_date = "a |first_aired= date is missing.",

end_date = "a |last_aired= date is missing.",

}

},

flag_icon = {

category = "Category:Pages using infobox television with flag icon",

warning = "flag icons should not be used per MOS:INFOBOXFLAG.",

},

image_values_without_an_image = {

category = "Category:Pages using infobox television with image-related values without an image",

warning = "an image is not used in the infobox. Remove value from |%s=.",

},

incorrectly_formatted = {

category = "%s",

warning = {

country_name = "country name should not be an abbreviation.",

based_on = "use the title of the original work in the |based_on= value.",

duplicate = "duplicate parameter usage found. Only one of %s can be used.",

external_links = "external links should not be used in infobox. Remove link from |%s=.",

inccorct_credit_usage = "a credit in |%s= uses %s. %s has its own parameter, please use it.",

inccorct_credit_usage_based_on = "a credit in |%s= uses %s. Use the |based_on= parameter instead.",

invalid_credit = "%s is not a valid credit for |%s=.",

italic_title = "remove value from |italic_title=.",

language = "langauge should not be linked per WP:OVERLINK.",

multiple_networks = "for shows with multiple networks or release gaps, use the additional release date parameters.",

num_episodes = "|num_episodes= should only be a number value.",

}

},

manual_display_title = {

category = "Category:Pages using infobox television with unnecessary manual displaytitle",

warning = "remove %s as the infobox handles this automatically.",

},

manual_display_title_temp_tracking = {

category = "Category:Pages using infobox television with manual displaytitle",

warning = "",

},

non_matching_title = {

category = "Category:Pages using infobox television with non-matching title",

warning = "",

},

unnecessary_title_parameter = {

category = "Category:Pages using infobox television with unnecessary name parameter",

warning = "",

},

unlinked_values = {

category = "%s",

warning = "",

},

temp = {

category = "%s",

warning = "",

},

}

local number_of_network_sets = 8

local error_prefix = "{{tl|Infobox television}} errors: "

local function add_warning(error_text)

--TODO: ignore if on the testcases page?

mw.addWarning(error_prefix .. error_text)

end

--- Returns a table consisting of the title's title parts.

---

--- The return table's properties:

--- - title - The title.

--- - disambiguation - the disambiguation text without parentheses.

---

--- Note: could potentially be moved to an outside module for other template and module uses.

---

--- @param text string

--- @return table

local function get_title_parts(text)

local title, disambiguation = string.match(text, "^(.+) (%b())$")

if not title or type(title) ~= "string" then

title = text

end

---@type table

local title_parts = {title = -- nil title, disambiguation = nil}

if not disambiguation or type(disambiguation) ~= "string" then

return title_parts

end

-- Remove outside parentheses from names which use parentheses as part of the name such as "episode (Randall and Hopkirk (Deceased))".

disambiguation = string.sub(-- nil disambiguation, 2, -2)

title_parts.disambiguation = -----@not number disambiguation

return title_parts

end

--- Returns a maintenance category if the italic_title value is not "no".

---

--- Infobox parameters checked:

--- - |italic_title=

---

--- @param args table

--- @return string

local function is_italic_title_valid_value(args)

if args.italic_title and args.italic_title ~= "no" then

add_warning(maintenance_categories.incorrectly_formatted.warning.italic_title)

return string.format(maintenance_categories.incorrectly_formatted.category, "italic_title")

end

return ""

end

--TODO: add doc

local function is_value_a_url(args)

for key, value in pairs(args) do

if string.find(value, "http") then

add_warning(string.format(maintenance_categories.incorrectly_formatted.warning.external_links, key))

return string.format(maintenance_categories.incorrectly_formatted.category, key)

end

end

return ""

end

--- Returns a maintenance category if the start_date value is set to a future date.

---

--- Infobox parameters checked:

--- - |first_aired[1-number_of_network_sets]=

--- - |released[1-number_of_network_sets]=

---

--- @param args table

--- @return string

local function is_start_date_in_the_future(start_date)

-- Extract the date from the start_date.

local date_pattern = ".*]*>(.-)"

local extracted_date = start_date:match(date_pattern)

-- Parse the date components

local year, month, day = extracted_date:match("(%d+)-(%d+)-(%d+)")

-- Create a table with the parsed date components

local date_table = {

year = tonumber(year) or 0,

month = tonumber(month) or 0,

day = tonumber(day) or 0,

hour = 0, -- Assuming 00:00:00 for simplicity

min = 0,

sec = 0

}

-- Convert the date table to a Unix timestamp

local start_date_timestamp = os.time(date_table)

-- Get the current date components.

local current_date = os.date("*t")

-- Set the time components to zero.

current_date.hour = 0

current_date.min = 0

current_date.sec = 0

-- Convert the date components into a timestamp.

local current_timestamp = os.time(current_date)

--local extracted_date_timestamp = os.time(extracted_date)

-- The infobox does not allow for future dates.

if current_timestamp < start_date_timestamp then

add_warning(maintenance_categories.dates_incorrectly_formatted.warning.future_dates)

return maintenance_categories.dates_incorrectly_formatted.category

end

return ""

end

--- Returns a maintenance category if the dates are not formatted correctly with

--- {{Start date}} and {{End date}} templates.

---

--- Infobox parameters checked:

--- - |first_aired[1-number_of_network_sets]=

--- - |released[1-number_of_network_sets]=

--- - |last_aired[1-number_of_network_sets]=

---

--- Note: all_tests is meant only for /testcases testing.

---

--- @param all_tests string Testing conditional value.

--- @param released string The start date value.

--- @param first_aired string The start date value.

--- @param last_aired string The end date value.

--- @return string

local function are_dates_formatted_correctly(all_tests, released, first_aired, last_aired)

-- To keep /testcases clean, this is set so only what is test is shown.

if all_tests == "no" then

return ""

end

-- Config parameters

local first_aired_future = "Upcoming"

local last_aired_current = "present"

local start_date_class = "itvstart"

local end_date_class = "itvend"

local film_date_class = "film%-date"

local start_date = released or first_aired

-- A start date should always be set.

if not start_date then

add_warning(maintenance_categories.dates_missing.warning.start_date)

return maintenance_categories.dates_missing.category

end

-- Validate the start date is formmated using {{Start date}} and not any other template, including {{Film date}}, or uses the word "Upcoming".

if start_date and (string.find(start_date, film_date_class) or not string.find(start_date, start_date_class) and start_date ~= first_aired_future) then

add_warning(maintenance_categories.dates_incorrectly_formatted.warning.start_date)

return maintenance_categories.dates_incorrectly_formatted.category

end

-- An end date should always be set if the show or film wasn't released all at once.

if first_aired and first_aired ~= first_aired_future and not last_aired then

add_warning(maintenance_categories.dates_missing.warning.end_date)

return maintenance_categories.dates_missing.category

end

-- Validate the end date is formmated using {{End date}} and not any other template, or uses the word "present".

if last_aired and (not string.find(last_aired, end_date_class) and last_aired ~= last_aired_current) then

add_warning(maintenance_categories.dates_incorrectly_formatted.warning.end_date)

return maintenance_categories.dates_incorrectly_formatted.category

end

-- Only one date should be used per field.

if (start_date and select(2, string.gsub(start_date, start_date_class, "")) > 1) or (last_aired and select(2, string.gsub(last_aired, end_date_class, "")) > 1) then

add_warning(maintenance_categories.dates_incorrectly_formatted.warning.multiple_dates)

return maintenance_categories.dates_incorrectly_formatted.category

end

-- Check if start date is set to a future date.

if start_date ~= first_aired_future then

return is_start_date_in_the_future(start_date)

end

return ""

end

--- Returns a maintenance category if exclusive parameter sets are used.

---

--- Infobox parameters checked:

--- - |image_alt= and |alt=

--- - |image_size= and |image_upright=

--- - |based_on= and |inspired_by=

--- - |screenplay= and |teleplay=

--- - |presenter= and |host=

--- - |narrator=, |narrated= and |announcer=

--- - |theme_music_composer= and |music=

--- - |open_theme= and |opentheme=

--- - |end_theme= and |endtheme=

--- - |released[1-number_of_network_sets]= and |first_aired[1-number_of_network_sets]=

--- - |released[1-number_of_network_sets]= and |last_aired[1-number_of_network_sets]=

--- - |network[1-number_of_network_sets]= and |channel[1-number_of_network_sets]=

---

--- The function currently checks if the network and channel parameters both have values.

---

--- @param args table

--- @param args table

--- @return string

local function _are_exclusive_parameter_sets_used(args, parameters)

for key, set in pairs(parameters) do

if args[set[1]] and args[set[2]] then

add_warning(string.format(maintenance_categories.incorrectly_formatted.warning.duplicate, key))

return string.format(maintenance_categories.incorrectly_formatted.category, "-duplicate")

end

end

return ""

end

--- Returns a maintenance category if exclusive parameter sets are used.

--- Create a set parameters to check.

--- Does not include release information related parameters which are sent from

--- a different function due to their numbered variations.

--- See _are_exclusive_parameter_sets_used() for more details.

---

--- @param args table

--- @return string

local function are_exclusive_parameter_sets_used(args)

local parameters = {

["|image_alt= and |alt="] = {"image_alt", "alt"},

["|image_size= and |image_upright="] = {"image_size", "image_upright"},

["|based_on= and |inspired_by="] = {"based_on", "inspired_by"},

["|screenplay= and |teleplay="] = {"screenplay", "teleplay"},

["|presenter= and |host="] = {"presenter", "host"},

["|narrator= and |narrated="] = {"narrator", "narrated"},

["|narrator= and |announcer="] = {"narrator", "announcer"},

["|narrated= and |announcer="] = {"narrated", "announcer"},

["|theme_music_composer= and |music="] = {"theme_music_composer", "music"},

["|open_theme= and |opentheme="] = {"open_theme", "opentheme"},

["|end_theme= and |endtheme="] = {"end_theme", "endtheme"},

}

return _are_exclusive_parameter_sets_used(args, parameters)

end

--- Returns a maintenance category if the values are unlinked.

---

--- Infobox parameters checked:

--- - |network[1-number_of_network_sets]=

--- - |channel[1-number_of_network_sets]=

---

--- The function currently checks if a value is unlinked.

---

--- @param args table

--- @return string

local function are_values_unlinked(args)

for key, value in pairs(args) do

-- Check whether the values are linked.

if value and not string.find(value, "%[%[.*%]%]") then

return string.format(maintenance_categories.unlinked_values.category, key)

end

end

return ""

end

--- Returns a maintenance category if the dates are not formatted correctly

--- and using "Original", "Revival" or italics to denote a split in the date range.

---

--- Infobox parameters checked:

--- - |first_aired[1-number_of_network_sets]=

--- - |released[1-number_of_network_sets]=

--- - |last_aired[1-number_of_network_sets]=

--- - |network[1-number_of_network_sets]=

--- - |channel[1-number_of_network_sets]=

---

--- @param args table

--- @return string

local function does_release_information_have_extraneous_text(args)

for k, v in pairs(args) do

if string.find(string.lower(v), "original") and not string.find(string.lower(v), "aboriginal") or

string.find(string.lower(v), "revival") or

string.find(v, "''") then

add_warning(maintenance_categories.incorrectly_formatted.warning.multiple_networks)

return string.format(maintenance_categories.incorrectly_formatted.category, k)

end

end

return ""

end

--- Returns a maintenance category if the release information:

---- Is not formatted correctly

---- Has extraneous text

---- Dates don't use correct mark up

---

--- Infobox parameters checked:

--- - |first_aired[1-number_of_network_sets]=

--- - |released[1-number_of_network_sets]=

--- - |last_aired[1-number_of_network_sets]=

--- - |network[1-number_of_network_sets]=

--- - |channel[1-number_of_network_sets]=

---

--- @param args table

--- @return string

local function is_release_information_formatted_correctly(args)

local release_information = {"first_aired", "released", "last_aired", "network", "channel"}

local categories = {}

for i = 1, number_of_network_sets do

local num = ""

if i > 1 then

num = i

end

local numbered_args = {}

for _, v in pairs(release_information) do

numbered_args[v .. num] = args[v .. num]

end

-- If current table is empty, break current cycle.

if next(numbered_args) then

table.insert(categories, does_release_information_have_extraneous_text(numbered_args))

table.insert(categories, are_values_unlinked({network = args["network" .. num], channel = args["channel" .. num]}))

local duplicates = {

["|network" .. num .. "= and |channel" .. num .. "="] = {"network" .. num, "channel" .. num},

["|released" .. num .. "= and |first_aired" .. num .. "="] = {"released" .. num, "first_aired" .. num},

["|released" .. num .. "= and |last_aired" .. num .. "="] = {"released" .. num, "last_aired" .. num},

}

table.insert(categories, _are_exclusive_parameter_sets_used(args, duplicates))

table.insert(categories, are_dates_formatted_correctly(args.all_tests, args["released" .. num], args["first_aired" .. num], args["last_aired" .. num]))

end

end

return categories

end

--- Returns a maintenance category if a {{Italic title}} or {{DISPLAYTITLE}} template is used.

--- Checks also for the following {{Italic title}} redirects:

---- Italic

---- Italics

---- Italictitle

---- Italics title

---

--- Testing parameters:

--- - |page_test= - a real Wikipedia page to read the content of the page.

---

--- Infobox parameters checked:

--- - |italic_title=

---

--- @param args table

--- @return string

local function has_display_title(args)

--TODO: when testing below is done uncomment code

--if args.italic_title then

-- return ""

--end

local article

if args.page_test then

article = mw.title.new(args.page_test)

else

article = mw.title.getCurrentTitle()

end

local page_text = article:getContent()

if not page_text then

return ""

end

if (string.find(page_text, "{{[Ii]talics?%s?title}}") or string.find(page_text, "{{[Ii]talics?}}")) and not string.match(page_text, "{{[Ii]talic title|all=yes}}") then

add_warning(string.format(maintenance_categories.manual_display_title.warning, "{{tl|Italic title}}"))

return maintenance_categories.manual_display_title.category

end

local display_title = string.match(page_text, "{{DISPLAYTITLE:(.*)}}")

if display_title then

local article_title = article.text

if article_title == display_title or article_title == string.sub(display_title, 3, string.len(display_title) - 2) then

add_warning(string.format(maintenance_categories.manual_display_title.warning, "DISPLAYTITLE"))

return maintenance_categories.manual_display_title.category

elseif string.find(display_title, "") or string.find(display_title, "") then

-- TODO: This is valid. Will remove when done with cleanup.

return ""

else

-- TODO: remove when done checking results.

--add_warning(maintenance_categories.manual_display_title_temp_tracking.warning)

return maintenance_categories.manual_display_title_temp_tracking.category

end

end

return ""

end

--- Returns a maintenance category if a flag icon is used.

---

--- All the infobox values are checked.

---

--- @param args table

--- @return string

local function has_flag_icon(args)

for _, v in pairs(args) do

if string.find(v, "flagicon") then

add_warning(maintenance_categories.flag_icon.warning)

return maintenance_categories.flag_icon.category

end

end

return ""

end

--- Returns a maintenance category if num_episodes uses:

--- - "as of" as text

--- - A date template

---

--- Infobox parameters checked:

--- - |num_episodes=

---

--- @param num_episodes string

--- @return string

local function does_num_episodes_have_extraneous_text(num_episodes)

if not num_episodes then

return ""

end

if string.find(string.lower(num_episodes), "as of")

or string.find(num_episodes, "dtstart") then

add_warning(maintenance_categories.incorrectly_formatted.warning.num_episodes)

return string.format(maintenance_categories.incorrectly_formatted.category, "num_episodes")

end

return ""

end

--- Returns a maintenance category if |based_on uses a generic word instead

--- of the title of the original work:

--- "book"

--- "novel"

---

--- Infobox parameters checked:

--- - |based_on=

---

--- @param num_episodes string

--- @return string

local function is_based_on_used_correctly(based_on)

if not based_on then

return ""

end

local generic_titles = {

"book",

"novel",

}

-- Remove content enclosed by double single quotes (text)

local stripped_text = based_on:gsub("[^']*", "")

-- Remove content enclosed by double quotes ("text")

stripped_text = based_on:gsub('"[^"]*"', "")

-- Remove content enclosed by double square brackets (text)

stripped_text = based_on:gsub("%[%[.-%]%]", "")

-- Convert to lowercase to ensure case-insensitive matching

stripped_text = stripped_text:lower()

for _, word in pairs(generic_titles) do

if string.find(stripped_text, word) then

add_warning(maintenance_categories.incorrectly_formatted.warning.based_on)

return string.format(maintenance_categories.incorrectly_formatted.category, "based_on")

end

end

return ""

end

--- Checks if a string is in the exceptions list.

---

--- @param str string

--- @return boolean

local function is_in_credit_exceptions(str)

local exceptions = {

"Aulsondro \"Novelist\" Hamilton",

-- Booker can be a last name.

"Booker",

"Jack Trevor Story",

"Tanveer Bookwala",

"Sanford Bookstaver",

}

for _, exception in ipairs(exceptions) do

if string.find(str, exception) then

return true

end

end

return false

end

--- Returns a maintenance category if a credit information matches a pattern.

---

--- @param key string

--- @param value string

--- @param invalid_credits table

--- @return string

local function check_credits(key, value, invalid_credits)

for _, invalid_credit in pairs(invalid_credits) do

local pattern = "%f[%a]" .. invalid_credit

if string.find(string.lower(value), pattern) and not is_in_credit_exceptions(value) then

return string.format(maintenance_categories.incorrectly_formatted.category, key), invalid_credit

end

end

end

--- Returns a maintenance category if "lyric" or "dialogue" are found.

--- This is a temporary function to check how widespread this unsupported usage is.

---

--- @param parameter string

--- @param text string

--- @return string

local function is_credit_used_correctly_tracking_credits(parameter, text)

local credits = {

"lyric",

"dialogue",

}

local category, invalid_credit = check_credits(parameter, text, credits)

if category then

return string.format(maintenance_categories.temp.category, invalid_credit)

end

return ""

end

--- Returns a maintenance category if a writing credit is used incorrectly.

---

--- @param parameter string

--- @param text string

--- @return string

local function is_credit_used_correctly_incorrectly_placed_credits(parameter, text)

local credits = {

"book",

"manuscript",

"novel",

"script",

"screenplay",

"story",

"teleplay",

}

local based_on = {

book = true,

manuscript = true,

novel = true,

}

local category, invalid_credit = check_credits(parameter, text, credits)

if category then

local invalid_credit_clean = string.gsub(invalid_credit, "%%", "")

local valid_parameter

if based_on[invalid_credit_clean] then

valid_parameter = "|based_on="

add_warning(string.format(maintenance_categories.incorrectly_formatted.warning.inccorct_credit_usage_based_on, parameter, invalid_credit_clean, valid_parameter))

else

valid_parameter = "|" .. invalid_credit_clean .. "="

add_warning(string.format(maintenance_categories.incorrectly_formatted.warning.inccorct_credit_usage, parameter, invalid_credit_clean, valid_parameter))

end

return category

end

return ""

end

--- Returns a maintenance category if an unusupported credit is used.

---

--- @param parameter string

--- @param text string

--- @return string

local function is_credit_used_correctly_invalid_credits(parameter, text)

local credits = {

"assistant",

"associate",

"co%-",

"executive",

"line producer",

"on%-line",

"prod%.",

"supervising",

}

local category, invalid_credit = check_credits(parameter, text, credits)

if category then

local invalid_credit_clean = "" .. string.gsub(invalid_credit, "%%", "") .. ""

add_warning(string.format(maintenance_categories.incorrectly_formatted.warning.invalid_credit, invalid_credit_clean, parameter))

return category

end

return ""

end

--- Returns a maintenance category if a credit information entered is from the following list:

--- "assistant", associate", "co-", "executive", "line producer", "on-line", "prod%.", "supervising"

--- "book", "manuscript", "novel", "script", "screenplay", "story", "teleplay"

--- TODO: "lyric" and "dialogue" should be either supported or removed.

--- These credits have their own unique parameters that should be used instead.

---

--- Infobox parameters checked:

--- - |director=

--- - |editor=

--- - |executive_producer=

--- - |producer=

--- - |screenplay=

--- - |story=

--- - |teleplay=

--- - |writer=

---

--- @param args table

--- @return string

local function is_credit_used_correctly(args)

local credits_list = {

"director",

"editor",

"executive_producer",

"producer",

"screenplay",

"story",

"teleplay",

"writer",

}

local credits = {}

for _, value in pairs(credits_list) do

credits[value] = args[value]

end

local tracking_credits = {

"lyric", -- temp

"dialogue", -- temp

}

local delink = require("Module:Delink")._delink

for parameter, text in pairs(credits) do

text = delink{text}

local category = is_credit_used_correctly_invalid_credits(parameter, text)

category = category .. is_credit_used_correctly_incorrectly_placed_credits(parameter, text)

category = category .. is_credit_used_correctly_tracking_credits(parameter, text)

return category

end

return ""

end

--- Returns a maintenance category if the country information entered is from the following list:

--- U.S.A, USA, U.S., US, UK, U.K.

---

--- Infobox parameters checked:

--- - |country=

---

--- @param country string

--- @return string

local function is_country_name_valid(country)

if not country then

return ""

end

local args = {"U.S", "US", "UK", "U.K."}

for _, v in pairs(args) do

if string.find(country, v) then

add_warning(maintenance_categories.incorrectly_formatted.warning.country_name)

return string.format(maintenance_categories.incorrectly_formatted.category, "country")

end

end

return ""

end

--- Returns a maintenance category if the language is linked.

---

--- Infobox parameters checked:

--- - |language=

---

--- The function currently checks if the following values are present:

--- - ] - links.

---

--- @param args table

--- @return string

local function is_language_linked(args)

local parameters = {language = args.language}

for key, value in pairs(parameters) do

for _, bad_value in pairs({"]"}) do

if string.find(value, bad_value, 1, true) then

add_warning(maintenance_categories.incorrectly_formatted.warning.language)

return string.format(maintenance_categories.incorrectly_formatted.category, key)

end

end

end

return ""

end

-- Splits a string and returns a table.

--

-- @param str string

-- @return table

local function split(str)

local sep = "\n"

local result = {}

local regex = ("([^%s]+)"):format(sep)

for each in str:gmatch(regex) do

table.insert(result, each)

end

return result

end

-- Returns a string value clean from various list syntax.

--

-- @param str string

-- @return string

local function clean_list_syntax(str)

str = string.gsub(str, "\127[^\127]*UNIQ%-%-(%a+)%-%x+%-QINU[^\127]*\127", "") -- Remove all strip-markers.

str = string.gsub(string.gsub(str, "%<%/? *div[^%>]*%>", ""), "%<%/? *span[^%>]*%>", "") -- Removes div and span tags.

str = string.gsub(str, "%<%/? *ul[^%>]*%>", "") -- Remove list tags.

str = string.gsub(str, "%<%/? *li[^%>]*%>", "\n") -- Remove list tags. Replace with new line.

str = string.gsub(str, "", "\n") -- Replace
(and variants) with new line.

str = string.gsub(str, "\n\n", "\n") -- Replace double new line with a single new line.

str = string.gsub(str, "*", "") -- Remove asterisks.

return str

end

--- Returns a maintenance category if the |image= value includes the "File:"

--- or "Image:" prefix, or if it is a URL.

---

--- Infobox parameters checked:

--- - |image=

---

--- @param image string

--- @return string

local function is_image_using_incorrect_syntax(image)

if not image then

return ""

end

local invalid_strings = {

"file:",

"image:",

}

image = string.lower(image)

for _, invalid in ipairs(invalid_strings) do

if string.find(image, invalid) then

return string.format(maintenance_categories.incorrectly_formatted.category, "image")

end

end

return ""

end

--- Returns a maintenance category if the |image_size= value includes "px".

---

--- Infobox parameters checked:

--- - |image_size=

---

--- @param image_size string

--- @return string

local function is_image_size_using_px(image_size)

if image_size and string.find(image_size, "px") then

return string.format(maintenance_categories.incorrectly_formatted.category, "image_size")

end

return ""

end

--- Returns a maintenance category if there is no image file while image auxiliary values are present.

---

--- Infobox parameters checked:

--- - |image=

--- - |image_size=

--- - |image_upright=

--- - |image_alt=

--- - |alt=

--- - |caption=

---

--- @param args table

--- @return string

local function are_image_auxiliary_values_used_for_no_image(args)

if args.image then

return ""

end

local image_args = {

image_size = args.image_size,

image_upright = args.image_upright,

image_alt = args.image_alt,

alt = args.alt,

caption = args.caption

}

for key, value in pairs(image_args) do

if value then

add_warning(string.format(maintenance_categories.image_values_without_an_image.warning, key))

return maintenance_categories.image_values_without_an_image.category

end

end

return ""

end

--- Returns the display title text used in either the {{DISPLAYTITLE}} or {{Italic title}} templates.

---

--- @param page_text string

--- @param article_title string

--- @return string | nil

local function get_display_title_text(page_text, article_title)

if not page_text then

return nil

end

local title_modification = string.match(page_text, "{{DISPLAYTITLE:(.-)}}")

if title_modification and type(title_modification) == "string" then

local title_parts = get_title_parts(title_modification)

return string.gsub(title_parts.title, "'", "")

end

title_modification = string.match(page_text, "{{[Ii]talic title|all=yes}}")

if title_modification and type(title_modification) == "string" then

return article_title

end

return nil

end

--- Returns the title used in the {{Lowercase title}} template and an optional maintenance category.

---

--- @param page_text string

--- @param args table

--- @param article_title string

--- @param title_parts table

--- @param return_category boolean

--- @return string | nil

local function get_lowercase_template_status(page_text, args, article_title, title_parts, return_category)

if not page_text then

return nil

end

local lowercase_template = string.match(page_text, "{{[Ll]owercase title.-}}")

if not lowercase_template then

return nil

end

local lowercase_title

if string.find(lowercase_template, "|force=") then

lowercase_title = string.gsub(article_title,"^%u", string.lower)

else

lowercase_title = string.gsub(title_parts.title,"^%u", string.lower)

end

if return_category and args.name then

if args.name == lowercase_title then

return maintenance_categories.unnecessary_title_parameter.category

else

return maintenance_categories.non_matching_title.category

end

return ""

end

return lowercase_title

end

--- Returns the title used in the {{Correct title}} template and an optional maintenance category.

---

--- @param page_text string

--- @param args table

--- @param return_category boolean

--- @return string | nil

local function get_correct_title_value(page_text, args, return_category)

if not page_text then

return nil

end

local correct_title_template_pattern = "{{[Cc]orrect title|title=(.*)|reason=.-}}"

local correct_title = string.match(page_text, correct_title_template_pattern)

if not correct_title then

correct_title_template_pattern = "{{[Cc]orrect title|(.*)|reason=.-}}"

correct_title = string.match(page_text, correct_title_template_pattern)

end

if not correct_title and type(correct_title) ~= "string" then

return nil

end

local correct_title_title_parts = get_title_parts(correct_title)

if return_category and args.name then

if args.name == correct_title or args.name == correct_title_title_parts.title then

return maintenance_categories.unnecessary_title_parameter.category

else

return maintenance_categories.non_matching_title.category

end

end

return correct_title

end

--- Returns a maintenance category if the infobox title is equal to the article title.

---

--- Infobox parameters checked:

--- - |name=

---

--- The function currently checks if the infobox title is equal to the article title while ignoring styling such as:

--- - Nowrap spans.

--- - Line breaks.

---

--- A return value can be one of three options:

--- - The value of maintenance_categories.non_matching_title - when the args.title does not match the article title..category

--- - The value of maintenance_categories.unnecessary_title_parameter - when the args.title matches the article title..category

--- - An empty string - when args.name isn't used or the args.name uses an allowed modification

--- (such as a nowrap template) while the rest of the args.name matches the article title.

---

--- Testing parameters:

--- - |page_test= - a real Wikipedia page to read the content of the page.

--- - |page_title_test= - the title of the page being checked.

---

--- @param frame table

--- @param args table

--- @return string

local function is_infobox_title_equal_to_article_title(frame, args)

if not args.name then

return ""

end

local page_text

if args.page_test then

page_text = mw.title.new(args.page_test):getContent()

else

page_text = mw.title.getCurrentTitle():getContent()

end

-- Check if the article is using a {{Correct title}} template.

local correct_title = get_correct_title_value(page_text, args, true)

if correct_title then

return correct_title

end

local article_title = args.page_title_test

if not args.page_title_test then

article_title = mw.title.getCurrentTitle().text

end

-- Remove disambiguation.

local title_parts = get_title_parts(article_title)

-- Check if the article is using a {{Lowercase title}} template.

local lowercase_title = get_lowercase_template_status(page_text, args, article_title, title_parts, true)

if lowercase_title then

return lowercase_title

end

-- Remove nowrap span.

if string.find(args.name, "nowrap") then

local title = frame:expandTemplate{title = "Strip tags", args = {args.name}}

if title == article_title or title == title_parts.title then

return ""

end

return maintenance_categories.non_matching_title.category

end

-- Remove line breaks and additional spaces as a result.

if string.find(args.name, "") then

local title, _ = string.gsub(args.name, "", "")

title, _ = string.gsub(title, " ", " ")

if title == article_title or title == title_parts.title then

return ""

end

return maintenance_categories.non_matching_title.category

end

if args.name == article_title or args.name == title_parts.title then

return maintenance_categories.unnecessary_title_parameter.category

end

-- Article and infobox titles do not match.

return maintenance_categories.non_matching_title.category

end

--- Returns the relevant maintenance categories based on the {{Infobox television}} values validated.

---

--- @param frame table

--- @return string

function p.validate_values(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

local categories = {}

table.insert(categories, is_infobox_title_equal_to_article_title(frame, args))

table.insert(categories, has_display_title(args))

table.insert(categories, are_image_auxiliary_values_used_for_no_image(args))

table.insert(categories, is_image_using_incorrect_syntax(args.image))

table.insert(categories, is_image_size_using_px(args.image_size))

-- table.insert(categories, is_language_linked(args)) -- commenting out for now

table.insert(categories, is_country_name_valid(args.country))

table.insert(categories, has_flag_icon(args))

table.insert(categories, is_credit_used_correctly(args))

table.insert(categories, is_based_on_used_correctly(args.based_on))

table.insert(categories, does_num_episodes_have_extraneous_text(args.num_episodes))

table.insert(categories, are_exclusive_parameter_sets_used(args))

table.insert(categories, is_value_a_url(args))

local release_categories = is_release_information_formatted_correctly(args)

for _, v in ipairs(release_categories) do

table.insert(categories, v)

end

table.insert(categories, is_italic_title_valid_value(args))

return table.concat(categories, "")

end

--- Returns the text used for the |above= field of the infobox.

---

--- Infobox parameters checked:

--- - |name=

---

--- Testing parameters:

--- - |page_test= - a real Wikipedia page to read the content of the page.

--- - |page_title_test= - the title of the page being checked.

---

--- @param frame table

--- @return string

function p.above_title(frame)

local getArgs = require("Module:Arguments").getArgs

local args = getArgs(frame)

local page

if args.page_test then

page = mw.title.new(args.page_test)

else

page = mw.title.getCurrentTitle()

end

local page_text = page:getContent()

local article_title = args.page_title_test

if not args.page_title_test then

article_title = page.text

end

local title_format = "%s"

local correct_title = get_correct_title_value(page_text, args, false)

if correct_title then

return string.format(title_format, correct_title)

end

local title_parts = get_title_parts(article_title)

local lowercase_title = get_lowercase_template_status(page_text, args, article_title, title_parts, false)

if lowercase_title then

return string.format(title_format, lowercase_title)

end

if args.italic_title then

local title_modification = get_display_title_text(page_text, article_title)

if title_modification then

return string.format(title_format, title_modification)

end

end

if args.name then

return string.format(title_format, args.name)

end

return string.format(title_format, title_parts.title)

end

return p

--Commented out code. Might be used in the future.

--- Returns a maintenance category if the values are formatted.

---

--- Most of the infobox values are checked. Not included are:

--- - |caption=

--- - |alt_name=

--- - |native_name=

--- - |based_on=

--- - |inspired_by=

--- - |open_theme=

--- - |end_theme=

---

--- The function currently checks if the following values are present:

--- - '' - italics or bold.

---

--- @param args table

--- @return string

-- local function are_values_incorrect_formatted_with_bold_or_italics(args)

-- ---@type table

-- local ignore_parameters = {

-- caption = true,

-- alt_name = true,

-- native_name = true,

-- based_on = true,

-- inspired_by = true,

-- open_theme = true,

-- end_theme = true,

-- }

-- for key, value in pairs(args) do

-- if not ignore_parameters[key] and string.find(value, "''", 1, true) then

-- add_warning(maintenance_categories.incorrectly_formatted.warning."country name should not be an abbreviation")

-- return string.format(maintenance_categories.incorrectly_formatted.category, key)

-- end

-- end

-- return ""

-- end

--- Returns a maintenance category if:

---- When alt_name= is a list of values, and not all entries are in italics.

---- When alt_name= is a single value and is in italics.

----- This is because the template automatically handles the italics and when also manually added,

----- results in 4 apostrophes which produce a bold title instead.

---

--- Infobox parameters checked:

--- - |alt_name=

---

--- @param alt_name string

--- @return string

-- local function is_alt_name_in_italics(alt_name)

-- if not alt_name then

-- return ""

-- end

-- local detect_singular = require("Module:Detect singular")._main

-- local args = {alt_name, ["no_and"] = "1", ["no_comma"] = "1"}

-- local is_singular = detect_singular(args)

-- if is_singular > 1 then

-- local alt_names = clean_list_syntax(alt_name)

-- alt_names = split(alt_names)

-- for _, name in ipairs(alt_names) do

-- if not string.find(name, "''") then

-- return string.format(maintenance_categories.alt_name.category, "alt_name")

-- end

-- end

-- else

-- if string.find(alt_name, "''") then

-- return string.format(maintenance_categories.alt_name.category, "alt_name")

-- end

-- end

-- return ""

-- end