diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua
index 424eb26d2..4497a42b3 100644
--- a/builtin/common/misc_helpers.lua
+++ b/builtin/common/misc_helpers.lua
@@ -262,6 +262,16 @@ function core.formspec_escape(text)
end
+local hypertext_escapes = {
+ ["\\"] = "\\\\",
+ ["<"] = "\\<",
+ [">"] = "\\>",
+}
+function core.hypertext_escape(text)
+ return text and text:gsub("[\\<>]", hypertext_escapes)
+end
+
+
function core.wrap_text(text, max_length, as_table)
local result = {}
local line = {}
diff --git a/builtin/mainmenu/content/contentdb.lua b/builtin/mainmenu/content/contentdb.lua
index eb06c8b61..145e9c762 100644
--- a/builtin/mainmenu/content/contentdb.lua
+++ b/builtin/mainmenu/content/contentdb.lua
@@ -179,6 +179,22 @@ function contentdb.get_package_by_id(id)
end
+function contentdb.get_package_by_info(author, name)
+ local id = author:lower() .. "/" .. name
+ local package = contentdb.package_by_id[id]
+ if package then
+ return package
+ end
+
+ local name_len = #name
+ if name_len > 5 and name:sub(name_len - 4) == "_game" then
+ id = author:lower() .. "/" .. name:sub(1, name_len - 5)
+ return contentdb.package_by_id[id]
+ end
+ return nil
+end
+
+
local function get_raw_dependencies(package)
if package.type ~= "mod" then
return {}
@@ -374,8 +390,8 @@ local function fetch_pkgs(params)
local aliases = {}
for _, package in pairs(packages) do
- local name_len = #package.name
-- This must match what contentdb.update_paths() does!
+ local name_len = #package.name
package.id = package.author:lower() .. "/"
if package.type == "game" and name_len > 5 and package.name:sub(name_len - 4) == "_game" then
package.id = package.id .. package.name:sub(1, name_len - 5)
@@ -540,3 +556,45 @@ function contentdb.filter_packages(query, by_type)
end
end
end
+
+
+function contentdb.get_full_package_info(package, callback)
+ assert(package)
+
+ local function fetch(params)
+ local version = core.get_version()
+ local base_url = core.settings:get("contentdb_url")
+
+ local languages
+ local current_language = core.get_language()
+ if current_language ~= "" then
+ languages = { current_language, "en;q=0.8" }
+ else
+ languages = { "en" }
+ end
+
+ local url = base_url ..
+ "/api/packages/" .. params.package.url_part .. "/for-client/?" ..
+ "protocol_version=" .. core.get_max_supp_proto() ..
+ "&engine_version=" .. core.urlencode(version.string) ..
+ "&formspec_version=" .. core.urlencode(7) ..
+ "&include_images=false"
+ local http = core.get_http_api()
+ local response = http.fetch_sync({
+ url = url,
+ extra_headers = {
+ "Accept-Language: " .. table.concat(languages, ", ")
+ },
+ })
+ if not response.succeeded then
+ return nil
+ end
+
+ return core.parse_json(response.data)
+ end
+
+ if not core.handle_async(fetch, { package = package }, callback) then
+ core.log("error", "ERROR: async event failed")
+ callback(nil)
+ end
+end
diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua
index 4dafafc45..5c943d0b4 100644
--- a/builtin/mainmenu/content/dlg_contentdb.lua
+++ b/builtin/mainmenu/content/dlg_contentdb.lua
@@ -46,7 +46,7 @@ local filter_types_type = {
}
-local function install_or_update_package(this, package)
+function install_or_update_package(parent, package)
local install_parent
if package.type == "mod" then
install_parent = core.get_modpath()
@@ -66,14 +66,14 @@ local function install_or_update_package(this, package)
local has_hard_deps = contentdb.has_hard_deps(package)
if has_hard_deps then
local dlg = create_install_dialog(package)
- dlg:set_parent(this)
- this:hide()
+ dlg:set_parent(parent)
+ parent:hide()
dlg:show()
elseif has_hard_deps == nil then
local dlg = messagebox("error_checking_deps",
fgettext("Error getting dependencies for package"))
- dlg:set_parent(this)
- this:hide()
+ dlg:set_parent(parent)
+ parent:hide()
dlg:show()
else
contentdb.queue_download(package, package.path and contentdb.REASON_UPDATE or contentdb.REASON_NEW)
@@ -83,13 +83,13 @@ local function install_or_update_package(this, package)
if package.type == "mod" and #pkgmgr.games == 0 then
local dlg = messagebox("install_game",
fgettext("You need to install a game before you can install a mod"))
- dlg:set_parent(this)
- this:hide()
+ dlg:set_parent(parent)
+ parent:hide()
dlg:show()
elseif not package.path and core.is_dir(install_parent .. DIR_DELIM .. package.name) then
local dlg = create_confirm_overwrite(package, on_confirm)
- dlg:set_parent(this)
- this:hide()
+ dlg:set_parent(parent)
+ parent:hide()
dlg:show()
else
on_confirm()
@@ -300,7 +300,7 @@ local function get_formspec(dlgdata)
-- image
formspec[#formspec + 1] = "image[0,0;1.5,1;"
- formspec[#formspec + 1] = core.formspec_escape(get_screenshot(package))
+ formspec[#formspec + 1] = core.formspec_escape(get_screenshot(package, package.thumbnail))
formspec[#formspec + 1] = "]"
-- title
@@ -310,52 +310,17 @@ local function get_formspec(dlgdata)
core.colorize("#BFBFBF", " by " .. package.author))
formspec[#formspec + 1] = "]"
- -- buttons
- local description_width = W - 2.625 - 2 * 0.7 - 2 * 0.15
-
- local second_base = "image_button[-1.55,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
- local third_base = "image_button[-2.4,0;0.7,0.7;" .. core.formspec_escape(defaulttexturedir)
- formspec[#formspec + 1] = "container["
- formspec[#formspec + 1] = W - 0.375*2
- formspec[#formspec + 1] = ",0.1]"
-
- if package.downloading then
- formspec[#formspec + 1] = "animated_image[-1.7,-0.15;1,1;downloading;"
- formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
- formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
- elseif package.queued then
- formspec[#formspec + 1] = second_base
- formspec[#formspec + 1] = "cdb_queued.png;queued;]"
- elseif not package.path then
- local elem_name = "install_" .. i .. ";"
- formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#71aa34]"
- formspec[#formspec + 1] = second_base .. "cdb_add.png;" .. elem_name .. "]"
- formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Install") .. tooltip_colors
- else
- if package.installed_release < package.release then
- -- The install_ action also handles updating
- local elem_name = "install_" .. i .. ";"
- formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#28ccdf]"
- formspec[#formspec + 1] = third_base .. "cdb_update.png;" .. elem_name .. "]"
- formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Update") .. tooltip_colors
-
- description_width = description_width - 0.7 - 0.15
- end
-
- local elem_name = "uninstall_" .. i .. ";"
- formspec[#formspec + 1] = "style[" .. elem_name .. "bgcolor=#a93b3b]"
- formspec[#formspec + 1] = second_base .. "cdb_clear.png;" .. elem_name .. "]"
- formspec[#formspec + 1] = "tooltip[" .. elem_name .. fgettext("Uninstall") .. tooltip_colors
- end
-
- local web_elem_name = "view_" .. i .. ";"
- formspec[#formspec + 1] = "image_button[-0.7,0;0.7,0.7;" ..
- core.formspec_escape(defaulttexturedir) .. "cdb_viewonline.png;" .. web_elem_name .. "]"
- formspec[#formspec + 1] = "tooltip[" .. web_elem_name ..
- fgettext("View more information in a web browser") .. tooltip_colors
- formspec[#formspec + 1] = "container_end[]"
+ -- button
+ formspec[#formspec + 1] = "button["
+ formspec[#formspec + 1] = W-0.375*2-2
+ formspec[#formspec + 1] = ",0.1;2,0.7;view_"
+ formspec[#formspec + 1] = i
+ formspec[#formspec + 1] = ";"
+ formspec[#formspec + 1] = fgettext("View")
+ formspec[#formspec + 1] = "]"
-- description
+ local description_width = W - 2.625 - 2 * 0.7 - 2 * 0.15
formspec[#formspec + 1] = "textarea[1.855,0.3;"
formspec[#formspec + 1] = tostring(description_width)
formspec[#formspec + 1] = ",0.8;;;"
@@ -437,32 +402,20 @@ local function handle_submit(this, fields)
return true
end
+ local num_per_page = this.data.num_per_page
local start_idx = (cur_page - 1) * num_per_page + 1
assert(start_idx ~= nil)
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
local package = contentdb.packages[i]
assert(package)
- if fields["install_" .. i] then
- install_or_update_package(this, package)
- return true
- end
-
- if fields["uninstall_" .. i] then
- local dlg = create_delete_content_dlg(package)
+ if fields["view_" .. i] then
+ local dlg = create_package_dialog(package)
dlg:set_parent(this)
this:hide()
dlg:show()
return true
end
-
- if fields["view_" .. i] then
- local url = ("%s/packages/%s?protocol_version=%d"):format(
- core.settings:get("contentdb_url"), package.url_part,
- core.get_max_supp_proto())
- core.open_url(url)
- return true
- end
end
return false
diff --git a/builtin/mainmenu/content/dlg_package.lua b/builtin/mainmenu/content/dlg_package.lua
new file mode 100644
index 000000000..126b3d8ad
--- /dev/null
+++ b/builtin/mainmenu/content/dlg_package.lua
@@ -0,0 +1,351 @@
+--Minetest
+--Copyright (C) 2018-24 rubenwardy
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+local function get_info_formspec(size, text)
+ return table.concat({
+ "formspec_version[6]",
+ "size[", size.x, ",", size.y, "]",
+ "padding[-0.01,-0.01]",
+
+ "label[4,4.35;", text, "]",
+ "container[0,", size.y - 0.8 - 0.375, "]",
+ "button[0.375,0;2,0.8;back;", fgettext("Back"), "]",
+ "container_end[]",
+ })
+end
+
+
+--- Creates a scrollbaroptions for a scroll_container
+--
+-- @param visible_l the length of the scroll_container and scrollbar
+-- @param total_l length of the scrollable area
+-- @param scroll_factor as passed to scroll_container
+local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor)
+ assert(total_l >= visible_l)
+ local max = total_l - visible_l
+ local thumb_size = (visible_l / total_l) * max
+ return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor)
+end
+
+
+local function get_formspec(data)
+ local window = core.get_window_info()
+ local size = { x = window.max_formspec_size.x, y = window.max_formspec_size.y }
+
+ if not data.info then
+ if not data.loading and not data.loading_error then
+ data.loading = true
+
+ contentdb.get_full_package_info(data.package, function(info)
+ data.loading = false
+
+ if info == nil then
+ data.loading_error = true
+ ui.update()
+ elseif data.package.name == info.name then
+ data.info = info
+ ui.update()
+ end
+ end)
+ end
+
+ if data.loading_error then
+ return get_info_formspec(size, fgettext("No packages could be retrieved"))
+ else
+ return get_info_formspec(size, fgettext("Loading..."))
+ end
+ else
+ -- Check installation status
+ contentdb.update_paths()
+
+ local info = data.info
+
+ local info_line =
+ fgettext("by $1 — $2 downloads — +$3 / $4 / -$5",
+ info.author, info.downloads,
+ info.reviews.positive, info.reviews.neutral, info.reviews.negative)
+
+ local bottom_buttons_y = size.y - 0.8 - 0.375
+
+ local formspec = {
+ "formspec_version[7]",
+ "size[", size.x, ",", size.y, "]",
+ "padding[-0.01,-0.01]",
+ "bgcolor[#0000]",
+ "box[0,0;", size.x, ",", size.y, ";#0000008C]",
+
+ "button[0.375,", bottom_buttons_y, ";2,0.8;back;", fgettext("Back"), "]",
+ "button[", size.x - 3.375, ",", bottom_buttons_y, ";3,0.8;open_contentdb;", fgettext("ContentDB page"), "]",
+
+ "style_type[label;font_size=+24;font=bold]",
+ "label[0.375,0.7;", core.formspec_escape(info.title), "]",
+ "style_type[label;font_size=;font=]",
+
+ "label[0.375,1.4;", core.formspec_escape(info_line), "]",
+ }
+
+ local x = size.x - 3.375
+ local function add_link_button(label, name)
+ if info[name] then
+ x = x - 3.25
+ table.insert_all(formspec, {
+ "button[", x, ",", bottom_buttons_y, ";3,0.8;open_", name, ";", label, "]",
+ })
+ end
+ end
+ add_link_button(fgettext("Translate"), "translation_url")
+ add_link_button(fgettext("Issue Tracker"), "issue_tracker")
+ add_link_button(fgettext("Forums"), "forums")
+ add_link_button(fgettext("Source"), "repo")
+ add_link_button(fgettext("Website"), "website")
+
+ table.insert_all(formspec, {
+ "container[", size.x - 6.375, ",0.375]"
+ })
+
+ local left_button_rect = "0,0;2.875,1"
+ local right_button_rect = "3.125,0;2.875,1"
+ if data.package.downloading then
+ formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
+ formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
+ formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
+ elseif data.package.queued then
+ formspec[#formspec + 1] = "style[queued;border=false]"
+ formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
+ formspec[#formspec + 1] = "cdb_queued.png;queued;]"
+ elseif not data.package.path then
+ formspec[#formspec + 1] = "style[install;bgcolor=green]"
+ formspec[#formspec + 1] = "button["
+ formspec[#formspec + 1] = right_button_rect
+ formspec[#formspec + 1] =";install;"
+ formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
+ formspec[#formspec + 1] = "]"
+ else
+ if data.package.installed_release < data.package.release then
+ -- The install_ action also handles updating
+ formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
+ formspec[#formspec + 1] = "button["
+ formspec[#formspec + 1] = left_button_rect
+ formspec[#formspec + 1] = ";install;"
+ formspec[#formspec + 1] = fgettext("Update")
+ formspec[#formspec + 1] = "]"
+ end
+
+ formspec[#formspec + 1] = "style[uninstall;bgcolor=#a93b3b]"
+ formspec[#formspec + 1] = "button["
+ formspec[#formspec + 1] = right_button_rect
+ formspec[#formspec + 1] = ";uninstall;"
+ formspec[#formspec + 1] = fgettext("Uninstall")
+ formspec[#formspec + 1] = "]"
+ end
+
+ local current_tab = data.current_tab or 1
+ local tab_titles = {
+ fgettext("Description"),
+ fgettext("Information"),
+ }
+
+ local tab_body_height = bottom_buttons_y - 2.8
+
+ table.insert_all(formspec, {
+ "container_end[]",
+
+ "tabheader[0.375,2.55;", size.x - 0.375*2, ",0.8;tabs;",
+ table.concat(tab_titles, ","), ";", current_tab, ";true;true]",
+
+ "container[0,2.8]",
+ })
+
+ if current_tab == 1 then
+ -- Screenshots and description
+ local hypertext = info.long_description.head ..
+ "" .. core.hypertext_escape(info.short_description) .. "\n\n" ..
+ info.long_description.body
+
+ hypertext = hypertext:gsub("
size.x
+ local hypertext_y = (#info.screenshots > 0 and 2.25 or 0) + (needs_scrollbar and 0.5 or 0)
+
+ table.insert_all(formspec, {
+ "hypertext[0.375,", hypertext_y, ";",
+ size.x - 2*0.375, ",",
+ tab_body_height - hypertext_y - 0.375,
+ ";desc;", core.formspec_escape(hypertext), "]",
+
+ "scroll_container[0,0;", size.x, ",2.25;images_sb;horizontal;", scroll_factor, "]",
+ })
+
+ for i, ss in ipairs(info.screenshots) do
+ local path = get_screenshot(data.package, ss.url, 2)
+ table.insert_all(formspec, {
+ "image_button[", (i-1)*3.25 + 0.375, ",0;3,2;",
+ core.formspec_escape(path), ";ss_", i, ";;false;false]",
+ })
+ end
+
+ formspec[#formspec + 1] = "scroll_container_end[]"
+
+ if needs_scrollbar then
+ table.insert_all(formspec, {
+ make_scrollbaroptions_for_scroll_container(size.x, screenshots_w, scroll_factor),
+ "scrollbar[0,2.25;", size.x, ",0.25;horizontal;images_sb;0]",
+ })
+ end
+ elseif current_tab == 2 then
+ local hypertext = info.info_hypertext.head .. info.info_hypertext.body
+
+ table.insert_all(formspec, {
+ "hypertext[0.375,0;", size.x - 2*0.375, ",", tab_body_height - 0.375,
+ ";info;", core.formspec_escape(hypertext), "]",
+ })
+ else
+ error("Unknown tab " .. current_tab)
+ end
+
+ formspec[#formspec + 1] = "container_end[]"
+
+ return table.concat(formspec)
+ end
+end
+
+
+local function handle_hypertext_event(this, event, hypertext_object)
+ if not (event and event:sub(1, 7) == "action:") then
+ return
+ end
+
+ -- TODO: escape base_url
+ local base_url = core.settings:get("contentdb_url")
+ for key, url in pairs(hypertext_object.links) do
+ if event == "action:" .. key then
+ local author, name = url:match("^" .. base_url .. "/?packages/([A-Za-z0-9 _-]+)/([a-z0-9_]+)/?$")
+ if author and name then
+ local package2 = contentdb.get_package_by_info(author, name)
+ if package2 then
+ local dlg = create_package_dialog(package2)
+ dlg:set_parent(this)
+ this:hide()
+ dlg:show()
+ return true
+ end
+ end
+
+ core.open_url_dialog(url)
+ return true
+ end
+ end
+end
+
+
+local function handle_submit(this, fields)
+ local info = this.data.info
+ local package = this.data.package
+
+ if fields.back then
+ this:delete()
+ return true
+ end
+
+ if not info then
+ return false
+ end
+
+ if fields.open_contentdb then
+ local url = ("%s/packages/%s/?protocol_version=%d"):format(
+ core.settings:get("contentdb_url"), package.url_part,
+ core.get_max_supp_proto())
+ core.open_url(url)
+ return true
+ end
+
+ if fields.open_translation_url then
+ core.open_url_dialog(info.translation_url)
+ return true
+ end
+
+ if fields.open_issue_tracker then
+ core.open_url_dialog(info.issue_tracker)
+ return true
+ end
+
+ if fields.open_forums then
+ core.open_url("https://forum.minetest.net/viewtopic.php?t=" .. info.forums)
+ return true
+ end
+
+ if fields.open_repo then
+ core.open_url_dialog(info.repo)
+ return true
+ end
+
+ if fields.open_website then
+ core.open_url_dialog(info.website)
+ return true
+ end
+
+ if fields.install then
+ install_or_update_package(this, package)
+ return true
+ end
+
+ if fields.uninstall then
+ local dlg = create_delete_content_dlg(package)
+ dlg:set_parent(this)
+ this:hide()
+ dlg:show()
+ return true
+ end
+
+ if fields.tabs then
+ this.data.current_tab = tonumber(fields.tabs)
+ return true
+ end
+
+ for i, ss in ipairs(info.screenshots) do
+ if fields["ss_" .. i] then
+ core.open_url(ss.url)
+ return true
+ end
+ end
+
+ if handle_hypertext_event(this, fields.desc, info.long_description) or
+ handle_hypertext_event(this, fields.info, info.info_hypertext) then
+ return true
+ end
+end
+
+
+function create_package_dialog(package)
+ assert(package)
+
+ local dlg = dialog_create("package_dialog_" .. package.id,
+ get_formspec,
+ handle_submit)
+ local data = dlg.data
+
+ data.package = package
+ data.info = nil
+ data.loading = false
+ data.loading_error = nil
+ data.current_tab = 1
+ return dlg
+end
diff --git a/builtin/mainmenu/content/init.lua b/builtin/mainmenu/content/init.lua
index e66828089..00eac681b 100644
--- a/builtin/mainmenu/content/init.lua
+++ b/builtin/mainmenu/content/init.lua
@@ -23,4 +23,5 @@ dofile(path .. DIR_DELIM .. "update_detector.lua")
dofile(path .. DIR_DELIM .. "screenshots.lua")
dofile(path .. DIR_DELIM .. "dlg_install.lua")
dofile(path .. DIR_DELIM .. "dlg_overwrite.lua")
+dofile(path .. DIR_DELIM .. "dlg_package.lua")
dofile(path .. DIR_DELIM .. "dlg_contentdb.lua")
diff --git a/builtin/mainmenu/content/screenshots.lua b/builtin/mainmenu/content/screenshots.lua
index 98c6658b9..b1321af16 100644
--- a/builtin/mainmenu/content/screenshots.lua
+++ b/builtin/mainmenu/content/screenshots.lua
@@ -23,23 +23,43 @@ local screenshot_downloading = {}
local screenshot_downloaded = {}
+local function get_filename(path)
+ local parts = path:split("/")
+ return parts[#parts]
+end
+
+
local function get_file_extension(path)
local parts = path:split(".")
return parts[#parts]
end
-function get_screenshot(package)
- if not package.thumbnail then
+function get_screenshot(package, screenshot_url, level)
+ if not screenshot_url then
return defaulttexturedir .. "no_screenshot.png"
- elseif screenshot_downloading[package.thumbnail] then
+ end
+
+ -- Minetest only supports png and jpg
+ local ext = get_file_extension(screenshot_url)
+ if ext ~= "png" and ext ~= "jpg" then
+ screenshot_url = screenshot_url:sub(0, -#ext - 1) .. "png"
+ level = level or 4
+ end
+
+ -- Set thumbnail level
+ if level then
+ screenshot_url = screenshot_url:gsub("/thumbnails/[0-9]+/", "/thumbnails/" .. level .. "/")
+ screenshot_url = screenshot_url:gsub("/uploads/", "/thumbnails/" .. level .. "/")
+ end
+
+ if screenshot_downloading[screenshot_url] then
return defaulttexturedir .. "loading_screenshot.png"
end
- -- Get tmp screenshot path
- local ext = get_file_extension(package.thumbnail)
local filepath = screenshot_dir .. DIR_DELIM ..
- ("%s-%s-%s.%s"):format(package.type, package.author, package.name, ext)
+ ("%s-%s-%s-l%d-%s"):format(package.type, package.author, package.name,
+ level or 1, get_filename(screenshot_url))
-- Return if already downloaded
local file = io.open(filepath, "r")
@@ -49,7 +69,7 @@ function get_screenshot(package)
end
-- Show error if we've failed to download before
- if screenshot_downloaded[package.thumbnail] then
+ if screenshot_downloaded[screenshot_url] then
return defaulttexturedir .. "error_screenshot.png"
end
@@ -59,16 +79,16 @@ function get_screenshot(package)
return core.download_file(params.url, params.dest)
end
local function callback(success)
- screenshot_downloading[package.thumbnail] = nil
- screenshot_downloaded[package.thumbnail] = true
+ screenshot_downloading[screenshot_url] = nil
+ screenshot_downloaded[screenshot_url] = true
if not success then
core.log("warning", "Screenshot download failed for some reason")
end
ui.update()
end
if core.handle_async(download_screenshot,
- { dest = filepath, url = package.thumbnail }, callback) then
- screenshot_downloading[package.thumbnail] = true
+ { dest = filepath, url = screenshot_url }, callback) then
+ screenshot_downloading[screenshot_url] = true
else
core.log("error", "ERROR: async event failed")
return defaulttexturedir .. "error_screenshot.png"
diff --git a/doc/lua_api.md b/doc/lua_api.md
index b08f7356d..fe4deb52e 100644
--- a/doc/lua_api.md
+++ b/doc/lua_api.md
@@ -6443,6 +6443,9 @@ Formspec
* `minetest.formspec_escape(string)`: returns a string
* escapes the characters "[", "]", "\", "," and ";", which cannot be used
in formspecs.
+* `minetest.hypertext_escape(string)`: returns a string
+ * escapes the charecters "\", "<", and ">" to show text in a hypertext element.
+ * not safe for use with tag attributes.
* `minetest.explode_table_event(string)`: returns a table
* returns e.g. `{type="CHG", row=1, column=2}`
* `type` is one of:
diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md
index cb1f07a90..c7c49aea8 100644
--- a/doc/menu_lua_api.md
+++ b/doc/menu_lua_api.md
@@ -47,7 +47,10 @@ Functions
* returns the maximum supported network protocol version
* `core.open_url(url)`
* opens the URL in a web browser, returns false on failure.
- * Must begin with http:// or https://
+ * `url` must begin with http:// or https://
+* `core.open_url_dialog(url)`
+ * shows a dialog to allow the user to choose whether to open a URL.
+ * `url` must begin with http:// or https://
* `core.open_dir(path)`
* opens the path in the system file browser/explorer, returns false on failure.
* Must be an existing directory.
diff --git a/src/script/lua_api/l_mainmenu.cpp b/src/script/lua_api/l_mainmenu.cpp
index a5913e807..75f8592ab 100644
--- a/src/script/lua_api/l_mainmenu.cpp
+++ b/src/script/lua_api/l_mainmenu.cpp
@@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "content/mod_configuration.h"
#include "threading/mutex_auto_lock.h"
#include "common/c_converter.h"
+#include "gui/guiOpenURL.h"
/******************************************************************************/
std::string ModApiMainMenu::getTextData(lua_State *L, const std::string &name)
@@ -1038,6 +1039,22 @@ int ModApiMainMenu::l_open_url(lua_State *L)
return 1;
}
+/******************************************************************************/
+int ModApiMainMenu::l_open_url_dialog(lua_State *L)
+{
+ GUIEngine* engine = getGuiEngine(L);
+ sanity_check(engine != NULL);
+
+ std::string url = luaL_checkstring(L, 1);
+
+ GUIOpenURLMenu* openURLMenu =
+ new GUIOpenURLMenu(engine->m_rendering_engine->get_gui_env(),
+ engine->m_parent, -1, engine->m_menumanager,
+ engine->m_texture_source.get(), url);
+ openURLMenu->drop();
+ return 1;
+}
+
/******************************************************************************/
int ModApiMainMenu::l_open_dir(lua_State *L)
{
@@ -1129,6 +1146,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
API_FCT(get_min_supp_proto);
API_FCT(get_max_supp_proto);
API_FCT(open_url);
+ API_FCT(open_url_dialog);
API_FCT(open_dir);
API_FCT(share_file);
API_FCT(do_async_callback);
diff --git a/src/script/lua_api/l_mainmenu.h b/src/script/lua_api/l_mainmenu.h
index 5535d2170..e2dfbd3f8 100644
--- a/src/script/lua_api/l_mainmenu.h
+++ b/src/script/lua_api/l_mainmenu.h
@@ -162,6 +162,8 @@ class ModApiMainMenu: public ModApiBase
// other
static int l_open_url(lua_State *L);
+ static int l_open_url_dialog(lua_State *L);
+
static int l_open_dir(lua_State *L);
static int l_share_file(lua_State *L);