--[[ userbouquets - Manage user defined bouquets Copyright (C) 2016 Sven Hoefer License: WTFPLv2 ]] -- ---------------------------------------------------------------------------- version = 0.12 function script_path() local str = debug.getinfo(2, "S").source:sub(2) return str:match("(.*/)") end function capitalize(s) return s:gsub("^%l", string.upper) end function add_slash(dir) -- add trailing slash if needed if string.sub(dir, -1) ~= "/" then dir = dir .. "/" end return dir end function remove_slash(dir) -- remove trailing slash if needed if string.sub(dir, -1) == "/" then dir = dir:sub(1, -2) end return dir end function showhint(caption, text, icon, timeout) local caption = caption or "Info" local text = text or "" local icon = icon or "information" local h = hintbox.new{caption=caption, text=text, icon=icon} h:paint() delay(5) h:hide() end function delay(timeout) local timeout = timeout or 2 local i = 0 repeat i = i + 1 msg, data = n:GetInput(500) until msg == RC.ok or msg == RC.home or i == (timeout * 2) end function check_content(dir) dir = add_slash(dir) if not fh:exist(dir .. bouquets_xml, "f") or not fh:exist(dir .. ubouquets_xml, "f") then return false end return true end function check_owner(dir) dir = add_slash(dir) if dir == owner_dir then return true end return false end function check_active(dir) dir = add_slash(dir) local bouquets_target = fh:readlink(zapit_dir .. bouquets_xml) if bouquets_target == dir .. bouquets_xml then return true end return false end function get_active_icon(dir) if check_active(dir) then return "checkmark" end return "" end function get_username(dir) dir = remove_slash(dir) local name = string.match(dir, "[^/]+$") if name == "owner" then name = locale[lang].owner end return capitalize(name:gsub("_", " ")) end function activate(dir) dir = add_slash(dir) if check_active(dir) then showhint(get_username(dir), locale[lang].already_active) return end if not check_content(dir) then showhint(get_username(dir), locale[lang].content_failed) return end if get_pin(dir) then fh:ln(dir .. bouquets_xml, zapit_dir .. bouquets_xml, "sf") fh:ln(dir .. ubouquets_xml, zapit_dir .. ubouquets_xml, "sf") os.execute("pzapit -c") end return MENU_RETURN.EXIT -- force rebuild menu end function remove(dir) dir = add_slash(dir) if check_owner(dir) then showhint(get_username(dir), locale[lang].cant_remove_owner) return end if check_active(dir) then showhint(get_username(dir), locale[lang].cant_remove_active) return end if get_pin(owner_dir) then fh:rmdir(dir) showhint(get_username(dir), locale[lang].removed) end end function get_pin(dir) local pin = read_pin(dir) if pin == "0000" or pin == "" or pin == nil then return true end local input = input_pin(dir) if input == pin then return true end showhint(locale[lang].panic, locale[lang].pin_wrong) return false end function set_pin(dir) dir = add_slash(dir) local input = input_pin(dir, "new") --[[ TODO: check input ]] local f = io.open(dir .. passwd, "w") f:write(input .. "\n") f:close() end function read_pin(dir) dir = add_slash(dir) local f = io.open(dir .. passwd, "rb") local pin = f:read() f:close() --[[ TODO: check pin ]] return pin end function input_pin(dir, new) dir = remove_slash(dir) local heading = get_username(dir) .. " - " if new == "new" then heading = heading .. locale[lang].pin_new else heading = heading .. locale[lang].pin end local input = stringinput.exec { caption=heading, value="", valid_chars="0123456789", pin=1, size=4 } return input end function pin(dir) --[[ if check_active(dir) then showhint(get_username(dir), locale[lang].cant_change_pin) return end ]] if get_pin(dir) then set_pin(dir) showhint(get_username(dir), locale[lang].pin_changed) end end function create(id, value) local user = value user = add_slash(user) fh:mkdir(users_dir .. user) if check_content(owner_dir) then fh:cp(owner_dir .. bouquets_xml, users_dir .. user, "a") fh:cp(owner_dir .. ubouquets_xml, users_dir .. user, "a") end fh:touch(users_dir .. user .. passwd) user_new = nil return MENU_RETURN.EXIT -- force rebuild menu end function reset(id, value) if not check_content(owner_dir) then showhint(locale[lang].panic, locale[lang].cant_reset_plugin) return end local res = messagebox.exec{title=locale[lang].reset, text=locale[lang].reset_confirm, buttons={ "yes", "no" } } if res == "yes" then if not get_pin(owner_dir) then return end os.remove(zapit_dir .. bouquets_xml) fh:cp(owner_dir .. bouquets_xml, zapit_dir .. bouquets_xml, "f") os.remove(zapit_dir .. ubouquets_xml) fh:cp(owner_dir .. ubouquets_xml, zapit_dir .. ubouquets_xml, "f") fh:rmdir(data_dir) repaint_menu = false return MENU_RETURN.EXIT_ALL end end function info() showhint(locale[lang].caption, locale[lang].info) end -- ---------------------------------------------------------------------------- n = neutrino() m = nil -- the menu fh = filehelpers.new() data_dir = "/var/tuxbox/plugins/userbouquets/" owner_dir = data_dir .. "owner/" users_dir = data_dir .. "users/" user_new = nil zapit_dir = "/var/tuxbox/config/zapit/" bouquets_xml = "bouquets.xml" ubouquets_xml = "ubouquets.xml" passwd = ".passwd" locale = {} locale["deutsch"] = { activate = "Aktivieren", active = "aktiv", already_active = "Benutzer ist bereits aktiviert.", apiversion_failed = "Ihre Lua-API ist zu alt. Bitte Neutrino aktualisieren.", back = "Zurück", back_hint = "Zurück zum vorherigen Menü.", cant_change_pin = "PIN kann beim aktiven Benutzer nicht geändert werden", cant_remove_active = "Der aktive Benutzer kann nicht entfernt werden.", cant_remove_owner = "Der Eigentümer darf nicht entfernt werden.", cant_reset_plugin = "Plugin kann nicht zurückgesetzt werden", caption = "Benutzer-Bouquets" .. " v" .. version, content_failed = "Fehler! Verzeichnisinhalt nicht korrekt.", create = "Neuen Benutzer anlegen", create_hint = "Erstellt einen neuen Benutzer basierend auf den Eigentümer-Daten", info = "Ben Uwe lebt!", no_users = "Keine Benutzer angelegt", owner = "Eigentümer", panic = "Panik!", pin = "PIN", pin_change = "PIN ändern", pin_changed = "PIN geändert", pin_new = "Neue PIN", pin_wrong = "PIN falsch", remove = "Entfernen", removed = "Benutzer wurde entfernt.", reset = "Plugin zurücksetzen", reset_confirm = "Es werden alle Benutzer-Bouquets und Plugin-Daten gelöscht.\n \nFortfahren?", reset_hint = "Entferne Benutzer-Bouquets und Plugin-Daten", selection = "Aktivieren, entfernen oder PIN ändern?", selection_hint = "Aktion für den Benutzer wählen", start_confirm = "Dieses Plugin erlaubt es, die Senderlisten bouquets.xml und\nubouquets.xml je nach Benutzer einzustellen.\nSie können dann unabhängig voneinander verwaltet werden.\n \nLöschen Sie das Plugin nicht, bevor sie es nicht zurückgesetzt haben!\n \nFortfahren?", users = "Benutzer", } locale["english"] = { activate = "Activate", active = "active", already_active = "User is already activated.", apiversion_failed = "Your Lua-API is too old. Please update Neutrino.", back = "Back", back_hint = "Return to previous menu", cant_change_pin = "PIN can not be changed while user is active", cant_remove_active = "The active user can not be removed.", cant_remove_owner = "The owner may not be removed.", cant_reset_plugin = "Can't reset plugin.", caption = "Userbouquets" .. " v" .. version, content_failed = "Error! Directory content not correct.", create = "Create new user", create_hint = "Create new user based apon owner data", info = "Ben Uwe lives!", no_users = "No users created", owner = "Owner", panic = "Panic!", pin = "PIN", pin_change = "Change PIN", pin_changed = "PIN changed", pin_new = "New PIN", pin_wrong = "Wrong PIN", remove = "Remove", removed = "User was removed.", reset = "Reset plugin", reset_confirm = "All user bouquets and plugin data will be removed.\n \nContinue?", reset_hint = "Remove user bouquets and plugin data", selection = "Activate, remove or change PIN?", selection_hint = "Select action for this user", start_confirm = "This plugin allows to adjust the channellists bouquets.xml and\nubouquets.xml depending on users.\nThey can be managed independently.\n \nDon't remove this plugin before you don't have executed the built-in reset!\n \nContinue?", users = "Users", } local neutrino_conf = configfile.new() neutrino_conf:loadConfig("/var/tuxbox/config/neutrino.conf") lang = neutrino_conf:getString("language", "english") if locale[lang] == nil then lang = "english" end timing_menu = neutrino_conf:getString("timing.menu", "0") -- ---------------------------------------------------------------------------- function init() if not check_content(owner_dir) then local res = messagebox.exec{title=locale[lang].caption, text=locale[lang].start_confirm, buttons={ "yes", "no" } } if res == "no" then return false end fh:mkdir(owner_dir) fh:mkdir(users_dir) fh:cp(zapit_dir .. bouquets_xml, owner_dir, "a") fh:cp(zapit_dir .. bouquets_xml, zapit_dir .. bouquets_xml .. ".bak", "a") fh:cp(zapit_dir .. ubouquets_xml, owner_dir, "a") fh:cp(zapit_dir .. ubouquets_xml, zapit_dir .. ubouquets_xml .. ".bak", "a") fh:touch(owner_dir .. passwd) activate(owner_dir) end return true end function paint_menu() m = menu.new{name=locale[lang].caption, icon="settings"} m:addKey{directkey=RC.home, id="home", action="exit_menu"} m:addKey{directkey=RC.setup, id="setup", action="exit_menu"} m:addKey{directkey=RC.info, id="info", action="info"} m:addItem{type="separator"} m:addItem{type="forwarder", id="home", action="exit_menu", name=locale[lang].back, icon="home", directkey=RC.home, hint_icon="hint_back", hint=locale[lang].back_hint}; m:addItem{type="separatorline"} m:addItem { type="forwarder", action="selection", enabled=check_content(owner_dir), id=owner_dir, name=get_username(owner_dir), right_icon=get_active_icon(owner_dir), directkey=RC.red, hint_icon="hint_service", hint=locale[lang].selection_hint } m:addItem{type="separator"} user_new = "" m:addItem { type="keyboardinput", action="create", id="dummy", value=user_new, name=locale[lang].create, directkey=RC.green, hint_icon="hint_bedit", hint=locale[lang].create_hint } m:addItem{type="separatorline", name=locale[lang].users} local i = 0 local d = io.popen('find "' .. users_dir .. '" -type d -maxdepth 1 -mindepth 1') for user_dir in d:lines() do i = i + 1 m:addItem { type="forwarder", action="selection", enabled=check_content(user_dir), id=user_dir, name=get_username(user_dir), right_icon=get_active_icon(user_dir), directkey=RC[""..i..""]; hint_icon="hint_service", hint=locale[lang].selection_hint } end if i == 0 then m:addItem { type="forwarder", enabled=false, name=locale[lang].no_users, } end m:addItem{type="separatorline"} m:addItem{type="separator"} m:addItem { type="forwarder", action="reset", name=locale[lang].reset, directkey=RC.blue, hint_icon="hint_delete", hint=locale[lang].reset_hint } m:exec() --[[ msg, data = n:GetInput(50) if msg == RC.timeout then repaint_menu = false end ]] end function hide_menu() if m ~= nil then m:hide() end end function exit_menu(id) repaint_menu = false if id == "setup" then return MENU_RETURN.EXIT_ALL end return MENU_RETURN.EXIT end function selection(dir) hide_menu() local dx = n:scale2Res(500) local dy = n:scale2Res(150) local x = SCREEN.OFF_X + (((SCREEN.END_X - SCREEN.OFF_X) - dx) / 2) local y = SCREEN.OFF_Y + (((SCREEN.END_Y - SCREEN.OFF_Y) - dy) / 2) local t = get_username(dir) if check_active(dir) then t = t .. " (" .. locale[lang].active .. ")" end local chooser = cwindow.new{x=x, y=y, dx=dx, dy=dy, title=t, icon="settings", has_shadow=true, show_footer=true, btnGreen=locale[lang].activate, btnYellow=locale[lang].remove, btnBlue=locale[lang].pin_change} ctext.new{parent=chooser, x=n:scale2Res(10), y=0, dx=dx-n:scale2Res(2*10), dy=dy-chooser:headerHeight(), text=locale[lang].selection, font_text=FONT.MENU, mode="ALIGN_CENTER"} chooser:paint() local i = 0 local d = 500 -- ms local t = (timing_menu * 1000) / d if t == 0 then t = -1 -- no timeout end repeat i = i + 1 msg, data = n:GetInput(d) if (msg == RC.ok) or (msg == RC.green) then activate(dir) msg = RC.home elseif (msg == RC.yellow) then remove(dir) msg = RC.home elseif (msg == RC.blue) then chooser:hide() pin(dir) chooser:paint() end until msg == RC.home or msg == RC.setup or i == t; chooser:hide() chooser = nil if msg == RC.setup then repaint_menu = false return MENU_RETURN.EXIT_ALL end return MENU_RETURN.EXIT end -- --------------------------------------------------------------------------- if APIVERSION.MAJOR .. APIVERSION.MINOR < "156" then local apiversion = "Lua-Api v" .. APIVERSION.MAJOR .. "." .. APIVERSION.MINOR .. "\n \n" showhint(locale[lang].panic, apiversion .. locale[lang].apiversion_failed) return end if init() then repaint_menu = true while repaint_menu do paint_menu() end end