local MODNAME = minetest.get_current_modname() local WORLDPATH = minetest.get_worldpath() local MODPATH = minetest.get_modpath (MODNAME) local TEXTDIR = WORLDPATH .. "/wikitext" local SNDDIR = MODPATH .. "/sounds" strfile = {} function strfile.open(s) return { _buf = s, _pos = 1, _readline = function(self) if self._pos == nil then return nil end local nl = self._buf:find("\n", self._pos, true) local line if nl then line = self._buf:sub(self._pos, nl - 1) nl = nl + 1 else line = self._buf:sub(self._pos) end self._pos = nl return line end, lines = function(self) return self._readline, self, true end, close = function(self) end, } end wikilib.paths = {} wikilib.paths.root = TEXTDIR wikilib.paths.pages = TEXTDIR.."/pages" wikilib.paths.plugins = TEXTDIR.."/plugins" wikilib.paths.users = TEXTDIR.."/users" local WIKI_FORMNAME = "wiki:wiki" os.mkdir(TEXTDIR) os.mkdir(wikilib.paths.pages) os.mkdir(wikilib.paths.plugins) os.mkdir(wikilib.paths.users) local function name_to_filename(name) name = name:gsub("[^A-Za-z0-9-]", function(c) if c == " " then return "_" else return ("%%%02X"):format(c:byte(1)) end end) return name:lower() end wikilib.name_to_filename = name_to_filename -- =================================================================== local function stopwiki (plname) if minetest.setting_getbool ("enable_damage") then local player = minetest.get_player_by_name (plname) if player then local hp = player:get_hp() minetest.log ("action", "[wiki] hp for " .. plname .. "=" .. hp) if hp < 1 then minetest.log ("action", "[wiki] disabled wiki for " .. plname .. " due to death") return true end end end return false end -- =================================================================== local function get_page_path (name, player) --> path, is_file, allow_save local allow_save = minetest.check_player_privs (player, { wiki=true }) local path if name:sub(1, 1) == "." then local text = wikilib.internal_pages[name] or wikilib.internal_pages[".NotFound_Internal"] if type(text) == "function" then text = text(player) end return text, false, false elseif name:sub(1, 1) == ":" then if name:match("^:[0-9]?$") then local n = tonumber(name:sub(2,2)) or 0 path = "users/"..player.."/page"..n os.mkdir(wikilib.paths.users.."/"..player) elseif name == ":profile" then path = "users/"..player.."/profile" os.mkdir(wikilib.paths.users.."/"..player) elseif name:match("^:.-:[0-9]$") then local user, n = name:match("^:(.-):([0-9])$") if user:find("..[/\\]") then return wikilib.internal_pages[".BadPageName"], false, false end if (n == "0") and (not minetest.check_player_privs(player, {wiki_admin=true})) then return wikilib.internal_pages[".Forbidden"], false, false end path = "users/"..user.."/page"..n os.mkdir(TEXTDIR.."/users/"..user) allow_save = false elseif name:match("^:.-:profile$") then local user = name:match("^:(.-):.*$") if user:find("..[/\\]") then return wikilib.internal_pages[".BadPageName"], false, false end path = "users/"..user.."/profile" os.mkdir(TEXTDIR.."/users/"..user) allow_save = false else return wikilib.internal_pages[".BadPageName"], false, false end else path = "pages/"..name_to_filename(name) end return TEXTDIR.."/"..path, true, allow_save end local function find_links(lines) --> links local links = { } local links_n = 0 for _,line in ipairs(lines) do for link in line:gmatch("%[(.-)%]") do links_n = links_n + 1 links[links_n] = link end end return links end local function load_page (name, player) --> text, links, allow_save local text, allow_save = wikilib.plugin_handle_load(name, player) if text then return text, find_links(text:split("\n")), allow_save end local path, is_file, allow_save = get_page_path(name, player) local f if is_file then f = io.open(path) if not f then f = strfile.open(wikilib.internal_pages[".NotFound"]) end else f = strfile.open(path) end local lines = { } local lines_n = 0 for line in f:lines() do lines_n = lines_n + 1 lines[lines_n] = line end f:close() local text = table.concat(lines, "\n") local links = find_links(lines) return text, links, allow_save end local function save_page (name, player, text) local ok = wikilib.plugin_handle_save(name, player, text) if ok then return ok end local path, is_file, allow_save = get_page_path(name, player) if (not is_file) or (not allow_save) then return end local f = io.open(path, "w") if not f then return end f:write(text) f:close() end local esc = minetest.formspec_escape function wikilib.get_wiki_page_formspec (player, name, w, h) if name == "" then name = "Main" end w = w or 12 h = h or 10 local text, links, allow_save = load_page (name, player) local buttons = "" local bx = 0 local by = 7.5 local ii = 1 local btype for i, link in ipairs(links) do if (ii > 5) then bx = 0 by = by + 1.0 ii = 1 end if (link == "Close") or (link == "Quit") then btype = "button_exit" else btype = "button" end link = esc(link) buttons = buttons..((btype .. "[%f,%f;2.4,0.3;page_%s;%s]"):format(bx, by, link, link)) bx = bx + 2.4 ii = ii + 1 end if (ii > 5) then bx = 0 by = by + 1.0 ii = 1 end if allow_save then buttons = buttons..(("button[%f,%f;2.4,0.3;%s;%s]"):format(bx, by, "save", "Save")) bx = bx + 2.4 ii = ii + 1 end return ("size["..w..","..h.."]" .. "field[0,1;11,1;page;Page;"..esc(name).."]" .. "button[11,1;1,0.5;go;-]" -- .. "bgcolor[#080808BB; false]" .. "bgcolor[#111188; false]" .. "textarea[0,2;12,6;text;"..esc(name)..";"..esc(text).."]" .. buttons ) end function wikilib.show_wiki_page (plname, name) if stopwiki (plname) then return end if (name ~= "Close") and (name ~= "Quit") then local fs = wikilib.get_wiki_page_formspec (plname, name) minetest.show_formspec (plname, WIKI_FORMNAME, fs) end end minetest.register_node("wiki:wiki", { description = "Wiki", tiles = { "default_wood.png", "default_wood.png", "default_bookshelf.png" }, groups = { choppy=3, oddly_breakable_by_hand=2, flammable=3 }, sounds = default.node_sound_wood_defaults(), on_construct = function(pos) local meta = minetest.get_meta(pos) meta:set_string("infotext", "Wiki") end, on_rightclick = function(pos, node, clicker, itemstack) if clicker then wikilib.show_wiki_page (clicker:get_player_name(), "Main") end end, }) minetest.register_privilege("wiki", { description = "Allow editing wiki pages in the global space", give_to_singleplayer = false, }) minetest.register_privilege("wiki_admin", { description = "Allow editing wiki pages in any space", give_to_singleplayer = false, }) local BS = "default:bookshelf" local BSL = { BS, BS, BS } -- minetest.register_craft({ -- output = "wiki:wiki", -- recipe = { BSL, BSL, BSL }, -- }) function wikilib.handle_formspec(player, formname, fields) if (not formname) or (formname ~= WIKI_FORMNAME) then return end local plname = player:get_player_name() if fields.save then local r = save_page(fields.page, plname, fields.text) if type(r) == "string" then wikilib.show_wiki_page (plname, r) else wikilib.show_wiki_page (plname, fields.page) end return true elseif fields.go and minetest.check_player_privs(player:get_player_name(),{wiki_admin=true}) then wikilib.show_wiki_page (plname, fields.page) return true elseif fields.go and minetest.check_player_privs(player:get_player_name(),{wiki_admin=false}) then minetest.chat_send_player(plname, "This button is currently restricted, sorry for any inconvenience caused!") return true else for k in pairs(fields) do if type(k) == "string" then local name = k:match("^page_(.*)") if name then wikilib.show_wiki_page (plname, name) return true end end end end end minetest.register_on_player_receive_fields(function(player, formname, fields) wikilib.handle_formspec(player, formname, fields) end) -- =================================================================== minetest.register_chatcommand ("english",{ params = "", description="Information in English", func = function (name,params) wikilib.show_wiki_page (name, "info") end, }) minetest.register_chatcommand ("info",{ params = "", description="Information in English", func = function (name,params) wikilib.show_wiki_page (name, "info") end, }) -- =================================================================== minetest.register_chatcommand ("wiki",{ params = "", description="Shows the wiki (same as /main)", func = function (name,params) wikilib.show_wiki_page (name, "Main") end, }) minetest.register_chatcommand ("main",{ params = "", description="Shows the wiki (same as /wiki)", func = function (name,params) wikilib.show_wiki_page (name, "Main") end, }) if ocutil.mod_missing ("newplayer") then minetest.register_chatcommand ("rules",{ params = "", description="Shows the wiki rules page", func = function (name,params) wikilib.show_wiki_page (name, "Rules") end, }) end minetest.register_chatcommand ("news",{ params = "", description="Shows the news page", func = function (xx, params) wikilib.show_wiki_page (xx, "News") end, }) -- =================================================================== minetest.register_chatcommand ("francais",{ params = "", description="Shows the French top page", func = function (xx, params) wikilib.show_wiki_page (xx, "French") end, }) minetest.register_chatcommand ("france",{ params = "", description="Shows the French top page", func = function (xx, params) wikilib.show_wiki_page (xx, "French") end, }) minetest.register_chatcommand ("french",{ params = "", description="Shows the French top page", func = function (xx, params) wikilib.show_wiki_page (xx, "French") end, }) minetest.register_chatcommand ("commands",{ params = "", description="Shows the /commands page", func = function (xx, params) wikilib.show_wiki_page (xx, "Commands") end, }) -- =================================================================== function ogg_exists (name) local f = io.open (SNDDIR .. "/" .. name .. ".ogg", "r") if f ~= nil then io.close (f) return true else return false end end function wikilib.quack_quack (plname, pos, name) if stopwiki (plname) then return end if ogg_exists (plname) then minetest.sound_play (plname, { pos=pos, gain = 2.0, max_hear_distance = 5000 }) end if (name ~= "Close") and (name ~= "Quit") then local fs = wikilib.get_wiki_page_formspec (plname, name) minetest.show_formspec (plname, WIKI_FORMNAME, fs) end end minetest.register_on_joinplayer(function (player) local name = player:get_player_name() if player:get_hp() <= 0 then return end if ocutil.mod_missing ("newplayer") or minetest.check_player_privs (name, { interact=true }) then minetest.log ("action", "showing wiki to " .. name) minetest.after (2, wikilib.quack_quack, player:get_player_name(), player:getpos(), "main") else minetest.log ("action", "disabled wiki for " .. name .. " due to initial checks") end end)