Compare commits

..

No commits in common. "master" and "1.3.0" have entirely different histories.

59 changed files with 1158 additions and 2445 deletions

5
.gitattributes vendored
View file

@ -1,5 +0,0 @@
mtt.lua export-ignore
docker-compose.yml export-ignore
*.spec.lua export-ignore
test/* export-ignore
screenshot_* export-ignore

View file

@ -1,10 +1,17 @@
name: luacheck name: luacheck
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
luacheck: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - uses: actions/checkout@v1
uses: actions/checkout@master - name: apt
- name: Luacheck run: sudo apt-get install -y luarocks
uses: lunarmodules/luacheck@master - name: luacheck install
run: luarocks install --local luacheck
- name: luacheck run
run: $HOME/.luarocks/bin/luacheck ./

View file

@ -9,9 +9,9 @@ jobs:
timeout-minutes: 10 timeout-minutes: 10
strategy: strategy:
matrix: matrix:
ENGINE_VERSION: [5.0.1, 5.1.1, 5.2.0, 5.3.0, 5.4.1, 5.5.1, 5.6.1, 5.7.0, 5.8.0, 5.9.1, 5.10.0, latest] ENGINE_VERSION: [5.0.1, 5.1.1, 5.2.0, 5.3.0, 5.4.1, 5.5.1, 5.6.1, 5.7.0, latest]
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v3
- name: test - name: test
run: docker compose up --exit-code-from sut run: docker-compose up --exit-code-from sut

2
.gitignore vendored
View file

@ -1,4 +1,2 @@
i18n.py i18n.py
mod_translation_updater.py
locale/*.tr.old locale/*.tr.old
*.patch

View file

@ -5,16 +5,15 @@ globals = {
read_globals = { read_globals = {
-- Stdlib -- Stdlib
string = {fields = {"split"}}, string = {fields = {"split"}},
table = {fields = {"copy", "getn", "indexof", "insert_all"}}, table = {fields = {"copy", "getn"}},
beerchat = {fields = {"has_player_muted_player", "execute_callbacks"}},
-- Luanti -- Minetest
"core", "minetest",
"vector", "ItemStack", "vector", "ItemStack",
"dump", "dump",
-- Deps -- Deps
"unified_inventory", "default", "sfinv_buttons", "unified_inventory", "default",
-- optional mods -- optional mods
"mtt", "canonical_name" "mtt", "canonical_name"

View file

@ -11,8 +11,7 @@ It adds a mail-system that allows players to send each other messages in-game an
# Screenshot # Screenshot
![Main view](screenshot_1.4.0_1.png) ![](screenshot_1.2.0.png)
![Message view](screenshot_1.4.0_2.png)
# Installation # Installation
@ -44,8 +43,7 @@ Mails can be deleted, marked as read or unread, replied to and forwarded to anot
* Multiple selection (new in 1.1.0) * Multiple selection (new in 1.1.0)
* Settings * Settings
* Chat, on join, HUD and sound notifications * Chat, on join, HUD and sound notifications
* Anti-spam detection * Translated in : English, French, German, Chinese (both traditional and simplified), Spanish, Brazilian Portuguese.
* Translated in : English, French, German, Chinese (both traditional and simplified), Spanish, Brazilian Portuguese, Hungarian, Indonesian.
# Compatibility / Migration # Compatibility / Migration
@ -53,10 +51,8 @@ Overview:
* `v1` all the data is in the `<worldfolder>/mails.db` file * `v1` all the data is in the `<worldfolder>/mails.db` file
* `v2` every player has its own (in-) mailbox in the `<worldfolder>/mails/<playername>.json` file * `v2` every player has its own (in-) mailbox in the `<worldfolder>/mails/<playername>.json` file
* `v3` every player has an entry in the `<playername>` `mod_storage/` (inbox, outbox, drafts, contacts, mailing lists, settings) * `v3` every player has an entry in the `<playername>` `mod_storage/` (inbox, outbox, drafts, contacts, mailing lists, settings)
* `v3.1` database fix after the message id mess
# Dependencies # Dependencies
* None * None
# License # License
@ -76,7 +72,7 @@ See the "LICENSE" file
* fluxionary (Minor fixups) * fluxionary (Minor fixups)
* Toby1710 (UX fixes) * Toby1710 (UX fixes)
* Peter Nerlich (CC, BCC) * Peter Nerlich (CC, BCC)
* Emojigit (Performance, Traditional Chinese translation) * Emojigit (Traditional Chinese translation)
* Niklp09 (German translation) * Niklp09 (German translation)
* Dennis Jenkins (UX fixes) * Dennis Jenkins (UX fixes)
* Thomas Rudin (Maintenance) * Thomas Rudin (Maintenance)
@ -88,12 +84,6 @@ See the "LICENSE" file
* nyomi (Hungarian translation) * nyomi (Hungarian translation)
* whosit (UI fixes) * whosit (UI fixes)
* Wuzzy (German translation) * Wuzzy (German translation)
* savilli (UX fixes)
* Panquesito7 (Maintenance)
* Eredin (Spanish translation)
* Muhammad Rifqi Priyo Susanto (Indonesian translation)
* aBlueShadow (sfinv compatibility)
* Singularis (UX and storage fixes)
# Contribute # Contribute

77
api.lua
View file

@ -1,7 +1,7 @@
-- see: mail.md -- see: mail.md
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local f = string.format local f = string.format
@ -10,22 +10,9 @@ function mail.register_on_receive(func)
mail.registered_on_receives[#mail.registered_on_receives + 1] = func mail.registered_on_receives[#mail.registered_on_receives + 1] = func
end end
mail.registered_on_player_receives = {}
function mail.register_on_player_receive(func)
table.insert(mail.registered_on_player_receives, func)
end
mail.registered_recipient_handlers = {}
function mail.register_recipient_handler(func)
table.insert(mail.registered_recipient_handlers, func)
end
function mail.send(m) function mail.send(m)
if type(m.from) ~= "string" then return false, "'from' is not a string" end if type(m.from) ~= "string" then return false, "'from' is not a string" end
if type(m.to or "") ~= "string" then return false, "'to' is not a string" end if type(m.to) ~= "string" then return false, "'to' is not a string" end
if type(m.cc or "") ~= "string" then return false, "'cc' is not a string" end
if type(m.bcc or "") ~= "string" then return false, "'bcc' is not a string" end
if type(m.subject or "") ~= "string" then return false, "'subject' is not a string" end
if type(m.body) ~= "string" then return false, "'body' is not a string" end if type(m.body) ~= "string" then return false, "'body' is not a string" end
-- defaults -- defaults
@ -34,25 +21,23 @@ function mail.send(m)
-- normalize to, cc and bcc while compiling a list of all recipients -- normalize to, cc and bcc while compiling a list of all recipients
local recipients = {} local recipients = {}
local undeliverable = {} local undeliverable = {}
m.to = mail.concat_player_list(mail.extract_maillists(m.to, m.from)) m.to = mail.concat_player_list(mail.extractMaillists(m.to, m.from))
m.to = mail.normalize_players_and_add_recipients(m.from, m.to, recipients, undeliverable) m.to = mail.normalize_players_and_add_recipients(m.to, recipients, undeliverable)
if m.cc then if m.cc then
m.cc = mail.concat_player_list(mail.extract_maillists(m.cc, m.from)) m.cc = mail.concat_player_list(mail.extractMaillists(m.cc, m.from))
m.cc = mail.normalize_players_and_add_recipients(mail.from, m.cc, recipients, undeliverable) m.cc = mail.normalize_players_and_add_recipients(m.cc, recipients, undeliverable)
end end
if m.bcc then if m.bcc then
m.bcc = mail.concat_player_list(mail.extract_maillists(m.bcc, m.from)) m.bcc = mail.concat_player_list(mail.extractMaillists(m.bcc, m.from))
m.bcc = mail.normalize_players_and_add_recipients(m.from, m.bcc, recipients, undeliverable) m.bcc = mail.normalize_players_and_add_recipients(m.bcc, recipients, undeliverable)
end end
if next(undeliverable) then -- table is not empty if next(undeliverable) then -- table is not empty
local undeliverable_reason = {S("The mail could not be sent:")} local undeliverable_names = {}
for _, reason in pairs(undeliverable) do for name in pairs(undeliverable) do
table.insert(undeliverable_reason, reason) undeliverable_names[#undeliverable_names + 1] = '"' .. name .. '"'
end end
return false, table.concat(undeliverable_reason, "\n") return false, f("recipients %s don't exist; cannot send mail.", table.concat(undeliverable_names, ", "))
elseif not next(recipients) then
return false, S("You did not specify any valid recipient.")
end end
local extra = {} local extra = {}
@ -69,7 +54,7 @@ function mail.send(m)
extra_log = "" extra_log = ""
end end
core.log("action", f("[mail] %q send mail to %q%s with subject %q and body %q", minetest.log("action", f("[mail] %q send mail to %q%s with subject %q and body %q",
m.from, m.to, extra_log, m.subject, m.body m.from, m.to, extra_log, m.subject, m.body
)) ))
@ -95,11 +80,34 @@ function mail.send(m)
local entry = mail.get_storage_entry(m.from) local entry = mail.get_storage_entry(m.from)
table.insert(entry.outbox, 1, msg) table.insert(entry.outbox, 1, msg)
mail.set_storage_entry(m.from, entry) mail.set_storage_entry(m.from, entry)
msg.spam = #mail.check_spam(msg) >= 1
-- add in every receivers inbox -- add in every receivers inbox
for _, deliver in pairs(recipients) do for recipient in pairs(recipients) do
deliver(msg) entry = mail.get_storage_entry(recipient)
table.insert(entry.inbox, msg)
mail.set_storage_entry(recipient, entry)
end
-- notify recipients that happen to be online
local mail_alert = S("You have a new message from @1! Subject: @2", m.from, m.subject) ..
"\n" .. S("To view it, type /mail")
local unified_inventory_alert = S("You could also use the button in your inventory.")
for _, player in ipairs(minetest.get_connected_players()) do
local name = player:get_player_name()
if recipients[name] then
if mail.get_setting(name, "chat_notifications") == true then
minetest.chat_send_player(name, mail_alert)
if minetest.get_modpath("unified_inventory") then
minetest.chat_send_player(name, unified_inventory_alert)
end
end
if mail.get_setting(name, "sound_notifications") == true then
minetest.sound_play("mail_notif", {to_player=name})
end
local receiver_entry = mail.get_storage_entry(name)
local receiver_messages = receiver_entry.inbox
mail.hud_update(name, receiver_messages)
end
end end
for i=1, #mail.registered_on_receives do for i=1, #mail.registered_on_receives do
@ -113,16 +121,13 @@ end
function mail.save_draft(m) function mail.save_draft(m)
if type(m.from) ~= "string" then return false, "'from' is not a string" end if type(m.from) ~= "string" then return false, "'from' is not a string" end
if type(m.to or "") ~= "string" then return false, "'to' is not a string" end if type(m.to) ~= "string" then return false, "'to' is not a string" end
if type(m.cc or "") ~= "string" then return false, "'cc' is not a string" end
if type(m.bcc or "") ~= "string" then return false, "'bcc' is not a string" end
if type(m.subject or "") ~= "string" then return false, "'subject' is not a string" end
if type(m.body) ~= "string" then return false, "'body' is not a string" end if type(m.body) ~= "string" then return false, "'body' is not a string" end
-- defaults -- defaults
m.subject = m.subject or "(No subject)" m.subject = m.subject or "(No subject)"
core.log("verbose", f("[mail] %q saves draft with subject %q and body %q", minetest.log("verbose", f("[mail] %q saves draft with subject %q and body %q",
m.from, m.subject, m.body m.from, m.subject, m.body
)) ))

42
api.md
View file

@ -1,3 +1,4 @@
# Mail format # Mail format
The mail format in the api hooks The mail format in the api hooks
@ -33,7 +34,7 @@ local success, error = mail.send({
``` ```
# Hooks # Hooks
Generic on-receive mail hook: On-receive mail hook:
```lua ```lua
mail.register_on_receive(function(m) mail.register_on_receive(function(m)
@ -41,35 +42,11 @@ mail.register_on_receive(function(m)
end) end)
``` ```
Player-specific on-receive mail hook:
```lua
mail.register_on_player_receive(function(player, msg)
-- "player" is the name of a recipient; "msg" is a mail object (see "Mail format")
end)
```
# Recipient handler
Recipient handlers are registered using
```lua
mail.register_recipient_handler(function(sender, name)
end)
```
where `name` is the name of a single recipient.
The recipient handler should return
* `nil` if the handler does not handle messages sent to the particular recipient,
* `true, player` (where `player` is a string or a list of strings) if the mail should be redirected to `player`,
* `true, deliver` if the mail should be delivered by calling `deliver` with the message, or
* `false, reason` (where `reason` is optional or, if provided, a string) if the recipient explicitly rejects the mail.
# Internals # Internals
mod-storage entry for a player (indexed by playername and serialized with json): mod-storage entry for a player (indexed by playername and serialized with json):
```lua ```lua
{ {
contacts = { contacts = {
{ {
-- name of the player (unique key in the list) -- name of the player (unique key in the list)
@ -99,9 +76,7 @@ mod-storage entry for a player (indexed by playername and serialized with json):
-- timestamp (os.time()) -- timestamp (os.time())
time = 1234, time = 1234,
-- read-flag (true: player has read the mail, inbox only) -- read-flag (true: player has read the mail, inbox only)
read = true, read = true
-- spam-flag (true: that mail is noted as a spam)
spam = false
},{ },{
... ...
} }
@ -109,12 +84,6 @@ mod-storage entry for a player (indexed by playername and serialized with json):
outbox = { outbox = {
-- same format as "inbox" -- same format as "inbox"
}, },
drafts = {
-- same format as "inbox"
},
trash = {
-- same format as "inbox"
},
lists = { lists = {
{ {
-- name of the maillist (unique key in the list) -- name of the maillist (unique key in the list)
@ -124,10 +93,5 @@ mod-storage entry for a player (indexed by playername and serialized with json):
-- playername list -- playername list
players = {"playername", "playername2"} players = {"playername", "playername2"}
} }
},
settings = {
setting1 = "value",
setting2 = true,
setting3 = 123
} }
} }

View file

@ -1,76 +1,12 @@
mail.register_recipient_handler(function(_, name)
if name:sub(1, 6) == "alias/" then
return true, name:sub(7)
elseif name == "list/test" then
return true, {"alias/player1", "alias/player2"}
elseif name == "list/reject" then
return false, "It works (?)"
end
end)
mail.update_maillist("player1", {
owner = "player1",
name = "recursive",
desc = "",
players = {"@recursive", "player1"},
}, "recursive")
local received_count = {}
mail.register_on_player_receive(function(player)
received_count[player] = (received_count[player] or 0) + 1
end)
local sent_count = 0
mail.register_on_receive(function()
sent_count = sent_count+1
end)
local function assert_inbox_count(player_name, count)
local entry = mail.get_storage_entry(player_name)
assert(entry, player_name .. " has no mail entry")
local actual_count = #entry.inbox
assert(actual_count == count, ("incorrect mail count: %d expected, got %d"):format(count, actual_count))
local player_received = received_count[player_name] or 0
assert(player_received == count, ("incorrect receive count: %d expected, got %d"):format(count, player_received))
end
local function assert_send(expected_success, ...)
local success, err = mail.send(...)
if expected_success then
assert(success, ("expected mail to be sent, got error message: %s"):format(err))
assert(not err, ("unexpected message after sending mail: %s"):format(err))
else
assert(not success, "expected mail to be rejected, mail was sent")
assert(type(err) == "string", ("expected error message, got datum of type %s"):format(type(err)))
end
end
mtt.register("send mail", function(callback) mtt.register("send mail", function(callback)
-- local maillists -- send a mail
assert_send(true, {from = "player1", to = "@recursive", subject = "hello recursion", body = "blah"}) local success, err = mail.send({from = "player1", to = "player2", subject = "something", body = "blah"})
assert_inbox_count("player1", 1) assert(success)
assert(sent_count == 1) assert(not err)
-- do not allow empty recipients
assert_send(false, {from = "player1", to = "@doesnotexist", subject = "should not be sent", body = "blah"})
assert(sent_count == 1)
-- send a mail to a list
assert_send(true, {from = "player1", to = "list/test", subject = "something", body = "blah"})
assert_inbox_count("player2", 1)
assert_inbox_count("player1", 1)
assert(sent_count == 2)
-- send a second mail to the list and also the sender
assert_send(true, {from = "player1", to = "list/test, alias/player1", subject = "something", body = "blah"})
assert_inbox_count("player2", 2)
assert_inbox_count("player1", 2)
assert(sent_count == 3)
-- send a mail to list/reject - the mail should be rejected
assert_send(false, {from = "player1", to = "list/reject", subject = "something", body = "NO"})
assert_inbox_count("player2", 2)
assert_inbox_count("player1", 2)
assert(sent_count == 3)
-- check the receivers inbox
local entry = mail.get_storage_entry("player2")
assert(entry)
assert(#entry.inbox > 0)
callback() callback()
end) end)

View file

@ -1,4 +1,4 @@
core.register_chatcommand("mail",{ minetest.register_chatcommand("mail",{
description = "Open the mail interface", description = "Open the mail interface",
func = function(name, param) func = function(name, param)
if #param > 0 then -- if param is not empty if #param > 0 then -- if param is not empty

View file

@ -1,4 +1,4 @@
version: "4.1" version: "3.6"
services: services:
sut: sut:

12
gui.lua
View file

@ -1,5 +1,5 @@
if core.get_modpath("unified_inventory") then if minetest.get_modpath("unified_inventory") then
unified_inventory.register_button("mail", { unified_inventory.register_button("mail", {
type = "image", type = "image",
@ -10,13 +10,3 @@ if core.get_modpath("unified_inventory") then
end end
}) })
end end
if core.get_modpath("sfinv_buttons") then
sfinv_buttons.register_button("mail", {
title = "Mail",
image = "mail_button.png",
action = function(player)
mail.show_mail_menu(player:get_player_name())
end
})
end

10
hud.lua
View file

@ -1,12 +1,12 @@
local huddata = {} local huddata = {}
core.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
local name = player:get_player_name() local name = player:get_player_name()
local data = {} local data = {}
data.imageid = player:hud_add({ data.imageid = player:hud_add({
type = "image", hud_elem_type = "image",
name = "MailIcon", name = "MailIcon",
position = {x=0.52, y=0.52}, position = {x=0.52, y=0.52},
text="", text="",
@ -15,7 +15,7 @@ core.register_on_joinplayer(function(player)
}) })
data.textid = player:hud_add({ data.textid = player:hud_add({
type = "text", hud_elem_type = "text",
name = "MailText", name = "MailText",
position = {x=0.55, y=0.52}, position = {x=0.55, y=0.52},
text= "", text= "",
@ -27,7 +27,7 @@ core.register_on_joinplayer(function(player)
huddata[name] = data huddata[name] = data
end) end)
core.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name() local name = player:get_player_name()
huddata[name] = nil huddata[name] = nil
end) end)
@ -35,7 +35,7 @@ end)
function mail.hud_update(playername, messages) function mail.hud_update(playername, messages)
local data = huddata[playername] local data = huddata[playername]
local player = core.get_player_by_name(playername) local player = minetest.get_player_by_name(playername)
if not data or not player then if not data or not player then
return return

View file

@ -3,10 +3,7 @@ mail = {
version = 3, version = 3,
-- mod storage -- mod storage
storage = core.get_mod_storage(), storage = minetest.get_mod_storage(),
-- translation
S = core.get_translator(core.get_current_modname()),
-- ui theme prepend -- ui theme prepend
theme = "", theme = "",
@ -20,7 +17,6 @@ mail = {
outbox = {}, outbox = {},
drafts = {}, drafts = {},
trash = {}, trash = {},
message = {},
contacts = {}, contacts = {},
maillists = {}, maillists = {},
to = {}, to = {},
@ -32,20 +28,42 @@ mail = {
filter = {}, filter = {},
multipleselection = {}, multipleselection = {},
optionstab = {}, optionstab = {},
settings_group = {}, chat_notifications = {},
contributor_grouping = {}, onjoin_notifications = {},
hud_notifications = {},
sound_notifications = {},
unreadcolorenable = {},
cccolorenable = {},
trash_move_enable = {},
auto_marking_read = {},
},
colors = {
header = "#999",
selected = "#72FF63",
important = "#FFD700",
additional = "#CCCCDD",
imp_sel = "#B9EB32",
add_sel = "#9FE6A0",
imp_add = "#E6D26F",
imp_add_sel = "#BFE16B",
highlighted = "#608631",
new = "#00F529"
}, },
message_drafts = {} message_drafts = {}
} }
if core.get_modpath("default") then if minetest.get_modpath("default") then
mail.theme = default.gui_bg .. default.gui_bg_img mail.theme = default.gui_bg .. default.gui_bg_img
end end
-- sub files -- sub files
local MP = core.get_modpath(core.get_current_modname()) local MP = minetest.get_modpath(minetest.get_current_modname())
dofile(MP .. "/util/init.lua") dofile(MP .. "/util/normalize.lua")
dofile(MP .. "/util/contact.lua")
dofile(MP .. "/util/uuid.lua")
dofile(MP .. "/util/time_ago.lua")
dofile(MP .. "/chatcommands.lua") dofile(MP .. "/chatcommands.lua")
dofile(MP .. "/migrate.lua") dofile(MP .. "/migrate.lua")
dofile(MP .. "/hud.lua") dofile(MP .. "/hud.lua")
@ -53,14 +71,28 @@ dofile(MP .. "/storage.lua")
dofile(MP .. "/api.lua") dofile(MP .. "/api.lua")
dofile(MP .. "/gui.lua") dofile(MP .. "/gui.lua")
dofile(MP .. "/onjoin.lua") dofile(MP .. "/onjoin.lua")
dofile(MP .. "/player_recipients.lua") dofile(MP .. "/ui/mail.lua")
-- sub directories dofile(MP .. "/ui/inbox.lua")
dofile(MP .. "/ui/init.lua") dofile(MP .. "/ui/outbox.lua")
dofile(MP .. "/ui/drafts.lua")
dofile(MP .. "/ui/trash.lua")
dofile(MP .. "/ui/message.lua")
dofile(MP .. "/ui/receivers.lua")
dofile(MP .. "/ui/events.lua")
dofile(MP .. "/ui/contacts.lua")
dofile(MP .. "/ui/edit_contact.lua")
dofile(MP .. "/ui/select_contact.lua")
dofile(MP .. "/ui/maillists.lua")
dofile(MP .. "/ui/edit_maillists.lua")
dofile(MP .. "/ui/compose.lua")
dofile(MP .. "/ui/options.lua")
dofile(MP .. "/ui/settings.lua")
dofile(MP .. "/ui/about.lua")
-- migrate storage -- migrate storage
mail.migrate() mail.migrate()
if core.get_modpath("mtt") then if minetest.get_modpath("mtt") then
dofile(MP .. "/mtt.lua") dofile(MP .. "/mtt.lua")
dofile(MP .. "/api.spec.lua") dofile(MP .. "/api.spec.lua")
dofile(MP .. "/migrate.spec.lua") dofile(MP .. "/migrate.spec.lua")

View file

@ -1,118 +1,93 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=Die Mail konnte nicht gesendet werden:
You did not specify any valid recipient.=Sie haben keinen gültigen Empfänger angegeben.
You have mail! Type /mail to read=Sie haben Post! „/mail“ zum Lesen eingeben
You have a new message from @1! Subject: @2=Sie haben eine neue Nachricht von @1! Betreff: @2
To view it, type /mail=Geben Sie zum Anzeigen „/mail“ ein
You could also use the button in your inventory.=Sie können auch die Schaltfläche in Ihrem Inventar verwenden.
Original author=Ursprünglicher Autor
Code=Code
Internationalization=Internationalisierung
Textures=Texturen
Audio=Audio
Provided by mt-mods=Bereitgestellt von mt-mods Provided by mt-mods=Bereitgestellt von mt-mods
Version: @1=Version: @1 Version=Version
Licenses=Lizenzen Licenses=Lizenzen
Expat (code), WTFPL (textures)=Expat (Code), WTFPL (Texturen) Expat (code), WTFPL (textures)=Expat (Code), WTFPL (Texturen)
Communication using this system is NOT guaranteed to be private!=Die Kommunikation über dieses System ist NICHT garantiert privat! Communication using this system is NOT guaranteed to be private!=Die Kommunikation über dieses System ist NICHT garantiert privat!
Admins are able to view the messages of any player.=Admins können die Nachrichten aller Spielenden sehen. Admins are able to view the messages of any player.=Admins können die Nachrichten aller Spieler sehen.
Contributors=Mitwirkende Contributors=Mitwirkende
Group by name=Nach Name gruppieren You have a new message from @1! Subject: @2=Sie haben eine neue Nachricht von @1! Betreff: @2
Group by contribution=Nach Beitrag gruppieren To view it, type /mail=Geben Sie zum Anzeigen „/mail“ ein
Note=Anmerkung You could also use the button in your inventory.=Sie können auch die Schaltfläche in Ihrem Inventar verwenden.
Settings=Einstellungen
About=Über
BCC=BCC BCC=BCC
Cancel=Abbrechen Cancel=Abbrechen
Save draft=Entwurf speichern Save draft=Entwurf speichern
Send=Senden Send=Senden
Subject=Betreff No contacts=Keine Kontakte
To=An
CC=CC
Delete=Löschen
New=Neu
Edit=Bearbeiten
Back=Zurück
Name=Name Name=Name
No drafts=Keine Entwürfe No drafts=Keine Entwürfe
Edit=Bearbeiten
To=An
Player name=Spielername
That name is already in your contacts=Dieser Name ist bereits in Ihren Kontakten
The contact name cannot be empty.=Der Kontaktname kann nicht leer sein.
Note=Anmerkung
Maillist name=Verteilerlistenname
Desc=Beschreibung
Players=Spieler
That name is already in your mailing lists.=Dieser Name ist bereits in Ihren Verteilerlisten.
The mailing list name cannot be empty.=Der Verteilerlistenname kann nicht leer sein.
Back=Zurück
Mark Read=Als gelesen makieren
Mark Unread=Als ungelesen makieren
Trash=Papierkorb Trash=Papierkorb
Inbox=Posteingang Inbox=Posteingang
Outbox=Gesendet Outbox=Senden
Drafts=Entwürfe Drafts=Entwürfe
Contacts=Kontakte Contacts=Kontakte
Mail lists=Verteilerlisten Mail lists=Verteilerlisten
Options=Einstellungen Options=Einstellungen
Close=Schließen Close=Schließen
(No subject)=(Kein Betreff) (No subject)=(Kein Betreff)
Player name=Spielername Subject=Betreff
That name is already in your contacts=Dieser Name ist bereits in Ihren Kontakten Delete=Löschen
The contact name cannot be empty.=Der Kontaktname kann nicht leer sein. New=Neu
Save=Speichern
Maillist name=Verteilerlistenname
Desc=Beschreibung
Players=Spieler
That name is already in your mailing lists.=Dieser Name ist bereits in Ihren Verteilerlisten.
The mailing list name cannot be empty.=Der Verteilerlistenname kann nicht leer sein.
Mark Read=Als gelesen makieren
Mark Unread=Als ungelesen makieren
Mark Spam=Als Spam markieren
Unmark Spam=Kein Spam
Reply=Antworten
Reply all=Allen antworten
Forward=Weiterleiten
Reply only to the sender=Nur dem Absender antworten
Reply to all involved people=Allen beteiligten Personen antworten
Transfer message to other people=Nachricht an andere Personen weiterleiten
Date=Datum Date=Datum
From=Von
Filter=Filter Filter=Filter
Allow multiple selection=Mehrfachauswahl zulassen Allow multiple selection=Mehrfachauswahl zulassen
@1 of @2 selected=@1 von @2 ausgewählt @1 of @2 selected=@1 von @2 ausgewählt
(Un)select all=Alle aus-/abwählen (Un)select all=Alle aus-/abwählen
No mail=Keine Nachrichten No mail=Keine Nachrichten
Reply=Antworten
Reply all=Allen antworten
Forward=Weiterleiten
Reply only to the sender=
Reply to all involved people=
Transfer message to other people=
Read=Lesen Read=Lesen
Ascending=Aufsteigend From=Von
Descending=Absteigend
(No description)=(Keine Beschreibung) (No description)=(Keine Beschreibung)
No maillist=Keine Verteilerliste No maillist=Keine Verteilerliste
Receivers=Empfänger Receivers=Empfänger
(Un)mute sender=Absender stummschalten/entstummen You have mail! Type /mail to read=Sie haben Post! „/mail“ eingeben zum Lesen
Add=Hinzufügen Add=Hinzufügen
Remove=Entfernen Remove=Entfernen
Reset=Zurücksetzen CC=CC
Restore=Wiederherstellen Notifications=Benachrichtigungen
Empty=Leer
Trash is empty=Papierkorb ist leer
From/To=Von/An
No contacts=Keine Kontakte
The method of delivery to @1 is invalid.=Die Zustellmethode an @1 ist ungültig.
The recipient @1 could not be identified.=Der Empfänger @1 konnte nicht identifiziert werden.
@1 rejected your mail.=@1 hat Ihre Mail abgewiesen.
Chat notifications=Chat-Benachrichtigungen Chat notifications=Chat-Benachrichtigungen
Receive a message in the chat when there is a new message=Eine Nachricht im Chat erhalten, wenn es eine neue Mail gibt On join notifications=Bei-Eintritt-Benachrichtigungen
On join notifications=Login-Benachrichtigungen
Receive a message at login when inbox isn't empty=Bei der Anmeldung eine Nachricht erhalten, wenn der Posteingang nicht leer ist
HUD notifications=HUD-Benachrichtigungen HUD notifications=HUD-Benachrichtigungen
Show an HUD notification when inbox isn't empty=Eine HUD-Benachrichtigung anzeigen, wenn der Posteingang nicht leer ist
Sound notifications=Klang-Benachrichtigungen Sound notifications=Klang-Benachrichtigungen
Play a sound when there is a new message=Einen Ton abspielen, wenn eine neue Mail eingeht Message list=Nachrichtenliste
Show unread in different color=Ungelesenes in anderer Farbe anzeigen Show unread in different color=Ungelesenes in anderer Farbe anzeigen
Show CC/BCC in different color=CC/BCC in anderer Farbe anzeigen Show CC/BCC in different color=CC/BCC in anderer Farbe anzeigen
Default sorting field=Standardsortierfeld Default sorting fields=Standardsortierfelder
Default sorting direction=Standardmäßige Sortierrichtung
Move deleted messages to trash=Gelöschte Nachrichten in den Papierkorb verschieben
Automatic marking read=Automatisch als gelesen markieren
Mark a message as read when opened=Nachrichten beim Öffnen als gelesen markieren
Date format=Datumsformat
Timezone offset=Zeitverschiebung
Offset to add to server time.=Verschiebung, die zur Serverzeit addiert wird.
Mute list=Stummgeschaltet-Liste
Notifications=Benachrichtigungen
Message list=Nachrichtenliste
Fields=Felder
Spam=Spam
Other=Anderes Other=Anderes
Date and Time=Datum und Uhrzeit Move deleted messages to trash=Gelöschte Nachrichten in den Papierkorb verschieben
Automatic marking read=
Date format:=Datumsformat:
Receive a message in the chat when there is a new message=
Receive a message at login when inbox isn't empty=
Show an HUD notification when inbox isn't empty=
Play a sound when there is a new message=
Mark a message as read when opened=
Reset=Zurücksetzen
Settings=Einstellungen
About=Über
Save=Speichern
Ascending=Aufsteigend
Descending=Absteigend
From/To=Von/An
years=Jahren years=Jahren
months=Monaten months=Monaten
weeks=Wochen weeks=Wochen
@ -121,3 +96,6 @@ hours=Stunden
minuts=Minuten minuts=Minuten
seconds=Sekunden seconds=Sekunden
@1 ago=Vor @1 @1 ago=Vor @1
Restore=Wiederherstellen
Empty=Leer
Trash is empty=Papierkorb ist leer

View file

@ -1,40 +1,55 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=
You did not specify any valid recipient.=
You have mail! Type /mail to read=¡Tienes correo! Escribe /mail para leerlo
You have a new message from @1! Subject: @2=¡Tienes un nuevo mensaje de @1! Asunto: @2
To view it, type /mail=Para verlo, escribe /mail
You could also use the button in your inventory.=También puedes usar el botón de tu inventario.
Original author=
Code=
Internationalization=
Textures=
Audio=
Provided by mt-mods=Proporcionado por mt-mods Provided by mt-mods=Proporcionado por mt-mods
Version: @1= Version=Versión
Licenses=Licencias Licenses=Licencias
Expat (code), WTFPL (textures)=Expat (código), WTFPL (texturas) Expat (code), WTFPL (textures)=Expat (código), WTFPL (texturas)
Communication using this system is NOT guaranteed to be private!=¡NO se garantiza que la comunicación mediante este sistema sea privada! Communication using this system is NOT guaranteed to be private!=¡NO se garantiza que la comunicación mediante este sistema sea privada!
Admins are able to view the messages of any player.=Los administradores pueden ver los mensajes de cualquier jugador. Admins are able to view the messages of any player.=Los administradores pueden ver los mensajes de cualquier jugador.
Contributors=Colaboradores Contributors=Colaboradores
Group by name= You have a new message from @1! Subject: @2=¡Tienes un nuevo mensaje de @1! Asunto: @2
Group by contribution= To view it, type /mail=Para verlo, escribe /mail
Note=Nota You could also use the button in your inventory.=También puedes usar el botón de tu inventario.
Settings=Ajustes
About=Acerca de
BCC=CCO BCC=CCO
Cancel=Cancelar Cancel=Cancelar
Save draft=Guardar borrador Save draft=Guardar borrador
Send=Enviar Send=Enviar
Subject=Asunto No contacts=Sin contactos
To=Para
CC=CC
Delete=Borrar
New=Nuevo
Edit=Editar Edit=Editar
Back=Volver
Name=Nombre
No drafts=No hay borradores No drafts=No hay borradores
Player name=Nombre del jugador
That name is already in your contacts=Ese nombre ya está en tus contactos
The contact name cannot be empty.=El nombre de contacto no puede estar vacío.
Note=Nota
Back=Volver
Save=Guardar
Maillist name=Nombre de la lista de correo
Desc=Desc
Players=Jugadores
That name is already in your mailing lists.=Ese nombre ya está en tus listas de correo.
The mailing list name cannot be empty.=El nombre de lista de correo no puede estar vacío.
Mark Read=Marcar como leído
Mark Unread=Marcar como no leído
Reply=Responder
Reply all=Responder a todos
Forward=Reenviar
Reply only to the sender=Responder solo al remitente
Reply to all involved people=Responder a todas las personas implicadas
Transfer message to other people=Transferir el mensaje a otras personas
From=De
Filter=Filtrar
Allow multiple selection=Permitir selección múltiple
@1 of @2 selected=@1 de @2 seleccionado(s)
(Un)select all=(Des)seleccionar todos
No mail=Sin correo
Subject=Asunto
New=Nuevo
Delete=Borrar
Date=Fecha
(No description)=(Sin descripción)
No maillist=Sin lista de correo
Receivers=Recipientes
You have mail! Type /mail to read=¡Tienes correo! Escribe /mail para leerlo
Read=Leído
Trash=Papelera Trash=Papelera
Inbox=Entrada Inbox=Entrada
Outbox=Enviados Outbox=Enviados
@ -44,75 +59,34 @@ Mail lists=Listas de correo
Options=Opciones Options=Opciones
Close=Cerrar Close=Cerrar
(No subject)=(Sin asunto) (No subject)=(Sin asunto)
Player name=Nombre del jugador To=Para
That name is already in your contacts=Ese nombre ya está en tus contactos
The contact name cannot be empty.=El nombre de contacto no puede estar vacío.
Save=Guardar
Maillist name=Nombre de la lista de correo
Desc=Desc
Players=Jugadores
That name is already in your mailing lists.=Ese nombre ya está en tus listas de correo.
The mailing list name cannot be empty.=El nombre de lista de correo no puede estar vacío.
Mark Read=Marcar como leído
Mark Unread=Marcar como no leído
Mark Spam=
Unmark Spam=
Reply=Responder
Reply all=Responder a todos
Forward=Reenviar
Reply only to the sender=Responder solo al remitente
Reply to all involved people=Responder a todas las personas implicadas
Transfer message to other people=Transferir el mensaje a otras personas
Date=Fecha
From=De
Filter=Filtro
Allow multiple selection=Permitir selección múltiple
@1 of @2 selected=@1 de @2 seleccionado(s)
(Un)select all=(Des)seleccionar todos
No mail=Sin correo
Read=Leer
Ascending=Ascendente
Descending=Descendente
(No description)=(Sin descripción)
No maillist=Sin lista de correo
Receivers=Recipientes
(Un)mute sender=
Add=Añadir Add=Añadir
Remove=Quitar Remove=Quitar
Reset=Restablecer Name=Nombre
Restore=Restaurar CC=CC
Empty=Vacío Notifications=Notificaciones
Trash is empty=La papelera está vacía
From/To=De/Para
No contacts=Sin contactos
The method of delivery to @1 is invalid.=
The recipient @1 could not be identified.=
@1 rejected your mail.=
Chat notifications=Notificaciones de chat Chat notifications=Notificaciones de chat
Receive a message in the chat when there is a new message=Recibir un mensaje en el chat cuando hay correo nuevo
On join notifications=Notificaciones al unirse On join notifications=Notificaciones al unirse
Receive a message at login when inbox isn't empty=Recibir mensaje al conectarse si la bandeja de entrada no está vacía
HUD notifications=Notificaciones de interfaz HUD notifications=Notificaciones de interfaz
Show an HUD notification when inbox isn't empty=Mostrar una notificación en la interfaz cuando la bandeja de entrada no está vacía
Sound notifications=Notificaciones de sonido Sound notifications=Notificaciones de sonido
Play a sound when there is a new message=Emitir un sonido cuando hay un correo nuevo Message list=Lista de mensajes
Show unread in different color=Mostrar no-leídos en diferente color Show unread in different color=Mostrar no-leídos en diferente color
Show CC/BCC in different color=Mostrar CC/CCO en diferente color Show CC/BCC in different color=Mostrar CC/CCO en diferente color
Default sorting field=Campo a ordenar por defecto Default sorting fields=Campos a ordenar por defecto
Default sorting direction= Other=Otros
Move deleted messages to trash=Mover mensajes borrados a la papelera Move deleted messages to trash=Mover mensajes borrados a la papelera
Automatic marking read=Marcar como leído automáticamente Automatic marking read=Marcar como leído automáticamente
Receive a message in the chat when there is a new message=Recibir un mensaje en el chat cuando hay correo nuevo
Receive a message at login when inbox isn't empty=Recibir mensaje al conectarse si la bandeja de entrada no está vacía
Show an HUD notification when inbox isn't empty=Mostrar una notificación en la interfaz cuando la bandeja de entrada no está vacía
Play a sound when there is a new message=Emitir un sonido cuando hay un correo nuevo
Mark a message as read when opened=Marcar un mensaje como leído al abrirlo Mark a message as read when opened=Marcar un mensaje como leído al abrirlo
Date format=Formato de fecha Reset=Restablecer
Timezone offset= Settings=Ajustes
Offset to add to server time.= About=Acerca de
Mute list= Ascending=Ascendente
Notifications=Notificaciones Descending=Descendente
Message list=Lista de mensajes From/To=De/Para
Fields=
Spam=
Other=Otros
Date and Time=
years=años years=años
months=meses months=meses
weeks=semanas weeks=semanas
@ -121,8 +95,7 @@ hours=horas
minuts=minutos minuts=minutos
seconds=segundos seconds=segundos
@1 ago=hace @1 @1 ago=hace @1
Restore=Restaurar
Empty=Vacío
##### not used anymore ##### Trash is empty=La papelera está vacía
Date format:=Formato de fecha:
Version=Versión

View file

@ -1,40 +1,35 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=Le mail ne peut pas être envoyé :
You did not specify any valid recipient.=Vous n'avez pas spécifié de destinataire valide.
You have mail! Type /mail to read=Vous avez reçu un mail ! Entrez /mail pour le consulter
You have a new message from @1! Subject: @2=Vous avez un nouveau message de @1 ! Objet : @2
To view it, type /mail=Pour le consulter, entrez /mail
You could also use the button in your inventory.=Vous pouvez également utiliser le bouton dans votre inventaire
Original author=Auteur original
Code=Code
Internationalization=Traduction
Textures=Textures
Audio=Audio
Provided by mt-mods=Fourni par mt-mods Provided by mt-mods=Fourni par mt-mods
Version: @1=Version : @1 Version=Version
Licenses=Licences Licenses=Licences
Expat (code), WTFPL (textures)=Expat (code), WTFPL (textures) Expat (code), WTFPL (textures)=Expat (code), WTFPL (textures)
Communication using this system is NOT guaranteed to be private!=La communication par ce système n'est pas garantie d'être privée ! Communication using this system is NOT guaranteed to be private!=La communication par ce système n'est pas garantie d'être privée !
Admins are able to view the messages of any player.=Les administrateurs peuvent voir les messages de chaque joueur. Admins are able to view the messages of any player.=Les administrateurs peuvent voir les messages de chaque joueur.
Contributors=Contributeurs Contributors=Contributeurs
Group by name=Grouper par nom You have a new message from @1! Subject: @2=Vous avez un nouveau message de @1 ! Objet : @2
Group by contribution=Grouper par contribution To view it, type /mail=Pour le consulter, entrez /mail
Note=Note You could also use the button in your inventory.=Vous pouvez également utiliser le bouton dans votre inventaire
Settings=Paramètres
About=À propos
BCC=Cci BCC=Cci
Cancel=Annuler Cancel=Annuler
Save draft=Enregistrer le brouillon Save draft=Enregistrer le brouillon
Send=Envoyer Send=Envoyer
Subject=Objet No contacts=Aucun contact
To=À
CC=Cc
Delete=Supprimer
New=Nouveau
Edit=Modifier
Back=Retour
Name=Nom Name=Nom
No drafts=Aucun brouillon No drafts=Aucun brouillon
Edit=Modifier
To=À
Player name=Nom du joueur
That name is already in your contacts=Ce nom est déjà dans vos contacts
The contact name cannot be empty.=Le nom du contact ne peut pas être vide.
Note=Note
Maillist name=Nom de la liste de diffusion
Desc=Desc
Players=Joueurs
That name is already in your mailing lists.=Ce nom est déjà présent dans vos listes de diffusion.
The mailing list name cannot be empty.=Le nom de la liste de diffusion ne peut pas être vide.
Back=Retour
Mark Read=Marquer comme lu
Mark Unread=Marquer non lu
Trash=Corbeille Trash=Corbeille
Inbox=Boîte de réception Inbox=Boîte de réception
Outbox=Envoyés Outbox=Envoyés
@ -44,75 +39,55 @@ Mail lists=Listes de diffusion
Options=Options Options=Options
Close=Fermer Close=Fermer
(No subject)=(Sans objet) (No subject)=(Sans objet)
Player name=Nom du joueur Subject=Objet
That name is already in your contacts=Ce nom est déjà dans vos contacts Delete=Supprimer
The contact name cannot be empty.=Le nom du contact ne peut pas être vide. New=Nouveau
Save=Sauvegarder Date=Date
Maillist name=Nom de la liste de diffusion Filter=Filtre
Desc=Desc Allow multiple selection=Autoriser la sélection multiple
Players=Joueurs @1 of @2 selected=@1 sur @2 sélectionnés
That name is already in your mailing lists.=Ce nom est déjà présent dans vos listes de diffusion. (Un)select all=Tout (dé)sélectionner
The mailing list name cannot be empty.=Le nom de la liste de diffusion ne peut pas être vide. No mail=Aucun mail
Mark Read=Marquer comme lu
Mark Unread=Marquer non lu
Mark Spam=Marquer comme spam
Unmark Spam=Marquer non-spam
Reply=Répondre Reply=Répondre
Reply all=Répondre à tous Reply all=Répondre à tous
Forward=Transférer Forward=Transférer
Reply only to the sender=Répondre uniquement à l'expéditeur Reply only to the sender=Répondre uniquement à l'expéditeur
Reply to all involved people=Répondre à toutes les personnes concernées Reply to all involved people=Répondre à toutes les personnes concernées
Transfer message to other people=Transférer le message à d'autres personnes Transfer message to other people=Transférer le message à d'autres personnes
Date=Date
From=De
Filter=Filtre
Allow multiple selection=Autoriser la sélection multiple
@1 of @2 selected=@1 sur @2 sélectionnés
(Un)select all=Tout (dé)sélectionner
No mail=Aucun mail
Read=Lire Read=Lire
Ascending=Croissant From=De
Descending=Décroissant
(No description)=(Sans description) (No description)=(Sans description)
No maillist=Aucune liste de diffusion No maillist=Aucune liste de diffusion
Receivers=Destinataires Receivers=Destinataires
(Un)mute sender=(Dé)mettre en sourdine You have mail! Type /mail to read=Vous avez reçu un mail ! Entrez /mail pour le consulter
Add=Ajouter Add=Ajouter
Remove=Enlever Remove=Enlever
Reset=Réinitialiser CC=Cc
Restore=Restaurer Notifications=Notifications
Empty=Vider
Trash is empty=La corbeille est vide
From/To=De/À
No contacts=Aucun contact
The method of delivery to @1 is invalid.=La méthode d'expédition à @1 est invalide.
The recipient @1 could not be identified.=Le destinataire @1 n'a pas pu être identifié.
@1 rejected your mail.=@1 a rejeté votre mail.
Chat notifications=Notifications dans le tchat Chat notifications=Notifications dans le tchat
Receive a message in the chat when there is a new message=Recevoir un message dans le tchat lorsqu'un nouveau message est reçu
On join notifications=Notifications à la connexion On join notifications=Notifications à la connexion
Receive a message at login when inbox isn't empty=Recevoir un message à la connexion lorsque la boîte de réception n'est pas vide
HUD notifications=Notifications ATH HUD notifications=Notifications ATH
Show an HUD notification when inbox isn't empty=Indiquer dans l'ATH que la boîte de réception n'est pas vide
Sound notifications=Notifications sonores Sound notifications=Notifications sonores
Play a sound when there is a new message=Jouer un son lorsqu'un nouveau message est reçu Message list=Liste de messages
Show unread in different color=Coloriser les non lus Show unread in different color=Coloriser les non lus
Show CC/BCC in different color=Coloriser les Cc/Cci Show CC/BCC in different color=Coloriser les Cc/Cci
Default sorting field=Champ de tri par défaut Default sorting fields=Champs de tri par défaut
Default sorting direction=Direction de tri par défaut Other=Autre
Move deleted messages to trash=Supprimer les messages dans la corbeille Move deleted messages to trash=Supprimer les messages dans la corbeille
Automatic marking read=Lu automatique Automatic marking read=Lu automatique
Date format:=Format de la date :
Receive a message in the chat when there is a new message=Recevoir un message dans le tchat lorsqu'un nouveau message est reçu
Receive a message at login when inbox isn't empty=Recevoir un message à la connexion lorsque la boîte de réception n'est pas vide
Show an HUD notification when inbox isn't empty=Indiquer dans l'ATH que la boîte de réception n'est pas vide
Play a sound when there is a new message=Jouer un son lorsqu'un nouveau message est reçu
Mark a message as read when opened=Marquer un message comme lu lorsqu'il est ouvert Mark a message as read when opened=Marquer un message comme lu lorsqu'il est ouvert
Date format=Format de la date Reset=Réinitialiser
Timezone offset=Compensation horaire Settings=Paramètres
Offset to add to server time.=Écart de temps à ajouter à l'heure du serveur. About=À propos
Mute list=Liste de sourdine Save=Sauvegarder
Notifications=Notifications Ascending=Croissant
Message list=Liste de messages Descending=Décroissant
Fields=Champs From/To=De/À
Spam=Spam
Other=Autre
Date and Time=Date et Heure
years=années years=années
months=mois months=mois
weeks=semaines weeks=semaines
@ -121,3 +96,6 @@ hours=heures
minuts=minutes minuts=minutes
seconds=secondes seconds=secondes
@1 ago=Il y a @1 @1 ago=Il y a @1
Restore=Restaurer
Empty=Vider
Trash is empty=La corbeille est vide

View file

@ -1,40 +1,35 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=
You did not specify any valid recipient.=
You have mail! Type /mail to read=Van egy leveled! Írd /mail az olvasáshoz
You have a new message from @1! Subject: @2=Van egy új üzeneted @1-től Cím: @2
To view it, type /mail=Ahhoz hogy megnézd, írd /mail
You could also use the button in your inventory.=A gombot is tudod használni az inventoridban.
Original author=
Code=
Internationalization=
Textures=
Audio=
Provided by mt-mods=Feltéve, hogy az én mt-mod-om Provided by mt-mods=Feltéve, hogy az én mt-mod-om
Version: @1= Version=Verzió
Licenses=License Licenses=License
Expat (code), WTFPL (textures)=Expat (kód), WTFPL (textúrák) Expat (code), WTFPL (textures)=Expat (kód), WTFPL (textúrák)
Communication using this system is NOT guaranteed to be private!=A systemben lévő komunikáció nem garantáltan privát! Communication using this system is NOT guaranteed to be private!=A systemben lévő komunikáció nem garantáltan privát!
Admins are able to view the messages of any player.=Az adminok megtudják nézni minden játékos üzenetjét. Admins are able to view the messages of any player.=Az adminok megtudják nézni minden játékos üzenetjét.
Contributors=Közreműködöttek Contributors=Közreműködöttek
Group by name= You have a new message from @1! Subject: @2=Van egy új üzeneted @1-től Cím: @2
Group by contribution= To view it, type /mail=Ahhoz hogy megnézd, írd /mail
Note=Jegyzet You could also use the button in your inventory.=A gombot is tudod használni az inventoridban.
Settings=Beállítások
About=Róla
BCC=BCC BCC=BCC
Cancel=Mégse Cancel=Mégse
Save draft=mentés piszkozatként Save draft=mentés piszkozatként
Send=Küldés Send=Küldés
Subject=Cím No contacts=
To=Neki
CC=CC
Delete=Törlés
New=Új
Edit=Szerkesztés
Back=Visza
Name=Név Name=Név
No drafts=Nincsenek piszkozatok No drafts=Nincsenek piszkozatok
Edit=Szerkesztés
To=Neki
Player name=Játékos neve
That name is already in your contacts=A név már a kontaktok között van
The contact name cannot be empty.=A contakt neve nem lehet üres.
Note=Jegyzet
Maillist name=Levelező lista neve
Desc=Desc
Players=Játékosok
That name is already in your mailing lists.=A név már benne van a levelező listában
The mailing list name cannot be empty.=A levelező lista neve nem lehet üres
Back=Visza
Mark Read=Jelöld olvasottként
Mark Unread=Jelöld olvasatlanul
Trash= Trash=
Inbox=PostaLáda Inbox=PostaLáda
Outbox=Elküldött Outbox=Elküldött
@ -44,75 +39,55 @@ Mail lists=Levelező lista
Options=Lehetőségek Options=Lehetőségek
Close=Bezár Close=Bezár
(No subject)=(nincs cím) (No subject)=(nincs cím)
Player name=Játékos neve Subject=Cím
That name is already in your contacts=A név már a kontaktok között van Delete=Törlés
The contact name cannot be empty.=A contakt neve nem lehet üres. New=Új
Save=Mentés Date=Dátum
Maillist name=Levelező lista neve Filter=Filterek
Desc=Desc Allow multiple selection=
Players=Játékosok @1 of @2 selected=
That name is already in your mailing lists.=A név már benne van a levelező listában (Un)select all=(ne válaszd ki) mindegyik választása
The mailing list name cannot be empty.=A levelező lista neve nem lehet üres No mail=Nincs levél
Mark Read=Jelöld olvasottként
Mark Unread=Jelöld olvasatlanul
Mark Spam=
Unmark Spam=
Reply=Válasz Reply=Válasz
Reply all=Válaszmindenkinek Reply all=Válaszmindenkinek
Forward=Továbbítás Forward=Továbbítás
Reply only to the sender= Reply only to the sender=
Reply to all involved people= Reply to all involved people=
Transfer message to other people= Transfer message to other people=
Date=Dátum
From=Tőle
Filter=Filterek
Allow multiple selection=
@1 of @2 selected=
(Un)select all=(ne válaszd ki) mindegyik választása
No mail=Nincs levél
Read=Olvasott Read=Olvasott
Ascending=Emelkedő From=Tőle
Descending=Sűlyedő
(No description)=(Nincs leírás) (No description)=(Nincs leírás)
No maillist=Nincs levelező lista No maillist=Nincs levelező lista
Receivers= Receivers=
(Un)mute sender= You have mail! Type /mail to read=Van egy leveled! Írd /mail az olvasáshoz
Add=Hozzáadás Add=Hozzáadás
Remove=Elvétel Remove=Elvétel
Reset=Viszaállítás CC=CC
Restore= Notifications=Értesítések
Empty=
Trash is empty=
From/To=Tól(től)/neki
No contacts=
The method of delivery to @1 is invalid.=
The recipient @1 could not be identified.=
@1 rejected your mail.=
Chat notifications=Chates értesítések Chat notifications=Chates értesítések
Receive a message in the chat when there is a new message=
On join notifications=Belépési értesírés On join notifications=Belépési értesírés
Receive a message at login when inbox isn't empty=
HUD notifications= HUD értesítés HUD notifications= HUD értesítés
Show an HUD notification when inbox isn't empty=
Sound notifications=Hang értesítés Sound notifications=Hang értesítés
Play a sound when there is a new message= Message list=Üzenetek listája
Show unread in different color=Mutasd a nem olvasottakat más színnel Show unread in different color=Mutasd a nem olvasottakat más színnel
Show CC/BCC in different color=Mutasd a CC-t/BCC-t más színnel Show CC/BCC in different color=Mutasd a CC-t/BCC-t más színnel
Default sorting field=Alap válogató terület Default sorting fields=Alap válogató terület
Default sorting direction= Other=
Move deleted messages to trash= Move deleted messages to trash=
Automatic marking read= Automatic marking read=
Date format:=
Receive a message in the chat when there is a new message=
Receive a message at login when inbox isn't empty=
Show an HUD notification when inbox isn't empty=
Play a sound when there is a new message=
Mark a message as read when opened= Mark a message as read when opened=
Date format= Reset=Viszaállítás
Timezone offset= Settings=Beállítások
Offset to add to server time.= About=Róla
Mute list= Save=Mentés
Notifications=Értesítések Ascending=Emelkedő
Message list=Üzenetek listája Descending=Sűlyedő
Fields= From/To=Tól(től)/neki
Spam=
Other=
Date and Time=
years= years=
months= months=
weeks= weeks=
@ -121,8 +96,6 @@ hours=
minuts= minuts=
seconds= seconds=
@1 ago= @1 ago=
Restore=
Empty=
##### not used anymore ##### Trash is empty=
Version=Verzió

View file

@ -1,128 +0,0 @@
# textdomain: mail
The mail could not be sent:=
You did not specify any valid recipient.=
You have mail! Type /mail to read=Anda memiliki surel! ketik /mail untuk membaca
You have a new message from @1! Subject: @2=Anda memiliki pesan baru dari @1! Subjek: @2
To view it, type /mail=Untuk melihatnya, ketik /mail
You could also use the button in your inventory.=Anda juga dapat menggunakan tombol dalam inventaris Anda.
Original author=
Code=
Internationalization=
Textures=
Audio=
Provided by mt-mods=Disediakan oleh mt-mods
Version: @1=
Licenses=Lisensi
Expat (code), WTFPL (textures)=Expat (kode), WTFPL (tekstur)
Communication using this system is NOT guaranteed to be private!=Komunikasi dengan sistem ini TIDAK dijamin bersifat pribadi!
Admins are able to view the messages of any player.=Admin dapat melihat pesan dari setiap pemain.
Contributors=Kontributor
Group by name=
Group by contribution=
Note=Catatan
Settings=Pengaturan
About=Tentang
BCC=BCC
Cancel=Batal
Save draft=Simpan Draf
Send=Kirim
Subject=Subjek
To=Kpd
CC=CC
Delete=Hapus
New=Baru
Edit=Sunting
Back=Kembali
Name=Nama
No drafts=Tidak ada draf
Trash=Sampah
Inbox=Kotak Masuk
Outbox=Kotak Keluar
Drafts=Draf
Contacts=Kontak
Mail lists=Milis
Options=Pengaturan
Close=Tutup
(No subject)=(Tanpa subjek)
Player name=Nama pemain
That name is already in your contacts=Nama itu sudah ada dalam kontak Anda
The contact name cannot be empty.=Nama kontak tidak boleh kosong.
Save=Simpan
Maillist name=Nama milis
Desc=Deskripsi
Players=Pemain
That name is already in your mailing lists.=Nama tersebut sudah ada dalam milis Anda.
The mailing list name cannot be empty.=Nama milis tidak boleh kosong.
Mark Read=Tndai Sdh Dibaca
Mark Unread=Tndai Blm Dibaca
Mark Spam=
Unmark Spam=
Reply=Balas
Reply all=Balas Semua
Forward=Teruskan
Reply only to the sender=Balas hanya kepada pengirim
Reply to all involved people=Balas kepada semua orang yang terlibat
Transfer message to other people=Teruskan pesan kepada orang lain
Date=Tanggal
From=Dari
Filter=Saring
Allow multiple selection=Izinkan beberapa pilihan
@1 of @2 selected=@1 dari @2 dipilih
(Un)select all=Batal/Pilih Semua
No mail=Tidak ada surat
Read=Baca
Ascending=Menaik
Descending=Menurun
(No description)=(Tidak ada deskripsi)
No maillist=Tidak ada milis
Receivers=Penerima
(Un)mute sender=
Add=Tambah
Remove=Hapus
Reset=Atur Ulang
Restore=Kembalikan
Empty=Kosong
Trash is empty=Sampah kosong
From/To=Dari/Kpd
No contacts=Tidak ada kontak
The method of delivery to @1 is invalid.=
The recipient @1 could not be identified.=
@1 rejected your mail.=
Chat notifications=Pemberitahuan obrolan
Receive a message in the chat when there is a new message=Terima pesan dalam obrolan ketika ada pesan baru
On join notifications=Pemberitahuan saat bergabung
Receive a message at login when inbox isn't empty=Terima pesan saat masuk log ketika kotak masuk tidak kosong
HUD notifications=Pemberitahuan HUD
Show an HUD notification when inbox isn't empty=Tampilkan pemberitahuan HUD saat kotak masuk tidak kosong
Sound notifications=Pemberitahuan suara
Play a sound when there is a new message=Putar suara saat ada pesan baru
Show unread in different color=Tampilkan belum dibaca dengan warna berbeda
Show CC/BCC in different color=Tampilkan CC/BCC dengan warna berbeda
Default sorting field=Kolom pengurutan bawaan
Default sorting direction=Arah pengurutan bawaan
Move deleted messages to trash=Pindahkan pesan yang dihapus ke sampah
Automatic marking read=Penandaan otomatis sudah dibaca
Mark a message as read when opened=Tandai pesan sebagai sudah dibaca saat dibuka
Date format=Format tanggal
Timezone offset=
Offset to add to server time.=
Mute list=
Notifications=Pemberitahuan
Message list=Daftar Pesan
Fields=
Spam=
Other=Lain-Lain
Date and Time=
years=tahun
months=bulan
weeks=pekan
days=hari
hours=jam
minuts=menit
seconds=detik
@1 ago=@1 yang lalu
##### not used anymore #####
Version=Versi

View file

@ -1,40 +1,35 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=
You did not specify any valid recipient.=
You have mail! Type /mail to read=Você recebeu e-mail! Tecle /mail para ler
You have a new message from @1! Subject: @2=Você tem uma mensagem de @1! Assunto: @2
To view it, type /mail=Para visualizar a mensagem, digite /mail
You could also use the button in your inventory.=Você também pode usar o botão do seu inventário.
Original author=
Code=
Internationalization=
Textures=
Audio=
Provided by mt-mods= Provided by mt-mods=
Version: @1= Version=
Licenses= Licenses=
Expat (code), WTFPL (textures)= Expat (code), WTFPL (textures)=
Communication using this system is NOT guaranteed to be private!=A comunicação usando este sistema não possui garantia de privacidade Communication using this system is NOT guaranteed to be private!=A comunicação usando este sistema não possui garantia de privacidade
Admins are able to view the messages of any player.=Administradores poderão ler as mensagens de qualquer jogador Admins are able to view the messages of any player.=Administradores poderão ler as mensagens de qualquer jogador
Contributors= Contributors=
Group by name= You have a new message from @1! Subject: @2=Você tem uma mensagem de @1! Assunto: @2
Group by contribution= To view it, type /mail=Para visualizar a mensagem, digite /mail
Note=Nota You could also use the button in your inventory.=Você também pode usar o botão do seu inventário.
Settings=Ajustes
About=Sobre
BCC=BCC BCC=BCC
Cancel=Cancelar Cancel=Cancelar
Save draft=Salvar rascunho Save draft=Salvar rascunho
Send=Enviar Send=Enviar
Subject=Assunto No contacts=
To=Para
CC=CC
Delete=Apagar
New=Novo
Edit=Editar
Back=Voltar
Name=Nome Name=Nome
No drafts=Sem rascunhos No drafts=Sem rascunhos
Edit=Editar
To=Para
Player name=Nome do jogador
That name is already in your contacts=Esse nome já consta em sua lista de contatos
The contact name cannot be empty.=Informe o nome do destinatário
Note=Nota
Maillist name=Nome da lista de discussão
Desc=Descrição
Players=Jogador
That name is already in your mailing lists.=Esse nome ja está sendo usado em sua lista de discussões
The mailing list name cannot be empty.=O nome da lista de discussões deve ser informado
Back=Voltar
Mark Read=Marcar como lido
Mark Unread=Marcar como não lido
Trash= Trash=
Inbox=Entrada Inbox=Entrada
Outbox=Enviadas Outbox=Enviadas
@ -44,75 +39,55 @@ Mail lists=Lista de correios
Options=Opções Options=Opções
Close=Fechar Close=Fechar
(No subject)=(Sem assunto) (No subject)=(Sem assunto)
Player name=Nome do jogador Subject=Assunto
That name is already in your contacts=Esse nome já consta em sua lista de contatos Delete=Apagar
The contact name cannot be empty.=Informe o nome do destinatário New=Novo
Save=Salvar Date=Data
Maillist name=Nome da lista de discussão Filter=Filtrar
Desc=Descrição Allow multiple selection=Permitir selecionar vários
Players=Jogador @1 of @2 selected=
That name is already in your mailing lists.=Esse nome ja está sendo usado em sua lista de discussões (Un)select all=Desmarcar todos
The mailing list name cannot be empty.=O nome da lista de discussões deve ser informado No mail=Sem e-mails no momento
Mark Read=Marcar como lido
Mark Unread=Marcar como não lido
Mark Spam=
Unmark Spam=
Reply=Responder Reply=Responder
Reply all=Responder Todos Reply all=Responder Todos
Forward=Encaminhar Forward=Encaminhar
Reply only to the sender= Reply only to the sender=
Reply to all involved people= Reply to all involved people=
Transfer message to other people= Transfer message to other people=
Date=Data
From=De
Filter=Filtrar
Allow multiple selection=Permitir selecionar vários
@1 of @2 selected=
(Un)select all=Desmarcar todos
No mail=Sem e-mails no momento
Read=Ler Read=Ler
Ascending=Ascendente From=De
Descending=Descendente
(No description)=(sem descrição) (No description)=(sem descrição)
No maillist=Sem lista de discussão No maillist=Sem lista de discussão
Receivers= Receivers=
(Un)mute sender= You have mail! Type /mail to read=Você recebeu e-mail! Tecle /mail para ler
Add=Adicionar Add=Adicionar
Remove=Remover Remove=Remover
Reset= CC=CC
Restore= Notifications=Notificações
Empty=
Trash is empty=
From/To=De/Para
No contacts=
The method of delivery to @1 is invalid.=
The recipient @1 could not be identified.=
@1 rejected your mail.=
Chat notifications=Notificação de conversa Chat notifications=Notificação de conversa
Receive a message in the chat when there is a new message=
On join notifications=Notificação ao entrar On join notifications=Notificação ao entrar
Receive a message at login when inbox isn't empty=
HUD notifications=Notificação no HUD HUD notifications=Notificação no HUD
Show an HUD notification when inbox isn't empty=
Sound notifications= Sound notifications=
Play a sound when there is a new message= Message list=Lista de mensagens
Show unread in different color=Exibir mensagens não lidas em uma cor diferente Show unread in different color=Exibir mensagens não lidas em uma cor diferente
Show CC/BCC in different color=Exibir mensagens com copia em uma cor diferente Show CC/BCC in different color=Exibir mensagens com copia em uma cor diferente
Default sorting field=Ordenamento de campo padrão Default sorting fields=Ordenamento de campos padrão
Default sorting direction= Other=
Move deleted messages to trash= Move deleted messages to trash=
Automatic marking read= Automatic marking read=
Date format:=
Receive a message in the chat when there is a new message=
Receive a message at login when inbox isn't empty=
Show an HUD notification when inbox isn't empty=
Play a sound when there is a new message=
Mark a message as read when opened= Mark a message as read when opened=
Date format= Reset=
Timezone offset= Settings=Ajustes
Offset to add to server time.= About=Sobre
Mute list= Save=Salvar
Notifications=Notificações Ascending=Ascendente
Message list=Lista de mensagens Descending=Descendente
Fields= From/To=De/Para
Spam=
Other=
Date and Time=
years= years=
months= months=
weeks= weeks=
@ -121,3 +96,6 @@ hours=
minuts= minuts=
seconds= seconds=
@1 ago= @1 ago=
Restore=
Empty=
Trash is empty=

View file

@ -1,123 +0,0 @@
# textdomain: mail
The mail could not be sent:=Невозможно отправить почту:
You did not specify any valid recipient.=Вы не указали получателя.
You have mail! Type /mail to read=У вас есть почта! Наберите /mail, чтобы прочитать
You have a new message from @1! Subject: @2=У вас новое сообщение от @1! Тема: @2
To view it, type /mail=Чтобы посмотреть, наберите /mail
You could also use the button in your inventory.=Вы также можете использовать кнопку в Вашем инвентаре.
Original author=Автор оригинала
Code=Код
Internationalization=Перевод
Textures=Текстуры
Audio=Аудио
Provided by mt-mods=Предоставлено mt-mods
Version: @1=Версия: @1
Licenses=Лицензии
Expat (code), WTFPL (textures)=Expat (код), WTFPL (текстуры)
Communication using this system is NOT guaranteed to be private!=Конфиденциальность общения с использованием этой системы НЕ гарантируется!
Admins are able to view the messages of any player.=Админ может читать сообщения любых игроков.
Contributors=Участники
Group by name=Группировать по имени
Group by contribution=Группировать по участию
Note=Заметка
Settings=Настройки
About=О...
BCC=С.копия
Cancel=Отмена
Save draft=Сохр. черновик
Send=Отправить
Subject=Тема
To=Кому
CC=Копия
Delete=Удалить
New=Новое
Edit=Изменить
Back=Назад
Name=Имя
No drafts=Нет черновиков
Trash=Корзина
Inbox=Входящие
Outbox=Исходящие
Drafts=Черновики
Contacts=Контакты
Mail lists=Списки рассылки
Options=Опции
Close=Закрыть
(No subject)=(без темы)
Player name=Имя игрока
That name is already in your contacts=Это имя уже есть в Ваших контактах
The contact name cannot be empty.=Имя контакта не может быть пустым.
Save=Сохранить
Maillist name=Название списка
Desc=Описание
Players=Игроки
That name is already in your mailing lists.=Это имя уже есть в ваших списках рассылки.
The mailing list name cannot be empty.=Название списка не может быть пустым.
Mark Read=Отм. прочитано
Mark Unread=Отм. непрочитано
Mark Spam=Отм. спам
Unmark Spam=Снять отм. спам
Reply=Ответить
Reply all=Ответить всем
Forward=Переслать
Reply only to the sender=Ответить только отправителю
Reply to all involved people=Ответить всем участникам
Transfer message to other people=Переслать сообщение другим игрокам
Date=Дата
From=От
Filter=Фильтр
Allow multiple selection=Разрешить выбор нескольких
@1 of @2 selected=@1 из @2 выбраны
(Un)select all=Снять выбор со всех
No mail=Нет почты
Read=Прочитано
Ascending=Возрастание
Descending=Убывание
(No description)=(Нет описания)
No maillist=Не список рассылки
Receivers=Получатели
(Un)mute sender=Вкл. звук для отправителя
Add=Добавить
Remove=Удалить
Reset=Сбросить
Restore=Восстановить
Empty=Очистить
Trash is empty=Корзина пуста
From/To=От/Кому
No contacts=Нет контактов
The method of delivery to @1 is invalid.=Метод доставки для @1 не действителен.
The recipient @1 could not be identified.=Невозможно идентифицировать получателя @1.
@1 rejected your mail.=@1 отклонил ваше письмо.
Chat notifications=Уведомления в чате
Receive a message in the chat when there is a new message=Получать сообщение в чате, когда приходит новое сообщение
On join notifications=Уведомления при присоединении
Receive a message at login when inbox isn't empty=Получать сообщение при входе, когда есть письма во Входящих
HUD notifications=HUD уведомления
Show an HUD notification when inbox isn't empty=Показывать уведомление HUD, если папка «Входящие» не пуста
Sound notifications=Звуковые уведомления
Play a sound when there is a new message=Проигрывать звук, когда приходит новое сообщение
Show unread in different color=Показывать не прочтенные други цветом
Show CC/BCC in different color=Показывать Копию/Скрытую копию другим цветом
Default sorting field=Поле для сортировки по умолчанию
Default sorting direction=Направление сортировки по умолчанию
Move deleted messages to trash=Перемещать удаленные сообщения в корзину
Automatic marking read=Автоматически отмечать прочтение
Mark a message as read when opened=Отмечать сообщение как прочитанное при открытии
Date format=Формат даты
Timezone offset=Временная зона
Offset to add to server time.=Добавлять ко времени сервера
Mute list=Заглушить список
Notifications=Уведомления
Message list=Список сообщений
Fields=Поля
Spam=Спам
Other=Другое
Date and Time=Дата и Время
years=лет
months=месяцев
weeks=недель
days=дней
hours=часов
minuts=минут
seconds=секунд
@1 ago=@1 назад

View file

@ -1,123 +0,0 @@
# textdomain: mail
The mail could not be sent:=Неможливо відправити пошту:
You did not specify any valid recipient.=Ви не вказали отримувача.
You have mail! Type /mail to read=У вас є пошта! Введіть /mail для прочитання
You have a new message from @1! Subject: @2=У вас нове повідомлення від @1! Тема: @2
To view it, type /mail=Введіть /mail аби прочитати це
You could also use the button in your inventory.=Також ви можете використовувати кнопку в вашому інвентарі.
Original author=Автор оригіналу
Code=Код
Internationalization=Переклад
Textures=Текстури
Audio=Аудіо
Provided by mt-mods=Надано mt-mods
Version: @1=Версія: @1
Licenses=Ліцензії
Expat (code), WTFPL (textures)=Expat (код), WTFPL (текстури)
Communication using this system is NOT guaranteed to be private!=Конфіденційність використання цієї системи НЕ гарантовано є приватною!
Admins are able to view the messages of any player.=Адмін може читати повідомлення всіх гравців.
Contributors=Учасники
Group by name=Групувати по імені
Group by contribution=Групувати по участю
Note=Замітка
Settings=Налаштування
About=Про...
BCC=ВСС
Cancel=Скасувати
Save draft=Збер.чернетку
Send=Відправити
Subject=Тема
To=Кому
CC=Копія
Delete=Видалити
New=Створити
Edit=Редагувати
Back=Назад
Name=Ім'я
No drafts=Немає чернеток
Trash=Кошик
Inbox=Вхідні
Outbox=Вихідні
Drafts=Чернетки
Contacts=Контакти
Mail lists=Списки розсилки
Options=Опції
Close=Закрити
(No subject)=(без теми)
Player name=Ім'я гравця
That name is already in your contacts=Це ім'я вже збережено у ваших контактах
The contact name cannot be empty.=Ім'я контакта не може бути пустим.
Save=Зберегти
Maillist name=Назва списку
Desc=Опис
Players=Гравці
That name is already in your mailing lists.=Це ім'я вже є у ваших списках розсилки.
The mailing list name cannot be empty.=Назва списка не може бути пустою.
Mark Read=Прочитано
Mark Unread=Непрочитано
Mark Spam=Відм. спам
Unmark Spam=Скас. спам
Reply=Відповісти
Reply all=Відповісти усім
Forward=Переслати
Reply only to the sender=Відповісти лише відправнику
Reply to all involved people=Відповісти усім учасникам
Transfer message to other people=Переслати це повідомлення іншим людям
Date=Дата
From=Від
Filter=Фільтр
Allow multiple selection=Дозволити вибір декількох
@1 of @2 selected=@1 з @2 вибрані
(Un)select all=Зняти вибір з усіх
No mail=Немає пошти
Read=Прочитано
Ascending=Зростання
Descending=Зменшення
(No description)=(Немає опису)
No maillist=Немає списків розсилки
Receivers=Отримувачі
(Un)mute sender=Увімк. звук для відправника
Add=Додати
Remove=Видалити
Reset=Скинути
Restore=Відновити
Empty=Очистити
Trash is empty=Кошик пустий
From/To=Від/До
No contacts=Немає контактів
The method of delivery to @1 is invalid.=Метод доставки для @1 не дійсний.
The recipient @1 could not be identified.=Не знайдено @1.
@1 rejected your mail.=@1 відхилив ваше лист.
Chat notifications=Повідомлення у чаті
Receive a message in the chat when there is a new message=Отримувати повідомленння у чаті при отриманні нового повідомлення
On join notifications=Повідомлення при приєднанні
Receive a message at login when inbox isn't empty=Отримувати повідомлення при приєднанні коли у теці «Вхідні» є нові листи
HUD notifications=HUD повідомлення
Show an HUD notification when inbox isn't empty=Показувати HUD повідомлення коли у теці «Вхідні» є нові листи
Sound notifications=Звукові повідомлення
Play a sound when there is a new message=Програвати звук при новому повідомленні
Show unread in different color=Показувати непрочитані іншим кольором
Show CC/BCC in different color=Показувати копію/приховану копію іншим кольором
Default sorting field=Поле для сортування за замовч.
Default sorting direction=Напрям сортування за замовч.
Move deleted messages to trash=Переміщати видалені повідомлення до кошика
Automatic marking read=Автоматично відмічати прочитані
Mark a message as read when opened=Відмічати повідомлення як прочитане, коли відкрито
Date format=Формат дати
Timezone offset=Часовий пояс
Offset to add to server time.=Додавати до часу сервера.
Mute list=Заглушити список
Notifications=Повідомлення
Message list=Список повідомлень
Fields=Поля
Spam=Спам
Other=Інше
Date and Time=Дата й час
years=років
months=місяців
weeks=тижнів
days=днів
hours=годин
minuts=хвилин
seconds=секунд
@1 ago=@1 тому

View file

@ -1,129 +1,102 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=无法发送邮件: Provided by mt-mods=
You did not specify any valid recipient.= Version=
You have mail! Type /mail to read=您有新邮件,请使用 /mail 查看。 Licenses=
You have a new message from @1! Subject: @2=您有一封来自 @1 的新邮件,主题为“@2”。 Expat (code), WTFPL (textures)=
To view it, type /mail=请使用 /mail 命令查看。 Communication using this system is NOT guaranteed to be private!=
You could also use the button in your inventory.=您也可以使用物品清单里的按键。 Admins are able to view the messages of any player.=
Original author= Contributors=
Code= You have a new message from @1! Subject: @2=
Internationalization= To view it, type /mail=
Textures= You could also use the button in your inventory.=
Audio=
Provided by mt-mods=由 mt-mods 提供
Version: @1=
Licenses=许可证
Expat (code), WTFPL (textures)=Expat代码WTFPL材质
Communication using this system is NOT guaranteed to be private!=这个系统不适用于私密沟通!
Admins are able to view the messages of any player.=管理员可以查看所有玩家的邮件。
Contributors=贡献者
Group by name=
Group by contribution=
Note=备注
Settings=设置
About=关于
BCC=密送 BCC=密送
Cancel=取消 Cancel=取消
Save draft=保存草稿 Save draft=保存草稿
Send=发送 Send=发送
Subject=主题 No contacts=
To=收件人
CC=抄送
Delete=删除
New=新
Edit=编辑
Back=返回
Name=名字 Name=名字
#if new means new mail, it would be New=新邮件 #if new means new mail, it would be New=新邮件
No drafts=没有草稿 No drafts=没有草稿
Trash=垃圾箱 Edit=编辑
To=收件人
Player name=玩家名字
That name is already in your contacts=
The contact name cannot be empty.=
Note=备注
Maillist name=建组名
Desc=描述
Players=玩家
That name is already in your mailing lists.=
The mailing list name cannot be empty.=
Back=返回
Mark Read=标记为已读
Mark Unread=标记为未读
Trash=
Inbox=收件箱 Inbox=收件箱
Outbox=已发送 Outbox=已发送
Drafts=草稿 Drafts=草稿
Contacts=通讯录 Contacts=通讯录
Mail lists=建组 Mail lists=建组
Options=选项 Options=
Close=关闭 Close=关闭
(No subject)=(无主题) (No subject)=(无主题)
Player name=玩家名字 Subject=主题
That name is already in your contacts=这个玩家已经在您的通讯录里。 Delete=删除
The contact name cannot be empty.=联系人名字不能为空。 New=新
Save=保存 Date=时间
Maillist name=邮件列表名称 Filter=筛选
Desc=描述 Allow multiple selection=允许多选
Players=玩家 @1 of @2 selected=
That name is already in your mailing lists.=这个玩家已经在您的邮件列表里。 (Un)select all=(取消)选中所有
The mailing list name cannot be empty.=邮件列表名称不能为空。 No mail=无邮件
Mark Read=标记为已读
Mark Unread=标记为未读
Mark Spam=标记为垃圾邮件
Unmark Spam=取消标记为垃圾邮件
Reply=回复 Reply=回复
Reply all=回复所有 Reply all=回复所有
Forward=转发 Forward=转发
Reply only to the sender=只回复给发件人 Reply only to the sender=
Reply to all involved people=回复给所有人 Reply to all involved people=
Transfer message to other people=将邮件转发给其他人 Transfer message to other people=
Date=时间
From=发件人
Filter=筛选
Allow multiple selection=允许多选
@1 of @2 selected=已选中 @2 项中的 @1 项
(Un)select all=(取消)选中所有
No mail=无邮件
Read=浏览 Read=浏览
Ascending=升序 From=发件人
Descending=降序 (No description)=(无描述)
(No description)=(无描述) No maillist=无建组
No maillist=无邮件列表 Receivers=
Receivers=收件人 You have mail! Type /mail to read=
(Un)mute sender=屏蔽或取消屏蔽发件人
Add=添加 Add=添加
Remove=移除 Remove=移除
Reset=重置 CC=抄送
Restore=恢复 Notifications=
Empty=空的 Chat notifications=
Trash is empty=垃圾箱为空 On join notifications=
From/To=发件人或收件人 HUD notifications=
No contacts=无联系人 Sound notifications=
The method of delivery to @1 is invalid.=无法将邮件发送给 @1。 Message list=
The recipient @1 could not be identified.=无法找到收件人“@1”。 Show unread in different color=
@1 rejected your mail.=@1 不接收您的邮件。 Show CC/BCC in different color=
Chat notifications=在聊天记录中显示通知 Default sorting fields=
Receive a message in the chat when there is a new message=收到新邮件时在聊天记录中显示通知。 Other=
On join notifications=加入服务器时显示通知 Move deleted messages to trash=
Receive a message at login when inbox isn't empty=加入服务器且由新邮件时显示通知。 Automatic marking read=
HUD notifications=HUD 通知 Date format:=
Show an HUD notification when inbox isn't empty=收到新邮件时通过 HUD 显示通知。 Receive a message in the chat when there is a new message=
Sound notifications=提示音 Receive a message at login when inbox isn't empty=
Play a sound when there is a new message=收到新邮件时播放提示音 Show an HUD notification when inbox isn't empty=
Show unread in different color=使用不同的颜色标记未读邮件 Play a sound when there is a new message=
Show CC/BCC in different color=使用不同的颜色标记抄送和密送邮件 Mark a message as read when opened=
Default sorting field=排序依据 Reset=
Default sorting direction=排序方向 Settings=
Move deleted messages to trash=将已删除的邮件移至垃圾箱 About=关于
Automatic marking read=自动将邮件标记为已读 Save=保存
Mark a message as read when opened=打开邮件时自动将邮件标记为已读 Ascending=升序
Date format=日期格式 Descending=降序
Timezone offset= From/To=
Offset to add to server time.= years=
Mute list=屏蔽列表 months=
Notifications=通知 weeks=
Message list=邮件列表 days=
Fields= hours=
Spam=垃圾邮件 minuts=
Other=其它 seconds=
Date and Time= @1 ago=
years=年 Restore=
months=月 Empty=
weeks=周 Trash is empty=
days=天
hours=小时
minuts=分钟
seconds=秒
@1 ago=@1前
##### not used anymore #####
Version=版本

View file

@ -1,128 +1,101 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=無法發送郵件: Provided by mt-mods=
You did not specify any valid recipient.= Version=
You have mail! Type /mail to read=您有新郵件,請使用 /mail 查看。 Licenses=
You have a new message from @1! Subject: @2=您有一封來自 @1 的新郵件,主題為“@2”。 Expat (code), WTFPL (textures)=
To view it, type /mail=請使用 /mail 指令查看。 Communication using this system is NOT guaranteed to be private!=
You could also use the button in your inventory.=您也可以使用物品欄裡的按鍵。 Admins are able to view the messages of any player.=
Original author= Contributors=
Code= You have a new message from @1! Subject: @2=
Internationalization= To view it, type /mail=
Textures= You could also use the button in your inventory.=
Audio=
Provided by mt-mods=由 mt-mods 提供
Version: @1=
Licenses=許可證
Expat (code), WTFPL (textures)=Expat源碼WTFPL材質
Communication using this system is NOT guaranteed to be private!=此系統不適合私密交流!
Admins are able to view the messages of any player.=管理員可以查看所有玩家的郵件。
Contributors=貢獻者
Group by name=
Group by contribution=
Note=備註
Settings=設置
About=關於
BCC=密件副本 BCC=密件副本
Cancel=取消 Cancel=取消
Save draft=儲存草稿 Save draft=儲存草稿
Send=發送 Send=發送
Subject=主旨 No contacts=
To=收件人
CC=副本
Delete=刪除
New=新建
Edit=編輯
Back=返回
Name=名稱 Name=名稱
No drafts=沒有草稿 No drafts=沒有草稿
Trash=垃圾箱 Edit=編輯
To=收件人
Player name=玩家名稱
That name is already in your contacts=
The contact name cannot be empty.=
Note=備註
Maillist name=郵件列表名稱
Desc=描述
Players=玩家
That name is already in your mailing lists.=
The mailing list name cannot be empty.=
Back=返回
Mark Read=標記已讀
Mark Unread=標記未讀
Trash=
Inbox=收件箱 Inbox=收件箱
Outbox=寄件備份 Outbox=寄件備份
Drafts=草稿 Drafts=草稿
Contacts=聯繫人 Contacts=聯繫人
Mail lists=郵件列表 Mail lists=郵件列表
Options=選項 Options=
Close=關閉 Close=關閉
(No subject)=(沒有主旨) (No subject)=(沒有主旨)
Player name=玩家名稱 Subject=主旨
That name is already in your contacts=玩家已經在您的通訊錄中。 Delete=刪除
The contact name cannot be empty.=聯繫人名字不能為空。 New=新建
Save=儲存 Date=日期
Maillist name=郵件列表名稱 Filter=
Desc=描述 Allow multiple selection=
Players=玩家 @1 of @2 selected=
That name is already in your mailing lists.=玩家已經在您的郵件列表中。 (Un)select all=
The mailing list name cannot be empty.=郵件列表名稱不能為空。 No mail=沒有郵件
Mark Read=標記已讀
Mark Unread=標記未讀
Mark Spam=標記垃圾郵件
Unmark Spam=取消標記垃圾郵件
Reply=回覆 Reply=回覆
Reply all=回覆所有人 Reply all=回覆所有人
Forward=轉寄 Forward=轉寄
Reply only to the sender=僅回覆給寄件人 Reply only to the sender=
Reply to all involved people=回覆給所有人 Reply to all involved people=
Transfer message to other people=將郵件轉發給所有人 Transfer message to other people=
Date=日期
From=寄件人
Filter=篩選
Allow multiple selection=允許多選
@1 of @2 selected=已選擇 @2 項中的 @1 項。
(Un)select all=(取消)選中所有
No mail=沒有郵件
Read=閱讀 Read=閱讀
Ascending= From=寄件者
Descending=
(No description)=(沒有描述) (No description)=(沒有描述)
No maillist=沒有郵件列表 No maillist=沒有郵件列表
Receivers=收件人 Receivers=
(Un)mute sender=(取消)屏蔽寄件人 You have mail! Type /mail to read=
Add=加入 Add=加入
Remove=移除 Remove=移除
Reset=重置 CC=副本
Restore=恢復 Notifications=
Empty=空 Chat notifications=
Trash is empty=垃圾箱為空 On join notifications=
From/To=寄件人或收件人 HUD notifications=
No contacts=沒有聯繫人 Sound notifications=
The method of delivery to @1 is invalid.=無法將郵件發送給 @1。 Message list=
The recipient @1 could not be identified.=無法找到收件人“@1”。 Show unread in different color=
@1 rejected your mail.=@1 不接收您的郵件 Show CC/BCC in different color=
Chat notifications=在聊天室中顯示通知 Default sorting fields=
Receive a message in the chat when there is a new message=收到新郵件時在聊天室中顯示通知。 Other=
On join notifications=加入伺服器時顯示通知 Move deleted messages to trash=
Receive a message at login when inbox isn't empty=加入伺服器且有新郵件時顯示通知。 Automatic marking read=
HUD notifications=HUD 通知 Date format:=
Show an HUD notification when inbox isn't empty=收到新郵件時在 HUD 中顯示通知。 Receive a message in the chat when there is a new message=
Sound notifications=提示聲 Receive a message at login when inbox isn't empty=
Play a sound when there is a new message=收到新郵件時播放提示聲 Show an HUD notification when inbox isn't empty=
Show unread in different color=使用不同的顏色標記未讀郵件 Play a sound when there is a new message=
Show CC/BCC in different color=使用不同的顏色標記副本和密件副本 Mark a message as read when opened=
Default sorting field=排序方式 Reset=
Default sorting direction=排序順序 Settings=
Move deleted messages to trash=將已刪除的郵件移至垃圾箱 About=關於
Automatic marking read=自動將郵件標記已讀 Save=儲存
Mark a message as read when opened=打開郵件時自動將郵件標記已讀 Ascending=
Date format=日期格式 Descending=
Timezone offset= From/To=
Offset to add to server time.= years=
Mute list=屏蔽列表 months=
Notifications=通知 weeks=
Message list=郵件列表 days=
Fields= hours=
Spam=垃圾郵件 minuts=
Other=其他 seconds=
Date and Time= @1 ago=
years=年 Restore=
months=月 Empty=
weeks=周 Trash is empty=
days=日
hours=小時
minuts=分鐘
seconds=秒
@1 ago=@1前
##### not used anymore #####
Version=版本

View file

@ -1,40 +1,35 @@
# textdomain: mail # textdomain: mail
The mail could not be sent:=
You did not specify any valid recipient.=
You have mail! Type /mail to read=
You have a new message from @1! Subject: @2=
To view it, type /mail=
You could also use the button in your inventory.=
Original author=
Code=
Internationalization=
Textures=
Audio=
Provided by mt-mods= Provided by mt-mods=
Version: @1= Version=
Licenses= Licenses=
Expat (code), WTFPL (textures)= Expat (code), WTFPL (textures)=
Communication using this system is NOT guaranteed to be private!= Communication using this system is NOT guaranteed to be private!=
Admins are able to view the messages of any player.= Admins are able to view the messages of any player.=
Contributors= Contributors=
Group by name= You have a new message from @1! Subject: @2=
Group by contribution= To view it, type /mail=
Note= You could also use the button in your inventory.=
Settings=
About=
BCC= BCC=
Cancel= Cancel=
Save draft= Save draft=
Send= Send=
Subject= No contacts=
To=
CC=
Delete=
New=
Edit=
Back=
Name= Name=
No drafts= No drafts=
Edit=
To=
Player name=
That name is already in your contacts=
The contact name cannot be empty.=
Note=
Maillist name=
Desc=
Players=
That name is already in your mailing lists.=
The mailing list name cannot be empty.=
Back=
Mark Read=
Mark Unread=
Trash= Trash=
Inbox= Inbox=
Outbox= Outbox=
@ -44,75 +39,55 @@ Mail lists=
Options= Options=
Close= Close=
(No subject)= (No subject)=
Player name= Subject=
That name is already in your contacts= Delete=
The contact name cannot be empty.= New=
Save= Date=
Maillist name= Filter=
Desc= Allow multiple selection=
Players= @1 of @2 selected=
That name is already in your mailing lists.= (Un)select all=
The mailing list name cannot be empty.= No mail=
Mark Read=
Mark Unread=
Mark Spam=
Unmark Spam=
Reply= Reply=
Reply all= Reply all=
Forward= Forward=
Reply only to the sender= Reply only to the sender=
Reply to all involved people= Reply to all involved people=
Transfer message to other people= Transfer message to other people=
Date=
From=
Filter=
Allow multiple selection=
@1 of @2 selected=
(Un)select all=
No mail=
Read= Read=
Ascending= From=
Descending=
(No description)= (No description)=
No maillist= No maillist=
Receivers= Receivers=
(Un)mute sender= You have mail! Type /mail to read=
Add= Add=
Remove= Remove=
Reset= CC=
Restore= Notifications=
Empty=
Trash is empty=
From/To=
No contacts=
The method of delivery to @1 is invalid.=
The recipient @1 could not be identified.=
@1 rejected your mail.=
Chat notifications= Chat notifications=
Receive a message in the chat when there is a new message=
On join notifications= On join notifications=
Receive a message at login when inbox isn't empty=
HUD notifications= HUD notifications=
Show an HUD notification when inbox isn't empty=
Sound notifications= Sound notifications=
Play a sound when there is a new message= Message list=
Show unread in different color= Show unread in different color=
Show CC/BCC in different color= Show CC/BCC in different color=
Default sorting field= Default sorting fields=
Default sorting direction= Other=
Move deleted messages to trash= Move deleted messages to trash=
Automatic marking read= Automatic marking read=
Date format:=
Receive a message in the chat when there is a new message=
Receive a message at login when inbox isn't empty=
Show an HUD notification when inbox isn't empty=
Play a sound when there is a new message=
Mark a message as read when opened= Mark a message as read when opened=
Date format= Reset=
Timezone offset= Settings=
Offset to add to server time.= About=
Mute list= Save=
Notifications= Ascending=
Message list= Descending=
Fields= From/To=
Spam=
Other=
Date and Time=
years= years=
months= months=
weeks= weeks=
@ -121,3 +96,6 @@ hours=
minuts= minuts=
seconds= seconds=
@1 ago= @1 ago=
Restore=
Empty=
Trash is empty=

76
mail.hu.tr Normal file
View file

@ -0,0 +1,76 @@
# textdomain: mail
# author: nyomi
Provided by mt-mods=Feltéve, hogy az én mt-mod-om
Version=Verzió
Licenses=License
Expat (code), WTFPL (textures)=Expat (kód), WTFPL (textúrák)
Communication using this system is NOT guaranteed to be private!=A systemben lévő komunikáció nem garantáltan privát!
Admins are able to view the messages of any player.=Az adminok megtudják nézni minden játékos üzenetjét.
Contributors=Közreműködöttek
Note=Jegyzet
Settings=Beállítások
About=Róla
You have a new message from @1! Subject: @2=Van egy új üzeneted @1-től Cím: @2
To view it, type /mail=Ahhoz hogy megnézd, írd /mail
You could also use the button in your inventory.=A gombot is tudod használni az inventoridban.
BCC=BCC
Cancel=Mégse
Save draft=mentés piszkozatként
Send=Küldés
Subject=Cím
To=Neki
CC=CC
Name=Név
No drafts=Nincsenek piszkozatok
Edit=Szerkesztés
New=Új
Delete=Törlés
Inbox=PostaLáda
Outbox=Elküldött üzenetek
Drafts=Piszkozatok
Contacts=Contaktok
Mail lists=Levelező lista
Options=Lehetőségek
Close=Bezár
(No subject)=(nincs cím)
Player name=Játékos neve
That name is already in your contacts=A név már a kontaktok között van
The contact name cannot be empty.=A contakt neve nem lehet üres.
Save=Mentés
Maillist name=Levelező lista neve
Desc=Desc
Players=Játékosok
That name is already in your mailing lists.=A név már benne van a levelező listában
The mailing list name cannot be empty.=A levelező lista neve nem lehet üres
Mark Read=Jelöld olvasottként
Mark Unread=Jelöld olvasatlanul
From=Tőle
Read=Olvasott
Filter=Filterek
Allow multiple selection=
@1 selected=@1 kiválasztva
(Un)select all=(ne válaszd ki) mindegyik választása
No mail=Nincs levél
Reply=Válasz
Reply all=Válaszmindenkinek
Forward=Továbbítás
Date=Dátum
Ascending=Emelkedő
Descending=Sűlyedő
(No description)=(Nincs leírás)
No maillist=Nincs levelező lista
You have mail! Type /mail to read=Van egy leveled! Írd /mail az olvasáshoz
Add=Hozzáadás
Remove=Elvétel
Back=Visza
Notifications=Értesítések
Chat notifications=Chates értesítések
On join notifications=Belépési értesírés
HUD notifications= HUD értesítés
Sound notifications=Hang értesítés
Message list=Üzenetek listája
Show unread in different color=Mutasd a nem olvasottakat más színnel
Show CC/BCC in different color=Mutasd a CC-t/BCC-t más színnel
Default sorting fields=Alap válogató terület
From/To=Tól(től)/neki
Reset=Viszaállítás

View file

@ -1,13 +1,13 @@
local STORAGE_VERSION_KEY = "@@version" local STORAGE_VERSION_KEY = "@@version"
local CURRENT_VERSION = 3.1
local function migrate_v1_to_v3() local function migrate_v1_to_v3()
local file = io.open(core.get_worldpath().."/mail.db", "r") local file = io.open(minetest.get_worldpath().."/mail.db", "r")
assert(file) assert(file)
print("[mail] Migration from v1 to v3 database") print("[mail] Migration from v1 to v3 database")
local data = file:read("*a") local data = file:read("*a")
local oldmails = core.deserialize(data) local oldmails = minetest.deserialize(data)
file:close() file:close()
for name, oldmessages in pairs(oldmails) do for name, oldmessages in pairs(oldmails) do
@ -28,7 +28,7 @@ local function migrate_v1_to_v3()
-- rename file -- rename file
print("[mail,v1] migration done, renaming old mail.db") print("[mail,v1] migration done, renaming old mail.db")
os.rename(core.get_worldpath().."/mail.db", core.get_worldpath().."/mail.db.old") os.rename(minetest.get_worldpath().."/mail.db", minetest.get_worldpath().."/mail.db.old")
end end
local function read_json_file(path) local function read_json_file(path)
@ -36,7 +36,7 @@ local function read_json_file(path)
local content = {} local content = {}
if file then if file then
local json = file:read("*a") local json = file:read("*a")
content = core.parse_json(json or "[]") or {} content = minetest.parse_json(json or "[]") or {}
file:close() file:close()
end end
return content return content
@ -44,13 +44,13 @@ end
-- migrate from v2 to v3 database -- migrate from v2 to v3 database
local function migrate_v2_to_v3() local function migrate_v2_to_v3()
local maildir = core.get_worldpath().."/mails" local maildir = minetest.get_worldpath().."/mails"
core.mkdir(maildir) -- if necessary (eg. first login) minetest.mkdir(maildir) -- if necessary (eg. first login)
print("[mail] Migration from v2 to v3 database") print("[mail] Migration from v2 to v3 database")
-- defer execution until auth-handler ready (first server-step) -- defer execution until auth-handler ready (first server-step)
core.after(0, function() minetest.after(0, function()
for playername, _ in core.get_auth_handler().iterate() do for playername, _ in minetest.get_auth_handler().iterate() do
local entry = mail.get_storage_entry(playername) local entry = mail.get_storage_entry(playername)
local player_contacts = read_json_file(maildir .. "/contacts/" .. playername .. ".json") local player_contacts = read_json_file(maildir .. "/contacts/" .. playername .. ".json")
@ -80,149 +80,20 @@ local function migrate_v2_to_v3()
end) end)
end end
local function search_box(playername, box, uuid)
local e = mail.get_storage_entry(playername)
for _, m in ipairs(e[box]) do
if m.id == uuid then
return { time = m.time, from = m.from, to = m.to, cc = m.cc, bcc = m.bcc, subject = m.subject, body = m.body } end
end
return false
end
local function search_boxes(playername, boxes, uuid)
local result
for _, b in ipairs(boxes) do
result = search_box(playername, b, uuid)
if result then return result end
end
end
local function is_uuid_existing(uuid)
local boxes = {"inbox", "outbox", "drafts", "trash"}
if mail.storage.get_keys then
for _, k in ipairs(mail.storage:get_keys()) do
if string.sub(k,1,5) == "mail/" then
local p = string.sub(k, 6)
local result = search_boxes(p, boxes, uuid)
if result then return result end
end
end
else
for p, _ in core.get_auth_handler().iterate() do
local result = search_boxes(p, boxes, uuid)
if result then return result end
end
end
return false
end
local function are_message_sames(a, b)
return a.time == b.time
and a.from == b.from
and a.to == b.to
and a.cc == b.cc
and a.bcc == b.bcc
and a.subject == b.subject
and a.body == b.body
end
local function replace_other_player_message_uuid(p, m, uuid, new_uuid)
local er = mail.get_storage_entry(p)
for _, r in ipairs(er.inbox) do
if r.id == uuid and not are_message_sames(m, r) then
r.id = new_uuid
end
end
for _, r in ipairs(er.outbox) do
if r.id == uuid and not are_message_sames(m, r) then
r.id = new_uuid
end
end
for _, r in ipairs(er.drafts) do
if r.id == uuid and not are_message_sames(m, r) then
r.id = new_uuid
end
end
for _, r in ipairs(er.trash) do
if r.id == uuid and not are_message_sames(m, r) then
r.id = new_uuid
end
end
mail.set_storage_entry(p, er)
end
local function fix_box_duplicate_uuids(playername, box)
local e = mail.get_storage_entry(playername)
for _, m in ipairs(e[box]) do
local uuid = m.id
local exists = is_uuid_existing(uuid)
if exists and not are_message_sames(exists, m) then
local new_uuid = mail.new_uuid() -- generates a new uuid to replace doublons
if mail.storage.get_keys then
for _, k in ipairs(mail.storage:get_keys()) do
if string.sub(k,1,5) == "mail/" then
local p = string.sub(k, 6)
replace_other_player_message_uuid(p, m, uuid, new_uuid)
end
end
else
for p, _ in core.get_auth_handler().iterate() do
replace_other_player_message_uuid(p, m, uuid, new_uuid)
end
end
end
end
end
local function fix_player_duplicate_uuids(playername)
fix_box_duplicate_uuids(playername, "inbox")
fix_box_duplicate_uuids(playername, "outbox")
fix_box_duplicate_uuids(playername, "drafts")
fix_box_duplicate_uuids(playername, "trash")
end
-- repair database for uuid doublons
local function repair_storage()
-- iterate through players
-- get_keys() was introduced in 5.7
if mail.storage.get_keys then
for _, k in ipairs(mail.storage:get_keys()) do
if string.sub(k,1,5) == "mail/" then
local p = string.sub(k, 6)
fix_player_duplicate_uuids(p)
end
end
else
core.after(0, function()
for p, _ in core.get_auth_handler().iterate() do
fix_player_duplicate_uuids(p)
end
end)
end
end
function mail.migrate() function mail.migrate()
-- check for v2 storage first, v1-migration might have set the v3-flag already -- check for v2 storage first, v1-migration might have set the v3-flag already
local version = mail.storage:get_float(STORAGE_VERSION_KEY) local version = mail.storage:get_int(STORAGE_VERSION_KEY)
if version < math.floor(CURRENT_VERSION) then if version < 3 then
-- v2 to v3 -- v2 to v3
migrate_v2_to_v3() migrate_v2_to_v3()
mail.storage:set_float(STORAGE_VERSION_KEY, CURRENT_VERSION) mail.storage:set_int(STORAGE_VERSION_KEY, 3)
end end
-- check for v1 storage -- check for v1 storage
local v1_file = io.open(core.get_worldpath().."/mail.db", "r") local v1_file = io.open(minetest.get_worldpath().."/mail.db", "r")
if v1_file then if v1_file then
-- v1 to v3 -- v1 to v3
migrate_v1_to_v3() migrate_v1_to_v3()
mail.storage:set_float(STORAGE_VERSION_KEY, CURRENT_VERSION) mail.storage:set_int(STORAGE_VERSION_KEY, 3)
end
-- repair storage for uuid doublons
if version < CURRENT_VERSION then
repair_storage()
mail.storage:set_float(STORAGE_VERSION_KEY, CURRENT_VERSION)
end end
end end

View file

@ -1,4 +1,3 @@
name = mail name = mail
description = ingame mail-system description = ingame mail-system
optional_depends = canonical_name,default,mtt,unified_inventory
optional_depends = canonical_name,default,mtt,unified_inventory,sfinv_buttons,beerchat

View file

@ -1,7 +1,7 @@
mtt.register("setup", function(callback) mtt.register("setup", function(callback)
-- create test players -- create test players
local auth_handler = core.get_auth_handler() local auth_handler = minetest.get_auth_handler()
auth_handler.set_password("player1", "") auth_handler.set_password("player1", "")
auth_handler.set_password("player2", "") auth_handler.set_password("player2", "")
auth_handler.set_password("player3", "") auth_handler.set_password("player3", "")

View file

@ -1,8 +1,8 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
core.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
core.after(2, function(name) minetest.after(2, function(name)
local entry = mail.get_storage_entry(name) local entry = mail.get_storage_entry(name)
local messages = entry.inbox local messages = entry.inbox
mail.hud_update(name, messages) mail.hud_update(name, messages)
@ -16,8 +16,8 @@ core.register_on_joinplayer(function(player)
end end
if unreadcount > 0 and mail.get_setting(name, "onjoin_notifications") then if unreadcount > 0 and mail.get_setting(name, "onjoin_notifications") then
core.chat_send_player(name, minetest.chat_send_player(name,
core.colorize(mail.get_color("new"), "(" .. unreadcount .. ") " .. S("You have mail! Type /mail to read"))) minetest.colorize(mail.colors.new, "(" .. unreadcount .. ") " .. S("You have mail! Type /mail to read")))
end end
end, player:get_player_name()) end, player:get_player_name())
end) end)

View file

@ -1,53 +0,0 @@
-- translation
local S = mail.S
local has_canonical_name = core.get_modpath("canonical_name")
mail.register_on_player_receive(function(name, msg)
-- add to inbox
local entry = mail.get_storage_entry(name)
table.insert(entry.inbox, msg)
mail.set_storage_entry(name, entry)
-- notify recipients that happen to be online
local mail_alert = S("You have a new message from @1! Subject: @2", msg.from, msg.subject) ..
"\n" .. S("To view it, type /mail")
local inventory_alert = S("You could also use the button in your inventory.")
local player = core.get_player_by_name(name)
if player then
if mail.get_setting(name, "chat_notifications") == true then
core.chat_send_player(name, mail_alert)
if core.get_modpath("unified_inventory") or core.get_modpath("sfinv_buttons") then
core.chat_send_player(name, inventory_alert)
end
end
if mail.get_setting(name, "sound_notifications") == true then
core.sound_play("mail_notif", {to_player=name})
end
local receiver_entry = mail.get_storage_entry(name)
local receiver_messages = receiver_entry.inbox
mail.hud_update(name, receiver_messages)
end
end)
mail.register_recipient_handler(function(_, pname)
if not core.player_exists(pname) then
return nil
end
return true, function(msg)
for _, on_player_receive in ipairs(mail.registered_on_player_receives) do
if on_player_receive(pname, msg) then
break
end
end
end
end)
if has_canonical_name then
mail.register_recipient_handler(function(_, name)
local realname = canonical_name.get(name)
if realname then
return true, realname
end
end)
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 722 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 802 KiB

View file

@ -31,7 +31,7 @@ function mail.get_storage_entry(playername)
entry = populate_entry() entry = populate_entry()
else else
-- deserialize existing entry -- deserialize existing entry
local e = core.parse_json(str) local e = minetest.parse_json(str)
entry = populate_entry(e) entry = populate_entry(e)
end end
@ -55,23 +55,20 @@ end
local function save_worker() local function save_worker()
for key, entry in pairs(save_queued_entries) do for key, entry in pairs(save_queued_entries) do
-- write to backend -- write to backend
mail.storage:set_string(key, core.write_json(entry)) mail.storage:set_string(key, minetest.write_json(entry))
end end
-- clear queue -- clear queue
save_queued_entries = {} save_queued_entries = {}
-- clear cached entries
cache = {}
-- save every second -- save every second
core.after(1, save_worker) minetest.after(1, save_worker)
end end
-- start save-worker loop -- start save-worker loop
save_worker() save_worker()
-- save on shutdown -- save on shutdown
core.register_on_shutdown(save_worker) minetest.register_on_shutdown(save_worker)
-- get a mail by id from the players in- or outbox -- get a mail by id from the players in- or outbox
function mail.get_message(playername, msg_id) function mail.get_message(playername, msg_id)
@ -154,46 +151,38 @@ function mail.sort_messages(messages, sortfield, descending, filter)
return results return results
end end
local function mark_property(playername, property, msg_ids, value, hud_update) -- marks a mail read by its id
function mail.mark_read(playername, msg_ids)
local entry = mail.get_storage_entry(playername) local entry = mail.get_storage_entry(playername)
if type(msg_ids) ~= "table" then -- if this is not a table if type(msg_ids) ~= "table" then -- if this is not a table
msg_ids = { msg_ids } msg_ids = { msg_ids }
end end
for _, property_msg_id in ipairs(msg_ids) do for _, read_msg_id in ipairs(msg_ids) do
for _, entry_msg in ipairs(entry.inbox) do for _, entry_msg in ipairs(entry.inbox) do
if entry_msg.id == property_msg_id then if entry_msg.id == read_msg_id then
entry_msg[property] = value entry_msg.read = true
end end
end end
end end
mail.set_storage_entry(playername, entry) mail.set_storage_entry(playername, entry)
if hud_update then
mail.hud_update(playername, entry.inbox) mail.hud_update(playername, entry.inbox)
end
return
end
-- marks a mail read by its id
function mail.mark_read(playername, msg_ids)
mark_property(playername, "read", msg_ids, true, true)
return return
end end
-- marks a mail unread by its id -- marks a mail unread by its id
function mail.mark_unread(playername, msg_ids) function mail.mark_unread(playername, msg_ids)
mark_property(playername, "read", msg_ids, false, true) local entry = mail.get_storage_entry(playername)
return if type(msg_ids) ~= "table" then -- if this is not a table
end msg_ids = { msg_ids }
end
-- marks a mail as a spam for _, unread_msg_id in ipairs(msg_ids) do
function mail.mark_spam(playername, msg_ids) for _, entry_msg in ipairs(entry.inbox) do
mark_property(playername, "spam", msg_ids, true) if entry_msg.id == unread_msg_id then
return entry_msg.read = false
end end
end
-- marks a mail as a non-spam end
function mail.unmark_spam(playername, msg_ids) mail.set_storage_entry(playername, entry)
mark_property(playername, "spam", msg_ids, false)
return return
end end
@ -346,9 +335,6 @@ function mail.get_maillist_by_name(playername, listname)
local entry = mail.get_storage_entry(playername) local entry = mail.get_storage_entry(playername)
for _, list in ipairs(entry.lists) do for _, list in ipairs(entry.lists) do
if list.name == listname then if list.name == listname then
if not list.players then
list.players = {}
end
return list return list
end end
end end
@ -365,9 +351,6 @@ function mail.update_maillist(playername, list, old_list_name)
end end
end end
-- insert -- insert
if not list.players then
list.players = {}
end
table.insert(entry.lists, list) table.insert(entry.lists, list)
mail.set_storage_entry(playername, entry) mail.set_storage_entry(playername, entry)
end end
@ -384,72 +367,63 @@ function mail.delete_maillist(playername, listname)
end end
end end
local function extract_maillists_main(receivers, maillists_owner, expanded_receivers, seen) function mail.extractMaillists(receivers_string, maillists_owner)
if type(receivers) == "string" then local receivers = mail.parse_player_list(receivers_string) -- extracted receivers
receivers = mail.parse_player_list(receivers)
end
for _, receiver in pairs(receivers) do -- extract players from mailing lists
if seen[receiver] then while string.find(receivers_string, "@") do
-- Do not add/expand this receiver as it is already seen local globalReceivers = mail.parse_player_list(receivers_string) -- receivers including maillists
core.log("verbose", ("mail: ignoring duplicate receiver %q during maillist expansion"):format(receiver)) receivers = {}
elseif string.find(receiver, "^@") then for _, receiver in ipairs(globalReceivers) do
seen[receiver] = true local receiverInfo = receiver:split("@") -- @maillist
local listname = string.sub(receiver, 2) if receiverInfo[1] and receiver == "@" .. receiverInfo[1] then
local maillist = mail.get_maillist_by_name(maillists_owner, listname) local maillist = mail.get_maillist_by_name(maillists_owner, receiverInfo[1])
if maillist then if maillist then
core.log("verbose", ("mail: expanding maillist %q"):format(listname)) for _, playername in ipairs(maillist.players) do
for _, entry in ipairs(maillist.players) do table.insert(receivers, playername)
extract_maillists_main(entry, maillists_owner, expanded_receivers, seen)
end end
end end
else else -- in case of player
seen[receiver] = true table.insert(receivers, receiver)
core.log("verbose", ("mail: adding %q to receiver list during maillist expansion"):format(receiver))
table.insert(expanded_receivers, receiver)
end end
end end
receivers_string = mail.concat_player_list(receivers)
end
return receivers
end end
function mail.extract_maillists(receivers, maillists_owner) function mail.get_setting_default_value(setting_name)
local expanded_receivers = {} local default_values = {
extract_maillists_main(receivers, maillists_owner, expanded_receivers, {}) chat_notifications = true,
return expanded_receivers onjoin_notifications = true,
hud_notifications = true,
sound_notifications = true,
unreadcolorenable = true,
cccolorenable = true,
defaultsortfield = 3,
defaultsortdirection = 1,
trash_move_enable = true,
auto_marking_read = true,
date_format = "%Y-%m-%d %X",
}
return default_values[setting_name]
end end
function mail.get_setting_default_value(key) function mail.get_setting(playername, setting_name)
return mail.settings[key].default
end
function mail.get_setting(playername, key)
local entry = mail.get_storage_entry(playername) local entry = mail.get_storage_entry(playername)
local value = (entry.settings[key] == nil if entry.settings[setting_name] ~= nil then
and {mail.get_setting_default_value(key)} return entry.settings[setting_name]
or {entry.settings[key]})[1] else
return mail.get_setting_default_value(setting_name)
if mail.settings[key].sync then -- in case this setting is shared with another mod
local sync_value = mail.settings[key].sync(playername) -- get new value
if sync_value then
value = sync_value
mail.set_setting(playername, key, value, true) -- update the setting in mail storage and don't transfer it again
end end
end
return value
end end
-- add or update a setting -- add or update a setting
function mail.set_setting(playername, key, value, not_transfer) function mail.set_setting(playername, key, value)
local entry = mail.get_storage_entry(playername) local entry = mail.get_storage_entry(playername)
local valid_value = value entry.settings[key] = value
if mail.settings[key].check then
valid_value = mail.settings[key].check(playername, value)
end
entry.settings[key] = valid_value
mail.set_storage_entry(playername, entry) mail.set_storage_entry(playername, entry)
if not not_transfer and mail.settings[key].transfer then -- in case this setting is shared with another mod
mail.settings[key].transfer(playername, valid_value)
end
end end
function mail.reset_settings(playername) function mail.reset_settings(playername)
@ -458,7 +432,7 @@ function mail.reset_settings(playername)
mail.set_storage_entry(playername, entry) mail.set_storage_entry(playername, entry)
end end
function mail.pairs_by_keys(t, f) function mail.pairsByKeys(t, f)
-- http://www.lua.org/pil/19.3.html -- http://www.lua.org/pil/19.3.html
local a = {} local a = {}
for n in pairs(t) do table.insert(a, n) end for n in pairs(t) do table.insert(a, n) end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -1,128 +1,54 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:about" local FORMNAME = "mail:about"
local groups = {
{ "o", S("Original author")},
{ "c", S("Code")},
{ "i", S("Internationalization")},
{ "t", S("Textures")},
{ "a", S("Audio")},
}
local contributors = {
{ name = "Cheapie", groups = {"o", "c"} },
{ name = "aBlueShadow", groups = {"c"} },
{ name = "APercy", groups = {"i"} },
{ name = "Athozus", groups = {"c", "i"} },
{ name = "BuckarooBanzay", groups = {"c"} },
{ name = "Chache", groups = {"i"} },
{ name = "Dennis Jenkins", groups = {"c"} },
{ name = "Emojigit", groups = {"c", "i"} },
{ name = "Eredin", groups = {"i"} },
{ name = "fluxionary", groups = {"c"} },
{ name = "imre84", groups = {"c"} },
{ name = "Muhammad Rifqi Priyo Susanto", groups = {"i"} },
{ name = "NatureFreshMilk", groups = {"c", "t"} },
{ name = "Niklp", groups = {"c", "i"} },
{ name = "Nuno Filipe Povoa", groups = {"a"} },
{ name = "nyomi", groups = {"i"} },
{ name = "OgelGames", groups = {"c"} },
{ name = "Panquesito7", groups = {"c"} },
{ name = "Peter Nerlich", groups = {"c"} },
{ name = "Rubenwardy", groups = {"c"} },
{ name = "savilli", groups = {"c"} },
{ name = "Singularis", groups = {"c"} },
{ name = "SX", groups = {"c"} },
{ name = "TheTrueBeginner", groups = {"i"} },
{ name = "Thomas Rudin", groups = {"c"} },
{ name = "Toby1710", groups = {"c"} },
{ name = "whosit", groups = {"c"} },
{ name = "Wuzzy", groups = {"i"} },
{ name = "y5nw", groups = {"c", "i"} },
}
function mail.show_about(name) function mail.show_about(name)
mail.selected_idxs.contributor_grouping[name] = tonumber(mail.selected_idxs.contributor_grouping[name]) or 1
local formspec = [[ local formspec = [[
size[10,6;] size[10,6;]
tabheader[0,0;optionstab;]] .. S("Settings") .. "," .. S("About") .. [[;2;false;false] tabheader[0.3,1;optionstab;]] .. S("Settings") .. "," .. S("About") .. [[;2;false;false]
button[9.35,0;0.75,0.5;back;X] button[9.35,0;0.75,0.5;back;X]
label[0,0.8;Mail]
box[0,0;3,0.45;]] .. mail.get_color("highlighted") .. [[] label[0,1.2;]] .. S("Provided by mt-mods") .. [[]
label[0.2,0;Mail] label[0,1.6;]] .. S("Version") .. [[ : 1.3.0]
label[0,2.2;]] .. S("Licenses") .. [[ :]
label[0.2,0.5;]] .. S("Provided by mt-mods") .. [[] label[0.2,2.6;]] .. S("Expat (code), WTFPL (textures)") .. [[]
label[0.2,0.9;]] .. S("Version: @1", "1.5.0-dev") .. [[] label[0,3.2;https://github.com/mt-mods/mail]
label[0,3.6;https://content.minetest.net/packages/mt-mods/mail]
box[0,1.5;3,0.45;]] .. mail.get_color("highlighted") .. [[] textarea[0.5,4.8;4,5.5;;]] .. S("Note") .. [[;]] ..
label[0.2,1.5;]] .. S("Licenses") .. [[]
label[0.2,2.0;]] .. S("Expat (code), WTFPL (textures)") .. [[]
box[0,2.6;3,0.45;]] .. mail.get_color("highlighted") .. [[]
label[0.2,2.6;]] .. S("Note") .. [[]
textarea[0.5,3.15;4,5.5;;;]] ..
S("Communication using this system is NOT guaranteed to be private!") .. " " .. S("Communication using this system is NOT guaranteed to be private!") .. " " ..
S("Admins are able to view the messages of any player.") .. [[] S("Admins are able to view the messages of any player.") .. [[]
button[0,5.7;2,0.5;github;GitHub] tablecolumns[color;text;text]
button[2,5.7;2,0.5;contentdb;ContentDB] table[5,0.75;4.9,5.5;contributors;]] ..
mail.colors.header .. [[,]] .. S("Contributors") .. [[,,]] ..
mail.colors.important .. [[,Cheapie,Initial idea/project,]] ..
[[,Rubenwardy,Lua/UI improvements,]] ..
[[,BuckarooBanzay,Clean-ups\, Refactoring,]] ..
[[,Athozus,Boxes\, Maillists\, UI\, Settings,]] ..
[[,fluxionary,Minor fixups,]] ..
[[,SX,Various fixes\, UI,]] ..
[[,Toby1710,UX fixes,]] ..
[[,Peter Nerlich,CC\, BCC,]] ..
[[,Niklp,German translation,]] ..
[[,Emojigit,Traditional Chinese trans.,]] ..
[[,Dennis Jenkins,UX fixes,]] ..
[[,Thomas Rudin,Maintenance,]] ..
[[,NatureFreshMilk,Maintenance,]] ..
[[,imre84,UI fixes,]] ..
[[,Chache,Spanish translation,]] ..
[[,APercy,Brazilian Portuguese trans.,]] ..
[[,Nuno Filipe Povoa,mail_notif.ogg,]] ..
[[,TheTrueBeginner,Simplified Chinese trans.,]] ..
[[,nyomi,Hungarian translation,]] ..
[[,whosit,UI fixes,]] ..
[[,Wuzzy,German translation]
]] .. mail.theme
box[4,0;3,0.45;]] .. mail.get_color("highlighted") .. [[] minetest.show_formspec(name, FORMNAME, formspec)
label[4.2,0;]] .. S("Contributors") .. [[]
dropdown[4,0.75;6.4;contributor_grouping;]]
.. S("Group by name") .. ","
.. S("Group by contribution") .. ";" .. mail.selected_idxs.contributor_grouping[name] .. [[;true]
]]
local contributor_list, contributor_columns = {}
if mail.selected_idxs.contributor_grouping[name] == 2 then
contributor_columns = "color;text"
local sorted = {}
for _, g in ipairs(groups) do
sorted[g[1]] = {}
end
for _, c in ipairs(contributors) do
for _, g in ipairs(c.groups) do
table.insert(sorted[g] or {}, c.name)
end
end
for _, g in ipairs(groups) do
table.insert(contributor_list, mail.get_color("header") .. "," .. g[2])
for _, c in ipairs(sorted[g[1]]) do
table.insert(contributor_list, "," .. c)
end
end
else
contributor_columns = "text;text"
for _, c in ipairs(contributors) do
for _, g in ipairs(groups) do
local index = table.indexof(c.groups, g[1])
if index >= 1 then
if index == 1 then
table.insert(contributor_list, c.name)
else
table.insert(contributor_list, "")
end
table.insert(contributor_list, g[2])
end
end
end
end
formspec = formspec .. ("tablecolumns[%s]"):format(contributor_columns) ..
("table[4,1.6;5.9,4.65;contributors;%s]"):format(table.concat(contributor_list, ","))
formspec = formspec .. mail.theme
core.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
@ -139,14 +65,5 @@ core.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.optionstab == "2" then elseif fields.optionstab == "2" then
mail.selected_idxs.optionstab[playername] = 2 mail.selected_idxs.optionstab[playername] = 2
mail.show_about(playername) mail.show_about(playername)
elseif fields.github then
core.chat_send_player(playername, "https://github.com/mt-mods/mail")
elseif fields.contentdb then
core.chat_send_player(playername, "https://content.minetest.net/packages/mt-mods/mail")
elseif fields.contributor_grouping then
mail.selected_idxs.contributor_grouping[playername] = fields.contributor_grouping
mail.show_about(playername)
end end
end) end)

View file

@ -1,7 +1,8 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:compose" local FORMNAME = "mail:compose"
local msg_id = {}
function mail.show_compose(name, to, subject, body, cc, bcc, id) function mail.show_compose(name, to, subject, body, cc, bcc, id)
local formspec = [[ local formspec = [[
@ -20,25 +21,32 @@ function mail.show_compose(name, to, subject, body, cc, bcc, id)
]] .. mail.theme ]] .. mail.theme
formspec = string.format(formspec, formspec = string.format(formspec,
core.formspec_escape(to) or "", minetest.formspec_escape(to) or "",
core.formspec_escape(cc) or "", minetest.formspec_escape(cc) or "",
core.formspec_escape(bcc) or "", minetest.formspec_escape(bcc) or "",
core.formspec_escape(subject) or "", minetest.formspec_escape(subject) or "",
core.formspec_escape(body) or "") minetest.formspec_escape(body) or "")
mail.selected_idxs.message[name] = id or mail.new_uuid() if id then
msg_id[name] = id
else
msg_id[name] = nil
end
core.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
local name = player:get_player_name() local name = player:get_player_name()
if fields.send then if fields.send then
local id = mail.selected_idxs.message[name] or mail.new_uuid() local id = mail.new_uuid()
if msg_id[name] then
id = msg_id[name]
end
if (fields.to == "" and fields.cc == "" and fields.bcc == "") or fields.body == "" then if (fields.to == "" and fields.cc == "" and fields.bcc == "") or fields.body == "" then
-- if mail is invalid then store it as a draft -- if mail is invalid then store it as a draft
mail.save_draft({ mail.save_draft({
@ -63,7 +71,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
body = fields.body, body = fields.body,
}) })
if not success then if not success then
core.chat_send_player(name, err) minetest.chat_send_player(name, err)
return return
end end
@ -87,7 +95,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
end end
core.after(0.5, function() minetest.after(0.5, function()
mail.selected_idxs.drafts[name] = nil mail.selected_idxs.drafts[name] = nil
mail.show_mail_menu(name) mail.show_mail_menu(name)
end) end)
@ -109,8 +117,8 @@ core.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.draft then elseif fields.draft then
local id = mail.new_uuid() local id = mail.new_uuid()
if mail.selected_idxs.message[name] then if msg_id[name] then
id = mail.selected_idxs.message[name] id = msg_id[name]
end end
mail.save_draft({ mail.save_draft({
id = id, id = id,

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:contacts" local FORMNAME = "mail:contacts"
@ -9,15 +9,15 @@ local contacts_formspec = "size[8,9;]" .. mail.theme .. [[
button[6,1.60;2,0.5;delete;]] .. S("Delete") .. [[] button[6,1.60;2,0.5;delete;]] .. S("Delete") .. [[]
button[6,8.25;2,0.5;back;]] .. S("Back") .. [[] button[6,8.25;2,0.5;back;]] .. S("Back") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0;5.75,9;contacts;]] .. mail.get_color("header") .. "," .. S("Name") .. "," .. S("Note") table[0,0;5.75,9;contacts;]] .. mail.colors.header .. "," .. S("Name") .. "," .. S("Note")
function mail.show_contacts(name) function mail.show_contacts(name)
local formspec = contacts_formspec .. mail.compile_contact_list(name, mail.selected_idxs.contacts[name]) local formspec = contacts_formspec .. mail.compile_contact_list(name, mail.selected_idxs.contacts[name])
core.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
@ -26,8 +26,8 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local contacts = mail.get_contacts(name) local contacts = mail.get_contacts(name)
if fields.contacts then if fields.contacts then
local evt = core.explode_table_event(fields.contacts) local evt = minetest.explode_table_event(fields.contacts)
for k, _, i in mail.pairs_by_keys(contacts) do for k, _, i in mail.pairsByKeys(contacts) do
if i == evt.row - 1 then if i == evt.row - 1 then
mail.selected_idxs.contacts[name] = tonumber(k) mail.selected_idxs.contacts[name] = tonumber(k)
break break
@ -58,7 +58,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
-- except if it was the last. Then determine the new last -- except if it was the last. Then determine the new last
local found = false local found = false
local last = nil local last = nil
for k in mail.pairs_by_keys(contacts) do for k in mail.pairsByKeys(contacts) do
if found then if found then
mail.selected_idxs.contacts[name] = tonumber(k) mail.selected_idxs.contacts[name] = tonumber(k)
break break

View file

@ -1,5 +1,6 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
function mail.show_drafts(name) function mail.show_drafts(name)
local trash_tab = "" local trash_tab = ""
@ -7,20 +8,20 @@ function mail.show_drafts(name)
trash_tab = "," .. S("Trash") trash_tab = "," .. S("Trash")
end end
local drafts_formspec = "size[8.5,11;]" .. mail.theme .. [[ local drafts_formspec = "size[8.5,10;]" .. mail.theme .. [[
tabheader[0.3,1;boxtab;]] .. tabheader[0.3,1;boxtab;]] ..
S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. trash_tab .. [[;3;false;false] S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. trash_tab .. [[;3;false;false]
button[6,0.10;2.5,0.5;new;]] .. S("New") .. [[] button[6,0.10;2.5,0.5;new;]] .. S("New") .. [[]
button[6,0.95;2.5,0.5;edit;]] .. S("Edit") .. [[] button[6,0.95;2.5,0.5;edit;]] .. S("Edit") .. [[]
button[6,1.70;2.5,0.5;delete;]] .. S("Delete") .. [[] button[6,1.70;2.5,0.5;delete;]] .. S("Delete") .. [[]
button[6,8.0;2.5,0.5;contacts;]] .. S("Contacts") .. [[] button[6,6.8;2.5,0.5;contacts;]] .. S("Contacts") .. [[]
button[6,8.8;2.5,0.5;maillists;]] .. S("Mail lists") .. [[] button[6,7.6;2.5,0.5;maillists;]] .. S("Mail lists") .. [[]
button[6,9.7;2.5,0.5;options;]] .. S("Options") .. [[] button[6,8.7;2.5,0.5;options;]] .. S("Options") .. [[]
button_exit[6,10.5;2.5,0.5;quit;]] .. S("Close") .. [[] button_exit[6,9.5;2.5,0.5;quit;]] .. S("Close") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0.7;5.75,10.35;drafts;]] .. mail.get_color("header") .. "," .. S("To") .. "," .. S("Subject") table[0,0.7;5.75,9.35;drafts;]] .. mail.colors.header .. "," .. S("To") .. "," .. S("Subject")
local formspec = { drafts_formspec } local formspec = { drafts_formspec }
local entry = mail.get_storage_entry(name) local entry = mail.get_storage_entry(name)
@ -32,14 +33,14 @@ function mail.show_drafts(name)
for _, message in ipairs(messages) do for _, message in ipairs(messages) do
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = core.formspec_escape(message.to) formspec[#formspec + 1] = minetest.formspec_escape(message.to)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if message.subject ~= "" then if message.subject ~= "" then
if string.len(message.subject) > 30 then if string.len(message.subject) > 30 then
formspec[#formspec + 1] = core.formspec_escape(string.sub(message.subject, 1, 27)) formspec[#formspec + 1] = minetest.formspec_escape(string.sub(message.subject, 1, 27))
formspec[#formspec + 1] = "..." formspec[#formspec + 1] = "..."
else else
formspec[#formspec + 1] = core.formspec_escape(message.subject) formspec[#formspec + 1] = minetest.formspec_escape(message.subject)
end end
else else
formspec[#formspec + 1] = S("(No subject)") formspec[#formspec + 1] = S("(No subject)")
@ -53,5 +54,5 @@ function mail.show_drafts(name)
else else
formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No drafts") .. "]" formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No drafts") .. "]"
end end
core.show_formspec(name, "mail:drafts", table.concat(formspec, "")) minetest.show_formspec(name, "mail:drafts", table.concat(formspec, ""))
end end

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:editcontact" local FORMNAME = "mail:editcontact"
@ -24,12 +24,12 @@ function mail.show_edit_contact(name, contact_name, note, illegal_name_hint)
end end
formspec = formspec .. mail.theme formspec = formspec .. mail.theme
formspec = string.format(formspec, formspec = string.format(formspec,
core.formspec_escape(contact_name or ""), minetest.formspec_escape(contact_name or ""),
core.formspec_escape(note or "")) minetest.formspec_escape(note or ""))
core.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:editmaillist" local FORMNAME = "mail:editmaillist"
@ -25,13 +25,13 @@ function mail.show_edit_maillist(playername, maillist_name, desc, players, illeg
end end
formspec = formspec .. mail.theme formspec = formspec .. mail.theme
formspec = string.format(formspec, formspec = string.format(formspec,
core.formspec_escape(maillist_name or ""), minetest.formspec_escape(maillist_name or ""),
core.formspec_escape(desc or ""), minetest.formspec_escape(desc or ""),
core.formspec_escape(players or "")) minetest.formspec_escape(players or ""))
core.show_formspec(playername, FORMNAME, formspec) minetest.show_formspec(playername, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
@ -59,6 +59,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
desc = fields.desc, desc = fields.desc,
players = mail.parse_player_list(fields.players) players = mail.parse_player_list(fields.players)
}, old_maillist.name) }, old_maillist.name)
maillists[mail.selected_idxs.maillists[name]] = nil
end end
else else
mail.update_maillist(name, { mail.update_maillist(name, {

View file

@ -1,6 +1,6 @@
-- Getter to filter and sort messages on demand -- Getter to filter and sort messages on demand
local function message_getter(messages, sortfield, ascending, filter) local function messageGetter(messages, sortfield, ascending, filter)
local results local results
return function() return function()
if not results then if not results then
@ -14,14 +14,12 @@ local function nonempty(x)
return ((type(x)=="table")and(#x>0)) return ((type(x)=="table")and(#x>0))
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= "mail:inbox" and formname ~= "mail:outbox" if formname ~= "mail:inbox" and formname ~= "mail:outbox"
and formname ~= "mail:drafts" and formname ~= "mail:trash" then and formname ~= "mail:drafts" and formname ~= "mail:trash" then
return return
end elseif fields.quit then
return
if fields.quit then
return true
end end
-- Get player name and handle / convert common input fields -- Get player name and handle / convert common input fields
@ -54,24 +52,19 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local entry = mail.get_storage_entry(name) local entry = mail.get_storage_entry(name)
local messagesDrafts = entry.drafts local messagesDrafts = entry.drafts
local messagesTrash = entry.trash local messagesTrash = entry.trash
local getInbox = message_getter(entry.inbox, inboxsortfield, sortdirection == "2", filter) local getInbox = messageGetter(entry.inbox, inboxsortfield, sortdirection == "2", filter)
local getOutbox = message_getter(entry.outbox, outboxsortfield, sortdirection == "2", filter) local getOutbox = messageGetter(entry.outbox, outboxsortfield, sortdirection == "2", filter)
-- Hanmdle formspec event -- Hanmdle formspec event
if fields.inbox then -- inbox table if fields.inbox then -- inbox table
local evt = core.explode_table_event(fields.inbox) local evt = minetest.explode_table_event(fields.inbox)
if evt.row == 1 then -- header if evt.row == 1 then -- header
if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction
mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2" mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2"
end end
mail.selected_idxs.sortfield[name] = evt.column-1 -- update column mail.selected_idxs.sortfield[name] = evt.column-1 -- update column
mail.show_mail_menu(name) mail.show_mail_menu(name)
return true return
end
local inbox = getInbox()[evt.row-1]
if not inbox then
mail.show_mail_menu(name)
return true
end end
if mail.selected_idxs.multipleselection[name] then if mail.selected_idxs.multipleselection[name] then
if not mail.selected_idxs.inbox[name] then if not mail.selected_idxs.inbox[name] then
@ -80,7 +73,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local selected_id = 0 local selected_id = 0
if mail.selected_idxs.inbox[name] and #mail.selected_idxs.inbox[name] > 0 then if mail.selected_idxs.inbox[name] and #mail.selected_idxs.inbox[name] > 0 then
for i, selected_msg in ipairs(mail.selected_idxs.inbox[name]) do for i, selected_msg in ipairs(mail.selected_idxs.inbox[name]) do
if inbox.id == selected_msg then if getInbox()[evt.row-1].id == selected_msg then
selected_id = i selected_id = i
table.remove(mail.selected_idxs.inbox[name], i) table.remove(mail.selected_idxs.inbox[name], i)
break break
@ -88,16 +81,13 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
end end
if selected_id == 0 then if selected_id == 0 then
table.insert(mail.selected_idxs.inbox[name], inbox.id) table.insert(mail.selected_idxs.inbox[name], getInbox()[evt.row-1].id)
mail.selected_idxs.message[name] = inbox.id
end end
else else
mail.selected_idxs.inbox[name] = { inbox.id } mail.selected_idxs.inbox[name] = { (getInbox()[evt.row-1] or {}).id }
mail.selected_idxs.message[name] = inbox.id
end end
if evt.type == "DCL" then if evt.type == "DCL" and getInbox()[evt.row-1] then
mail.selected_idxs.message[name] = inbox.id mail.show_message(name, getInbox()[evt.row-1].id)
mail.show_message(name, inbox.id)
else else
mail.show_mail_menu(name) mail.show_mail_menu(name)
end end
@ -105,19 +95,14 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
if fields.outbox then -- outbox table if fields.outbox then -- outbox table
local evt = core.explode_table_event(fields.outbox) local evt = minetest.explode_table_event(fields.outbox)
if evt.row == 1 then -- header if evt.row == 1 then -- header
if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction
mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2" mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2"
end end
mail.selected_idxs.sortfield[name] = evt.column-1 -- update column mail.selected_idxs.sortfield[name] = evt.column-1 -- update column
mail.show_mail_menu(name) mail.show_mail_menu(name)
return true return
end
local outbox = getOutbox()[evt.row-1]
if not outbox then
mail.show_mail_menu(name)
return true
end end
if mail.selected_idxs.multipleselection[name] then if mail.selected_idxs.multipleselection[name] then
if not mail.selected_idxs.outbox[name] then if not mail.selected_idxs.outbox[name] then
@ -126,7 +111,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local selected_id = 0 local selected_id = 0
if mail.selected_idxs.outbox[name] and #mail.selected_idxs.outbox[name] > 0 then if mail.selected_idxs.outbox[name] and #mail.selected_idxs.outbox[name] > 0 then
for i, selected_msg in ipairs(mail.selected_idxs.outbox[name]) do for i, selected_msg in ipairs(mail.selected_idxs.outbox[name]) do
if outbox.id == selected_msg then if getOutbox()[evt.row-1].id == selected_msg then
selected_id = i selected_id = i
table.remove(mail.selected_idxs.outbox[name], i) table.remove(mail.selected_idxs.outbox[name], i)
break break
@ -134,16 +119,13 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
end end
if selected_id == 0 then if selected_id == 0 then
table.insert(mail.selected_idxs.outbox[name], outbox.id) table.insert(mail.selected_idxs.outbox[name], getOutbox()[evt.row-1].id)
mail.selected_idxs.message[name] = outbox.id
end end
else else
mail.selected_idxs.outbox[name] = { outbox.id } mail.selected_idxs.outbox[name] = { (getOutbox()[evt.row-1] or {}).id }
mail.selected_idxs.message[name] = outbox.id
end end
if evt.type == "DCL" then if evt.type == "DCL" and getOutbox()[evt.row-1] then
mail.selected_idxs.message[name] = outbox.id mail.show_message(name, getOutbox()[evt.row-1].id)
mail.show_message(name, outbox.id)
else else
mail.show_mail_menu(name) mail.show_mail_menu(name)
end end
@ -151,7 +133,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
if fields.drafts then -- drafts table if fields.drafts then -- drafts table
local evt = core.explode_table_event(fields.drafts) local evt = minetest.explode_table_event(fields.drafts)
if evt.row == 1 then -- header if evt.row == 1 then -- header
if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction
mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2" mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2"
@ -162,7 +144,6 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
mail.selected_idxs.drafts[name] = evt.row - 1 mail.selected_idxs.drafts[name] = evt.row - 1
if evt.type == "DCL" and messagesDrafts[mail.selected_idxs.drafts[name]] then if evt.type == "DCL" and messagesDrafts[mail.selected_idxs.drafts[name]] then
mail.selected_idxs.message[name] = messagesDrafts[mail.selected_idxs.drafts[name]].id
mail.show_compose(name, mail.show_compose(name,
messagesDrafts[mail.selected_idxs.drafts[name]].to, messagesDrafts[mail.selected_idxs.drafts[name]].to,
messagesDrafts[mail.selected_idxs.drafts[name]].subject, messagesDrafts[mail.selected_idxs.drafts[name]].subject,
@ -176,7 +157,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
if fields.trash then -- trash table if fields.trash then -- trash table
local evt = core.explode_table_event(fields.trash) local evt = minetest.explode_table_event(fields.trash)
if evt.row == 1 then -- header if evt.row == 1 then -- header
if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction if mail.selected_idxs.sortfield[name] == evt.column-1 then -- if already this field, then change direction
mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2" mail.selected_idxs.sortdirection[name] = mail.selected_idxs.sortdirection[name] == "2" and "1" or "2"
@ -187,7 +168,6 @@ core.register_on_player_receive_fields(function(player, formname, fields)
end end
mail.selected_idxs.trash[name] = evt.row - 1 mail.selected_idxs.trash[name] = evt.row - 1
if evt.type == "DCL" and messagesTrash[mail.selected_idxs.trash[name]] then if evt.type == "DCL" and messagesTrash[mail.selected_idxs.trash[name]] then
mail.selected_idxs.message[name] = messagesTrash[mail.selected_idxs.trash[name]].id
mail.show_message(name, messagesTrash[mail.selected_idxs.trash[name]].id) mail.show_message(name, messagesTrash[mail.selected_idxs.trash[name]].id)
end end
return true return true
@ -211,14 +191,11 @@ core.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.read then elseif fields.read then
if formname == "mail:inbox" and nonempty(mail.selected_idxs.inbox[name]) then -- inbox table if formname == "mail:inbox" and nonempty(mail.selected_idxs.inbox[name]) then -- inbox table
mail.selected_idxs.message[name] = mail.selected_idxs.inbox[name][#mail.selected_idxs.inbox[name]] mail.show_message(name, mail.selected_idxs.inbox[name][#mail.selected_idxs.inbox[name]])
elseif formname == "mail:outbox" and nonempty(mail.selected_idxs.outbox[name]) then -- outbox table elseif formname == "mail:outbox" and nonempty(mail.selected_idxs.outbox[name]) then -- outbox table
mail.selected_idxs.message[name] = mail.selected_idxs.outbox[name][#mail.selected_idxs.outbox[name]] mail.show_message(name, mail.selected_idxs.outbox[name][#mail.selected_idxs.outbox[name]])
elseif formname == "mail:trash" and messagesTrash[mail.selected_idxs.trash[name]] then elseif formname == "mail:trash" and messagesTrash[mail.selected_idxs.trash[name]] then
mail.selected_idxs.message[name] = messagesTrash[mail.selected_idxs.trash[name]].id mail.show_message(name, messagesTrash[mail.selected_idxs.trash[name]].id)
end
if mail.selected_idxs.message[name] then
mail.show_message(name, mail.selected_idxs.message[name])
end end
elseif fields.edit then elseif fields.edit then
@ -313,20 +290,6 @@ core.register_on_player_receive_fields(function(player, formname, fields)
mail.show_mail_menu(name, sortfieldindex, sortdirection, filter) mail.show_mail_menu(name, sortfieldindex, sortdirection, filter)
elseif fields.markspam then
if formname == "mail:inbox" and mail.selected_idxs.inbox[name] then
mail.mark_spam(name, mail.selected_idxs.inbox[name])
end
mail.show_mail_menu(name, sortfieldindex, sortdirection, filter)
elseif fields.unmarkspam then
if formname == "mail:inbox" and mail.selected_idxs.inbox[name] then
mail.unmark_spam(name, mail.selected_idxs.inbox[name])
end
mail.show_mail_menu(name, sortfieldindex, sortdirection, filter)
elseif fields.new then elseif fields.new then
mail.show_compose(name) mail.show_compose(name)

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
function mail.show_inbox(name, sortfieldindex, sortdirection, filter) function mail.show_inbox(name, sortfieldindex, sortdirection, filter)
sortfieldindex = tonumber(sortfieldindex or mail.selected_idxs.sortfield[name]) sortfieldindex = tonumber(sortfieldindex or mail.selected_idxs.sortfield[name])
@ -33,7 +33,7 @@ function mail.show_inbox(name, sortfieldindex, sortdirection, filter)
trash_tab = "," .. S("Trash") trash_tab = "," .. S("Trash")
end end
local inbox_formspec = "size[8.5,11;]" .. mail.theme .. [[ local inbox_formspec = "size[8.5,10;]" .. mail.theme .. [[
tabheader[0.3,1;boxtab;]] .. tabheader[0.3,1;boxtab;]] ..
S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. trash_tab .. [[;1;false;false] S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. trash_tab .. [[;1;false;false]
@ -43,46 +43,41 @@ function mail.show_inbox(name, sortfieldindex, sortdirection, filter)
button[6,2.45;2.5,0.5;replyall;]] .. S("Reply all") .. [[] button[6,2.45;2.5,0.5;replyall;]] .. S("Reply all") .. [[]
button[6,3.20;2.5,0.5;forward;]] .. S("Forward") .. [[] button[6,3.20;2.5,0.5;forward;]] .. S("Forward") .. [[]
button[6,3.95;2.5,0.5;delete;]] .. S("Delete") .. [[] button[6,3.95;2.5,0.5;delete;]] .. S("Delete") .. [[]
button[6,4.85;2.5,0.5;markread;]] .. S("Mark Read") .. [[] button[6,4.82;2.5,0.5;markread;]] .. S("Mark Read") .. [[]
button[6,5.55;2.5,0.5;markunread;]] .. S("Mark Unread") .. [[] button[6,5.55;2.5,0.5;markunread;]] .. S("Mark Unread") .. [[]
button[6,6.4;2.5,0.5;markspam;]] .. S("Mark Spam") .. [[] button[6,6.8;2.5,0.5;contacts;]] .. S("Contacts") .. [[]
button[6,7.1;2.5,0.5;unmarkspam;]] .. S("Unmark Spam") .. [[] button[6,7.6;2.5,0.5;maillists;]] .. S("Mail lists") .. [[]
button[6,8.0;2.5,0.5;contacts;]] .. S("Contacts") .. [[] button[6,8.7;2.5,0.5;options;]] .. S("Options") .. [[]
button[6,8.8;2.5,0.5;maillists;]] .. S("Mail lists") .. [[] button_exit[6,9.5;2.5,0.5;quit;]] .. S("Close") .. [[]
button[6,9.7;2.5,0.5;options;]] .. S("Options") .. [[]
button_exit[6,10.5;2.5,0.5;quit;]] .. S("Close") .. [[]
tooltip[reply;]] .. S("Reply only to the sender") .. [[] tooltip[reply;]] .. S("Reply only to the sender") .. [[]
tooltip[replyall;]] .. S("Reply to all involved people") .. [[] tooltip[replyall;]] .. S("Reply to all involved people") .. [[]
tooltip[forward;]] .. S("Transfer message to other people") .. [[] tooltip[forward;]] .. S("Transfer message to other people") .. [[]
dropdown[0,9.5;2,0.5;sortfield;]] .. dropdown[0,8.5;2,0.5;sortfield;]] ..
S("From") .. "," .. S("Subject") .. "," .. S("Date") .. [[;]] .. sortfieldindex .. [[;true] S("From") .. "," .. S("Subject") .. "," .. S("Date") .. [[;]] .. sortfieldindex .. [[;true]
dropdown[2.0,9.5;2,0.5;sortdirection;]] .. dropdown[2.0,8.5;2,0.5;sortdirection;]] ..
S("Ascending") .. "," .. S("Descending") .. [[;]] .. sortdirection .. [[;true] S("Ascending") .. "," .. S("Descending") .. [[;]] .. sortdirection .. [[;true]
field[4.25,9.95;1.4,0.5;filter;]] .. S("Filter") .. [[:;]] .. filter .. [[] field[4.25,8.95;1.4,0.5;filter;]] .. S("Filter") .. [[:;]] .. filter .. [[]
image_button[5.14,9.5;0.85,0.85;search.png;search;] button[5.14,8.62;0.85,0.5;search;Q]
checkbox[0,10.1;multipleselection;]] .. S("Allow multiple selection") .. [[;]] .. checkbox[0,9.1;multipleselection;]] .. S("Allow multiple selection") .. [[;]] ..
tostring(mail.selected_idxs.multipleselection[name]) .. [[] tostring(mail.selected_idxs.multipleselection[name]) .. [[]
label[0,10.65;]] .. label[0,9.65;]] .. S("@1 of @2 selected", tostring(#mail.selected_idxs.inbox[name]), tostring(#messages)) .. [[]
S("@1 of @2 selected", tostring(#mail.selected_idxs.inbox[name]), tostring(#messages)) .. [[] button[3.5,9.5;2.5,0.5;selectall;]] .. S("(Un)select all") .. [[]
button[3.5,10.5;2.5,0.5;selectall;]] .. S("(Un)select all") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0.7;5.75,8.45;inbox;]] .. mail.get_color("header") .. "," .. S("From") .. "," .. S("Subject") table[0,0.7;5.75,7.45;inbox;]] .. mail.colors.header .. "," .. S("From") .. "," .. S("Subject")
local formspec = { inbox_formspec } local formspec = { inbox_formspec }
mail.message_drafts[name] = nil mail.message_drafts[name] = nil
local unread_color_enable = mail.get_setting(name, "unreadcolorenable") local unread_color_enable = mail.get_setting(name, "unreadcolorenable")
local cc_color_enable = mail.get_setting(name, "cccolorenable") local cc_color_enable = mail.get_setting(name, "cccolorenable")
local mute_list = mail.get_setting(name, "mute_list")
if #messages > 0 then if #messages > 0 then
for _, message in ipairs(messages) do for _, message in ipairs(messages) do
local selected_id = 0 local selected_id = 0
local displayed_color = {}
-- check if message is in selection list and return its id -- check if message is in selection list and return its id
if mail.selected_idxs.inbox[name] and #mail.selected_idxs.inbox[name] > 0 then if mail.selected_idxs.inbox[name] and #mail.selected_idxs.inbox[name] > 0 then
for i, selected_msg in ipairs(mail.selected_idxs.inbox[name]) do for i, selected_msg in ipairs(mail.selected_idxs.inbox[name]) do
@ -93,30 +88,43 @@ function mail.show_inbox(name, sortfieldindex, sortdirection, filter)
end end
end end
if selected_id > 0 then if selected_id > 0 then
table.insert(displayed_color, "selected")
end
if not message.read and unread_color_enable then if not message.read and unread_color_enable then
table.insert(displayed_color, "important")
end
if not mail.player_in_list(name, message.to) and cc_color_enable then if not mail.player_in_list(name, message.to) and cc_color_enable then
table.insert(displayed_color, "additional") formspec[#formspec + 1] = "," .. mail.colors.imp_add_sel
else
formspec[#formspec + 1] = "," .. mail.colors.imp_sel
end end
if message.spam then else
table.insert(displayed_color, "warning") if not mail.player_in_list(name, message.to) and cc_color_enable then
formspec[#formspec + 1] = "," .. mail.colors.add_sel
else
formspec[#formspec + 1] = "," .. mail.colors.selected
end end
if table.indexof(mute_list, message.from) >= 1 then
table.insert(displayed_color, "muted")
end end
formspec[#formspec + 1] = "," .. mail.get_color(displayed_color) else
if not message.read and unread_color_enable then
if not mail.player_in_list(name, message.to) and cc_color_enable then
formspec[#formspec + 1] = "," .. mail.colors.imp_add
else
formspec[#formspec + 1] = "," .. mail.colors.important
end
else
if not mail.player_in_list(name, message.to) and cc_color_enable then
formspec[#formspec + 1] = "," .. mail.colors.additional
else
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = core.formspec_escape(message.from) end
end
end
formspec[#formspec + 1] = ","
formspec[#formspec + 1] = minetest.formspec_escape(message.from)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if message.subject ~= "" then if message.subject ~= "" then
if string.len(message.subject) > 30 then if string.len(message.subject) > 30 then
formspec[#formspec + 1] = core.formspec_escape(string.sub(message.subject, 1, 27)) formspec[#formspec + 1] = minetest.formspec_escape(string.sub(message.subject, 1, 27))
formspec[#formspec + 1] = "..." formspec[#formspec + 1] = "..."
else else
formspec[#formspec + 1] = core.formspec_escape(message.subject) formspec[#formspec + 1] = minetest.formspec_escape(message.subject)
end end
else else
formspec[#formspec + 1] = S("(No subject)") formspec[#formspec + 1] = S("(No subject)")
@ -127,5 +135,5 @@ function mail.show_inbox(name, sortfieldindex, sortdirection, filter)
formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No mail") .. "]" formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No mail") .. "]"
end end
core.show_formspec(name, "mail:inbox", table.concat(formspec, "")) minetest.show_formspec(name, "mail:inbox", table.concat(formspec, ""))
end end

View file

@ -1,23 +1,3 @@
-- sub files
local MP = core.get_modpath(core.get_current_modname())
dofile(MP .. "/ui/inbox.lua")
dofile(MP .. "/ui/outbox.lua")
dofile(MP .. "/ui/drafts.lua")
dofile(MP .. "/ui/trash.lua")
dofile(MP .. "/ui/message.lua")
dofile(MP .. "/ui/receivers.lua")
dofile(MP .. "/ui/events.lua")
dofile(MP .. "/ui/contacts.lua")
dofile(MP .. "/ui/edit_contact.lua")
dofile(MP .. "/ui/select_contact.lua")
dofile(MP .. "/ui/maillists.lua")
dofile(MP .. "/ui/edit_maillists.lua")
dofile(MP .. "/ui/compose.lua")
dofile(MP .. "/ui/options.lua")
dofile(MP .. "/ui/settings.lua")
dofile(MP .. "/ui/about.lua")
-- helper function for tabbed overview -- helper function for tabbed overview
function mail.show_mail_menu(playername, sortfield, sortdirection, filter) function mail.show_mail_menu(playername, sortfield, sortdirection, filter)

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:maillists" local FORMNAME = "mail:maillists"
@ -9,7 +9,7 @@ local maillists_formspec = "size[8,9;]" .. mail.theme .. [[
button[6,1.60;2,0.5;delete;]] .. S("Delete") .. [[] button[6,1.60;2,0.5;delete;]] .. S("Delete") .. [[]
button[6,8.25;2,0.5;back;]] .. S("Back") .. [[] button[6,8.25;2,0.5;back;]] .. S("Back") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0;5.75,9;maillists;]] .. mail.get_color("header") .. "," .. S("Name") .. "," .. S("Note") table[0,0;5.75,9;maillists;]] .. mail.colors.header .. "," .. S("Name") .. "," .. S("Note")
function mail.show_maillists(name) function mail.show_maillists(name)
local formspec = { maillists_formspec } local formspec = { maillists_formspec }
@ -19,14 +19,14 @@ function mail.show_maillists(name)
for _, maillist in ipairs(maillists) do for _, maillist in ipairs(maillists) do
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = "@" .. core.formspec_escape(maillist.name) formspec[#formspec + 1] = "@" .. minetest.formspec_escape(maillist.name)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if maillist.desc ~= "" then if maillist.desc ~= "" then
if string.len(maillist.desc or "") > 30 then if string.len(maillist.desc) > 30 then
formspec[#formspec + 1] = core.formspec_escape(string.sub(maillist.desc, 1, 27)) formspec[#formspec + 1] = minetest.formspec_escape(string.sub(maillist.desc, 1, 27))
formspec[#formspec + 1] = "..." formspec[#formspec + 1] = "..."
else else
formspec[#formspec + 1] = core.formspec_escape(maillist.desc) formspec[#formspec + 1] = minetest.formspec_escape(maillist.desc)
end end
else else
formspec[#formspec + 1] = S("(No description)") formspec[#formspec + 1] = S("(No description)")
@ -40,10 +40,10 @@ function mail.show_maillists(name)
else else
formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No maillist") .. "]" formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No maillist") .. "]"
end end
core.show_formspec(name, FORMNAME, table.concat(formspec, "")) minetest.show_formspec(name, FORMNAME, table.concat(formspec, ""))
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
@ -52,7 +52,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
local maillists = mail.get_maillists(name) local maillists = mail.get_maillists(name)
if fields.maillists then if fields.maillists then
local evt = core.explode_table_event(fields.maillists) local evt = minetest.explode_table_event(fields.maillists)
mail.selected_idxs.maillists[name] = evt.row - 1 mail.selected_idxs.maillists[name] = evt.row - 1
if evt.type == "DCL" and maillists[mail.selected_idxs.maillists[name]] then if evt.type == "DCL" and maillists[mail.selected_idxs.maillists[name]] then
local maillist = mail.get_maillist_by_name(name, maillists[mail.selected_idxs.maillists[name]].name) local maillist = mail.get_maillist_by_name(name, maillists[mail.selected_idxs.maillists[name]].name)
@ -85,7 +85,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
-- except if it was the last. Then determine the new last -- except if it was the last. Then determine the new last
local found = false local found = false
local last = nil local last = nil
for k in mail.pairs_by_keys(maillists) do for k in mail.pairsByKeys(maillists) do
if found then if found then
mail.selected_idxs.maillists[name] = k mail.selected_idxs.maillists[name] = k
break break

View file

@ -1,67 +1,47 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:message" local FORMNAME = "mail:message"
local function interleave_msg(body)
return "> " .. (body or ""):gsub("\n", "\n> ")
end
function mail.show_message(name, id) function mail.show_message(name, id)
local message = mail.get_message(name, id) local message = mail.get_message(name, id)
if not message then
-- message not found or vanished
return
end
mail.selected_idxs.message[name] = id
local formspec = [[ local formspec = [[
size[10,10] size[8,9]
box[0,0;7,1.9;]] .. mail.get_color("highlighted") .. [[] box[0,0;7,1.9;]] .. mail.colors.highlighted .. [[]
button[9.25,0.15;0.75,0.5;back;X] button[7.25,0.15;0.75,0.5;back;X]
button[7.25,-0.07;2,1;receivers;]] .. S("Receivers") .. [[]
label[0.2,0.1;]] .. S("From") .. [[: %s] label[0.2,0.1;]] .. S("From") .. [[: %s]
label[0.2,0.5;]] .. S("To") .. [[: %s] label[0.2,0.5;]] .. S("To") .. [[: %s]
label[0.2,0.9;]] .. S("CC") .. [[: %s] label[0.2,0.9;]] .. S("CC") .. [[: %s]
label[0.2,1.3;]] .. S("Date") .. [[: %s] label[0.2,1.3;]] .. S("Date") .. [[: %s]
tooltip[0.2,1.3;4.8,0.4;]] .. mail.time_ago(message.time) .. [[] tooltip[0.2,1.3;4.8,0.4;]] .. mail.time_ago(message.time) .. [[]
button[5.1,1;2,1;receivers;]] .. S("Receivers") .. [[]
label[0,2.1;]] .. S("Subject") .. [[: %s] label[0,2.1;]] .. S("Subject") .. [[: %s]
textarea[0.25,2.6;7.25,8.75;;;%s] textarea[0.25,2.6;8,7.0;;;%s]
button[7.25,1.0;2.75,1;reply;]] .. S("Reply") .. [[] button[0,8.5;2,1;reply;]] .. S("Reply") .. [[]
button[7.25,1.8;2.75,1;replyall;]] .. S("Reply all") .. [[] button[2,8.5;2,1;replyall;]] .. S("Reply all") .. [[]
button[7.25,2.6;2.75,1;forward;]] .. S("Forward") .. [[] button[4,8.5;2,1;forward;]] .. S("Forward") .. [[]
button[6,8.5;2,1;delete;]] .. S("Delete") .. [[]
button[7.25,3.6;2.75,1;markspam;]] .. S("Mark Spam") .. [[]
button[7.25,4.4;2.75,1;unmarkspam;]] .. S("Unmark Spam") .. [[]
button[7.25,5.4;2.75,1;togglemute;]] .. S("(Un)mute sender") .. [[]
box[7.25,6.4;2.5,2.75;]] .. mail.get_color("disabled") .. [[]
button[7.25,9.25;2.75,1;delete;]] .. S("Delete") .. [[]
tooltip[reply;]] .. S("Reply only to the sender") .. [[] tooltip[reply;]] .. S("Reply only to the sender") .. [[]
tooltip[replyall;]] .. S("Reply to all involved people") .. [[] tooltip[replyall;]] .. S("Reply to all involved people") .. [[]
tooltip[forward;]] .. S("Transfer message to other people") .. [[] tooltip[forward;]] .. S("Transfer message to other people") .. [[]
]] .. mail.theme ]] .. mail.theme
local from = core.formspec_escape(message.from) or "" local from = minetest.formspec_escape(message.from) or ""
local to = core.formspec_escape(message.to) or "" local to = minetest.formspec_escape(message.to) or ""
if string.len(to) > 70 then to = string.sub(to, 1, 67) .. "..." end if string.len(to) > 70 then to = string.sub(to, 1, 67) .. "..." end
local cc = core.formspec_escape(message.cc) or "" local cc = minetest.formspec_escape(message.cc) or ""
if string.len(cc) > 50 then cc = string.sub(cc, 1, 47) .. "..." end if string.len(cc) > 50 then cc = string.sub(cc, 1, 47) .. "..." end
local date = type(message.time) == "number" local date = type(message.time) == "number"
and core.formspec_escape(os.date(mail.get_setting(name, "date_format"), and minetest.formspec_escape(os.date(mail.get_setting(name, "date_format"), message.time)) or ""
message.time+3600*mail.get_setting(name, "timezone_offset"))) or "" local subject = minetest.formspec_escape(message.subject) or ""
local subject = core.formspec_escape(message.subject) or "" local body = minetest.formspec_escape(message.body) or ""
local body = core.formspec_escape(message.body) or ""
formspec = string.format(formspec, from, to, cc, date, subject, body) formspec = string.format(formspec, from, to, cc, date, subject, body)
if not message.read and mail.get_setting(name, "auto_marking_read") then if not message.read and mail.get_setting(name, "auto_marking_read") then
@ -69,27 +49,30 @@ function mail.show_message(name, id)
mail.mark_read(name, id) mail.mark_read(name, id)
end end
core.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
end end
function mail.reply(name, message) function mail.reply(name, message)
if not message then if not message then
-- TODO: workaround for https://github.com/mt-mods/mail/issues/84 -- TODO: workaround for https://github.com/mt-mods/mail/issues/84
core.log("error", "[mail] reply called with nil message for player: " .. name) minetest.log("error", "[mail] reply called with nil message for player: " .. name)
core.log("error", "[mail] current mail-context: " .. dump(mail.selected_idxs)) minetest.log("error", "[mail] current mail-context: " .. dump(mail.selected_idxs))
return return
end end
mail.show_compose(name, message.from, "Re: "..message.subject, interleave_msg(message.body)) local replyfooter = "Type your reply here.\n\n--Original message follows--\n" ..message.body
mail.show_compose(name, message.from, "Re: "..message.subject, replyfooter)
end end
function mail.replyall(name, message) function mail.replyall(name, message)
if not message then if not message then
-- TODO: workaround for https://github.com/mt-mods/mail/issues/84 -- TODO: workaround for https://github.com/mt-mods/mail/issues/84
core.log("error", "[mail] replyall called with nil message for player: " .. name) minetest.log("error", "[mail] replyall called with nil message for player: " .. name)
core.log("error", "[mail] current mail-context: " .. dump(mail.selected_idxs)) minetest.log("error", "[mail] current mail-context: " .. dump(mail.selected_idxs))
return return
end end
local replyfooter = "Type your reply here.\n\n--Original message follows--\n" ..message.body
-- new recipients are the sender plus the original recipients, minus ourselves -- new recipients are the sender plus the original recipients, minus ourselves
local recipients = message.to or "" local recipients = message.to or ""
if message.from ~= nil then if message.from ~= nil then
@ -114,21 +97,30 @@ function mail.replyall(name, message)
end end
cc = mail.concat_player_list(cc) cc = mail.concat_player_list(cc)
mail.show_compose(name, recipients, "Re: "..message.subject, interleave_msg(message.body), cc) mail.show_compose(name, recipients, "Re: "..message.subject, replyfooter, cc)
end end
function mail.forward(name, message) function mail.forward(name, message)
mail.show_compose(name, "", "Fw: " .. (message.subject or ""), interleave_msg(message.body)) local fwfooter = "Type your message here.\n\n--Original message follows--\n" .. (message.body or "")
mail.show_compose(name, "", "Fw: " .. (message.subject or ""), fwfooter)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
local name = player:get_player_name() local name = player:get_player_name()
local entry = mail.get_storage_entry(name)
local message = mail.get_message(name, mail.selected_idxs.message[name]) local message = ""
if mail.selected_idxs.inbox[name] and mail.selected_idxs.boxtab[name] == 1 then
message = mail.get_message(name, mail.selected_idxs.inbox[name][#mail.selected_idxs.inbox[name]])
elseif mail.selected_idxs.outbox[name] and mail.selected_idxs.boxtab[name] == 2 then
message = mail.get_message(name, mail.selected_idxs.outbox[name][#mail.selected_idxs.outbox[name]])
elseif mail.selected_idxs.trash[name] and mail.selected_idxs.boxtab[name] == 4 then
message = mail.get_message(name, entry.trash[mail.selected_idxs.trash[name]].id)
end
if fields.back then if fields.back then
mail.show_mail_menu(name) mail.show_mail_menu(name)
@ -143,22 +135,6 @@ core.register_on_player_receive_fields(function(player, formname, fields)
elseif fields.forward then elseif fields.forward then
mail.forward(name, message) mail.forward(name, message)
elseif fields.markspam then
mail.mark_spam(name, message.id)
elseif fields.unmarkspam then
mail.unmark_spam(name, message.id)
elseif fields.togglemute then
local mutes = table.copy(mail.get_setting(name, "mute_list"))
local mute_indexof = table.indexof(mutes, message.from)
if mute_indexof == -1 then -- mute
table.insert(mutes, message.from)
else -- unmute
table.remove(mutes, mute_indexof)
end
mail.set_setting(name, "mute_list", mutes)
elseif fields.delete then elseif fields.delete then
if mail.get_setting(name, "trash_move_enable") and mail.selected_idxs.boxtab[name] ~= 4 then if mail.get_setting(name, "trash_move_enable") and mail.selected_idxs.boxtab[name] ~= 4 then
mail.trash_mail(name, message.id) mail.trash_mail(name, message.id)

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
function mail.show_outbox(name, sortfieldindex, sortdirection, filter) function mail.show_outbox(name, sortfieldindex, sortdirection, filter)
sortfieldindex = tonumber(sortfieldindex or mail.selected_idxs.sortfield[name]) sortfieldindex = tonumber(sortfieldindex or mail.selected_idxs.sortfield[name])
@ -33,7 +33,7 @@ function mail.show_outbox(name, sortfieldindex, sortdirection, filter)
trash_tab = "," .. S("Trash") trash_tab = "," .. S("Trash")
end end
local outbox_formspec = "size[8.5,11;]" .. mail.theme .. [[ local outbox_formspec = "size[8.5,10;]" .. mail.theme .. [[
tabheader[0.3,1;boxtab;]] .. tabheader[0.3,1;boxtab;]] ..
S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. trash_tab .. [[;2;false;false] S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. trash_tab .. [[;2;false;false]
@ -43,30 +43,29 @@ function mail.show_outbox(name, sortfieldindex, sortdirection, filter)
button[6,2.45;2.5,0.5;replyall;]] .. S("Reply all") .. [[] button[6,2.45;2.5,0.5;replyall;]] .. S("Reply all") .. [[]
button[6,3.20;2.5,0.5;forward;]] .. S("Forward") .. [[] button[6,3.20;2.5,0.5;forward;]] .. S("Forward") .. [[]
button[6,3.95;2.5,0.5;delete;]] .. S("Delete") .. [[] button[6,3.95;2.5,0.5;delete;]] .. S("Delete") .. [[]
button[6,8.0;2.5,0.5;contacts;]] .. S("Contacts") .. [[] button[6,6.8;2.5,0.5;contacts;]] .. S("Contacts") .. [[]
button[6,8.8;2.5,0.5;maillists;]] .. S("Mail lists") .. [[] button[6,7.6;2.5,0.5;maillists;]] .. S("Mail lists") .. [[]
button[6,9.7;2.5,0.5;options;]] .. S("Options") .. [[] button[6,8.7;2.5,0.5;options;]] .. S("Options") .. [[]
button_exit[6,10.5;2.5,0.5;quit;]] .. S("Close") .. [[] button_exit[6,9.5;2.5,0.5;quit;]] .. S("Close") .. [[]
tooltip[reply;]] .. S("Reply only to the sender") .. [[] tooltip[reply;]] .. S("Reply only to the sender") .. [[]
tooltip[replyall;]] .. S("Reply to all involved people") .. [[] tooltip[replyall;]] .. S("Reply to all involved people") .. [[]
tooltip[forward;]] .. S("Transfer message to other people") .. [[] tooltip[forward;]] .. S("Transfer message to other people") .. [[]
dropdown[0,9.5;2,0.5;sortfield;]] .. dropdown[0,8.5;2,0.5;sortfield;]] ..
S("To") .. "," .. S("Subject") .. "," .. S("Date") .. [[;]] .. sortfieldindex .. [[;true] S("To") .. "," .. S("Subject") .. "," .. S("Date") .. [[;]] .. sortfieldindex .. [[;true]
dropdown[2.0,9.5;2,0.5;sortdirection;]] .. dropdown[2.0,8.5;2,0.5;sortdirection;]] ..
S("Ascending") .. "," .. S("Descending") .. [[;]] .. sortdirection .. [[;true] S("Ascending") .. "," .. S("Descending") .. [[;]] .. sortdirection .. [[;true]
field[4.25,9.95;1.4,0.5;filter;]] .. S("Filter") .. [[:;]] .. filter .. [[] field[4.25,8.95;1.4,0.5;filter;]] .. S("Filter") .. [[:;]] .. filter .. [[]
image_button[5.14,9.5;0.85,0.85;search.png;search;] button[5.14,8.62;0.85,0.5;search;Q]
checkbox[0,10.1;multipleselection;]] .. S("Allow multiple selection") .. [[;]] .. checkbox[0,9.1;multipleselection;]] .. S("Allow multiple selection") .. [[;]] ..
tostring(mail.selected_idxs.multipleselection[name]) .. [[] tostring(mail.selected_idxs.multipleselection[name]) .. [[]
label[0,10.65;]] .. label[0,9.65;]] .. S("@1 of @2 selected", tostring(#mail.selected_idxs.outbox[name]), tostring(#messages)) ..[[]
S("@1 of @2 selected", tostring(#mail.selected_idxs.outbox[name]), tostring(#messages)) .. [[] button[3.5,9.5;2.5,0.5;selectall;]] .. S("(Un)select all") .. [[]
button[3.5,10.5;2.5,0.5;selectall;]] .. S("(Un)select all") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0.7;5.75,8.45;outbox;]] .. mail.get_color("header") .. "," .. S("To") .. "," .. S("Subject") table[0,0.7;5.75,7.45;outbox;]] .. mail.colors.header .. "," .. S("To") .. "," .. S("Subject")
local formspec = { outbox_formspec } local formspec = { outbox_formspec }
mail.message_drafts[name] = nil mail.message_drafts[name] = nil
@ -74,7 +73,6 @@ function mail.show_outbox(name, sortfieldindex, sortdirection, filter)
if #messages > 0 then if #messages > 0 then
for _, message in ipairs(messages) do for _, message in ipairs(messages) do
local selected_id = 0 local selected_id = 0
local displayed_color = {}
-- check if message is in selection list and return its id -- check if message is in selection list and return its id
if mail.selected_idxs.outbox[name] and #mail.selected_idxs.outbox[name] > 0 then if mail.selected_idxs.outbox[name] and #mail.selected_idxs.outbox[name] > 0 then
for i, selected_msg in ipairs(mail.selected_idxs.outbox[name]) do for i, selected_msg in ipairs(mail.selected_idxs.outbox[name]) do
@ -85,23 +83,24 @@ function mail.show_outbox(name, sortfieldindex, sortdirection, filter)
end end
end end
if selected_id > 0 then if selected_id > 0 then
table.insert(displayed_color, "selected") formspec[#formspec + 1] = "," .. mail.colors.selected
else
formspec[#formspec + 1] = ","
end end
formspec[#formspec + 1] = "," .. mail.get_color(displayed_color)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if string.len(message.to) > 20 then if string.len(message.to) > 20 then
formspec[#formspec + 1] = core.formspec_escape(string.sub(message.to, 1, 17)) formspec[#formspec + 1] = minetest.formspec_escape(string.sub(message.to, 1, 17))
formspec[#formspec + 1] = "..." formspec[#formspec + 1] = "..."
else else
formspec[#formspec + 1] = core.formspec_escape(message.to) formspec[#formspec + 1] = minetest.formspec_escape(message.to)
end end
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if message.subject ~= "" then if message.subject ~= "" then
if string.len(message.subject) > 30 then if string.len(message.subject) > 30 then
formspec[#formspec + 1] = core.formspec_escape(string.sub(message.subject, 1, 27)) formspec[#formspec + 1] = minetest.formspec_escape(string.sub(message.subject, 1, 27))
formspec[#formspec + 1] = "..." formspec[#formspec + 1] = "..."
else else
formspec[#formspec + 1] = core.formspec_escape(message.subject) formspec[#formspec + 1] = minetest.formspec_escape(message.subject)
end end
else else
formspec[#formspec + 1] = S("(No subject)") formspec[#formspec + 1] = S("(No subject)")
@ -112,5 +111,5 @@ function mail.show_outbox(name, sortfieldindex, sortdirection, filter)
formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No mail") .. "]" formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("No mail") .. "]"
end end
core.show_formspec(name, "mail:outbox", table.concat(formspec, "")) minetest.show_formspec(name, "mail:outbox", table.concat(formspec, ""))
end end

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = core.get_translator("mail") local S = minetest.get_translator("mail")
local FORMNAME = "mail:receivers" local FORMNAME = "mail:receivers"
@ -9,7 +9,7 @@ function mail.show_receivers(name, id)
local formspec = [[ local formspec = [[
size[8,6] size[8,6]
box[0,0;7,1.1;]] .. mail.get_color("highlighted") .. [[] box[0,0;7,1.1;]] .. mail.colors.highlighted .. [[]
button[7.25,0.15;0.75,0.5;back;X] button[7.25,0.15;0.75,0.5;back;X]
@ -23,28 +23,36 @@ function mail.show_receivers(name, id)
table[4,1.5;3.8,4.5;cc;%s] table[4,1.5;3.8,4.5;cc;%s]
]] .. mail.theme ]] .. mail.theme
local from = core.formspec_escape(message.from) or "" local from = minetest.formspec_escape(message.from) or ""
local to = mail.parse_player_list(message.to or "") local to = mail.parse_player_list(message.to or "")
local to_str = mail.get_color("header") .. "," .. S("To") .. ",," local to_str = mail.colors.header .. "," .. S("To") .. ",,"
to_str = to_str .. table.concat(to, ",,") to_str = to_str .. table.concat(to, ",,")
local cc = mail.parse_player_list(message.cc or "") local cc = mail.parse_player_list(message.cc or "")
local cc_str = mail.get_color("header") .. "," .. S("CC") .. ",," local cc_str = mail.colors.header .. "," .. S("CC") .. ",,"
cc_str = cc_str .. table.concat(cc, ",,") cc_str = cc_str .. table.concat(cc, ",,")
local date = type(message.time) == "number" local date = type(message.time) == "number"
and core.formspec_escape(os.date(mail.get_setting(name, "date_format"), message.time)) or "" and minetest.formspec_escape(os.date(mail.get_setting(name, "date_format"), message.time)) or ""
formspec = string.format(formspec, from, date, to_str, cc_str) formspec = string.format(formspec, from, date, to_str, cc_str)
core.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
local name = player:get_player_name() local name = player:get_player_name()
local entry = mail.get_storage_entry(name)
local message_id = mail.selected_idxs.message[name] local message_id = ""
if mail.selected_idxs.inbox[name] and mail.selected_idxs.boxtab[name] == 1 then
message_id = mail.selected_idxs.inbox[name][#mail.selected_idxs.inbox[name]]
elseif mail.selected_idxs.outbox[name] and mail.selected_idxs.boxtab[name] == 2 then
message_id = mail.selected_idxs.outbox[name][#mail.selected_idxs.outbox[name]]
elseif mail.selected_idxs.trash[name] and mail.selected_idxs.boxtab[name] == 4 then
message_id = entry.trash[mail.selected_idxs.trash[name]].id
end
if fields.back then if fields.back then
mail.show_message(name, message_id) mail.show_message(name, message_id)

View file

@ -1,19 +1,19 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:selectcontact" local FORMNAME = "mail:selectcontact"
local select_contact_formspec = "size[8,9;]" .. mail.theme .. [[ local select_contact_formspec = "size[8,9;]" .. mail.theme .. [[
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0;3.5,9;contacts;]] .. mail.get_color("header") .. "," .. S("Name") .. "," .. S("Note") .. [[%s] table[0,0;3.5,9;contacts;]] .. mail.colors.header .. "," .. S("Name") .. "," .. S("Note") .. [[%s]
button[3.55,2.00;1.75,0.5;toadd; ]] .. S("Add") .. [[] button[3.55,2.00;1.75,0.5;toadd; ]] .. S("Add") .. [[]
button[3.55,2.75;1.75,0.5;toremove; ]] .. S("Remove") .. [[] button[3.55,2.75;1.75,0.5;toremove; ]] .. S("Remove") .. [[]
button[3.55,6.00;1.75,0.5;ccadd; ]] .. S("Add") .. [[] button[3.55,6.00;1.75,0.5;ccadd; ]] .. S("Add") .. [[]
button[3.55,6.75;1.75,0.5;ccremove; ]] .. S("Remove") .. [[] button[3.55,6.75;1.75,0.5;ccremove; ]] .. S("Remove") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[5.15,0.0;2.75,4.5;to;]] .. mail.get_color("header") .. "," .. S("To") .. ":," .. S("Note") .. [[%s] table[5.15,0.0;2.75,4.5;to;]] .. mail.colors.header .. "," .. S("To") .. ":," .. S("Note") .. [[%s]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[5.15,4.6;2.75,4.5;cc;]] .. mail.get_color("header") .. "," .. S("CC") .. ":," .. S("Note") .. [[%s] table[5.15,4.6;2.75,4.5;cc;]] .. mail.colors.header .. "," .. S("CC") .. ":," .. S("Note") .. [[%s]
button[3.55,8.25;1.75,0.5;back;]] .. S("Back") .. [[] button[3.55,8.25;1.75,0.5;back;]] .. S("Back") .. [[]
]] ]]
@ -39,10 +39,10 @@ function mail.show_select_contact(name, to, cc)
bcc = "" bcc = ""
end]]-- end]]--
formspec = string.format(formspec, contacts, to, cc)--, bcc() formspec = string.format(formspec, contacts, to, cc)--, bcc()
core.show_formspec(name, FORMNAME, formspec) minetest.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
@ -60,7 +60,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
bcc = "bccremove" bcc = "bccremove"
}) do }) do
if fields[k] then if fields[k] then
local evt = core.explode_table_event(fields[k]) local evt = minetest.explode_table_event(fields[k])
mail.selected_idxs[k][name] = evt.row - 1 mail.selected_idxs[k][name] = evt.row - 1
if evt.type == "DCL" and mail.selected_idxs[k][name] then if evt.type == "DCL" and mail.selected_idxs[k][name] then
fields[action] = true fields[action] = true
@ -75,7 +75,7 @@ core.register_on_player_receive_fields(function(player, formname, fields)
if fields[v.."add"] then if fields[v.."add"] then
update = true update = true
if mail.selected_idxs.contacts[name] then if mail.selected_idxs.contacts[name] then
for k, contact, i in mail.pairs_by_keys(contacts) do for k, contact, i in mail.pairsByKeys(contacts) do
if k == mail.selected_idxs.contacts[name] or i == mail.selected_idxs.contacts[name] then if k == mail.selected_idxs.contacts[name] or i == mail.selected_idxs.contacts[name] then
local list = mail.parse_player_list(draft[v]) local list = mail.parse_player_list(draft[v])
list[#list+1] = contact.name list[#list+1] = contact.name

View file

@ -1,241 +1,86 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local FORMNAME = "mail:settings" local FORMNAME = "mail:settings"
local function get_settings_groups(parent) local date_formats = {"%Y-%m-%d %X", "%d/%m/%y %X", "%A %d %B %Y %X"}
-- generate ordered list of settings
local groups = {}
for _, g in ipairs(mail.settings_groups) do
if (g.parent or 0) == parent then
table.insert(groups, g)
-- insert sub groups just after the parent group
table.insert_all(groups, get_settings_groups(g.name))
end
end
return groups
end
local groups_labels = {}
local ordered_groups = get_settings_groups(0)
local tree_indent = 0
for i, g in ipairs(ordered_groups) do
if not g.parent then tree_indent = 0
elseif i > 1 and g.parent == ordered_groups[i-1].name then tree_indent = tree_indent + 1
elseif i > 1 and g.parent ~= ordered_groups[i-1].parent then tree_indent = tree_indent - 1
end
table.insert(groups_labels, tostring(tree_indent))
table.insert(groups_labels, g.label)
end
local groups_str = table.concat(groups_labels, ",")
function mail.show_settings(name) function mail.show_settings(name)
local group_index = 1 -- date formats prepare
mail.selected_idxs.settings_group[name] = mail.selected_idxs.settings_group[name] or mail.settings_groups[1].name local dates_now = {}
local previous_date_format = mail.get_setting(name, "date_format")
for i, g in ipairs(ordered_groups) do local date_dropdown_index = 1
if g.name == mail.selected_idxs.settings_group[name] then for i, f in pairs(date_formats) do
group_index = i table.insert(dates_now, os.date(f, os.time()))
break if f == previous_date_format then date_dropdown_index = i end
end
end end
local date_dropdown_str = table.concat(dates_now, ",")
local formspec = [[ local formspec = [[
size[10,6;] size[10,6;]
tabheader[0,0;optionstab;]] .. S("Settings") .. "," .. S("About") .. [[;1;false;false] tabheader[0.3,1;optionstab;]] .. S("Settings") .. "," .. S("About") .. [[;1;false;false]
button[9.35,0;0.75,0.5;back;X] button[9.35,0;0.75,0.5;back;X]
tablecolumns[tree;text] box[0,0.8;3,0.45;]] .. mail.colors.highlighted .. [[]
table[0,0.775;3,4.5;groups;]] .. groups_str .. [[;]] .. group_index .. [[] label[0.2,0.8;]] .. S("Notifications") .. [[]
checkbox[0,1.2;chat_notifications;]] .. S("Chat notifications") .. [[;]] ..
tostring(mail.get_setting(name, "chat_notifications")) .. [[]
checkbox[0,1.6;onjoin_notifications;]] .. S("On join notifications") .. [[;]] ..
tostring(mail.get_setting(name, "onjoin_notifications")) .. [[]
checkbox[0,2.0;hud_notifications;]] .. S("HUD notifications") .. [[;]] ..
tostring(mail.get_setting(name, "hud_notifications")) .. [[]
checkbox[0,2.4;sound_notifications;]] .. S("Sound notifications") .. [[;]] ..
tostring(mail.get_setting(name, "sound_notifications")) .. [[]
box[0,0;3,0.45;]] .. mail.get_color("highlighted") .. [[] box[5,0.8;3,0.45;]] .. mail.colors.highlighted .. [[]
label[0.2,0;]] .. mail.settings_groups[group_index].label .. [[] label[5.2,0.8;]] .. S("Message list") .. [[]
checkbox[5,1.2;unreadcolorenable;]] .. S("Show unread in different color") .. [[;]] ..
tostring(mail.get_setting(name, "unreadcolorenable")) .. [[]
checkbox[5,1.6;cccolorenable;]] .. S("Show CC/BCC in different color") .. [[;]] ..
tostring(mail.get_setting(name, "cccolorenable")) .. [[]
button[0,5.65;2.5,0.5;reset;]] .. S("Reset") .. [[] label[5,2.6;]] .. S("Default sorting fields") .. [[]
button[7.5,5.65;2.5,0.5;save;]] .. S("Save") .. [[] dropdown[5.5,3.0;2,0.5;defaultsortfield;]] ..
]] S("From/To") .. "," .. S("Subject") .. "," .. S("Date") .. [[;]] ..
tostring(mail.get_setting(name, "defaultsortfield")) .. [[;true]
dropdown[7.5,3.0;2,0.5;defaultsortdirection;]] ..
S("Ascending") .. "," .. S("Descending") .. [[;]] ..
tostring(mail.get_setting(name, "defaultsortdirection")) .. [[;true]
local x = 3.5 box[0,3.2;3,0.45;]] .. mail.colors.highlighted .. [[]
local y = -0.7 label[0.2,3.2;]] .. S("Other") .. [[]
-- put settings in order checkbox[0,3.6;trash_move_enable;]] .. S("Move deleted messages to trash") .. [[;]] ..
local ordered_settings = {} tostring(mail.get_setting(name, "trash_move_enable")) .. [[]
for setting, data in pairs(mail.settings) do checkbox[0,4.0;auto_marking_read;]] .. S("Automatic marking read") .. [[;]] ..
if data.group == mail.selected_idxs.settings_group[name] then tostring(mail.get_setting(name, "auto_marking_read")) .. [[]
table.insert(ordered_settings, setting) label[0.31,4.7;]] .. S("Date format:") .. [[]
end dropdown[2.7,4.6;4,0.5;date_format;]] .. date_dropdown_str .. [[;]] ..
end tostring(date_dropdown_index) .. [[;true]
table.sort(ordered_settings, function(a, b) return mail.settings[a].index < mail.settings[b].index end)
for _, setting in pairs(ordered_settings) do
local data = mail.settings[setting]
y = y + 0.4
local field_default = mail.selected_idxs[setting][name]
if field_default == nil then field_default = mail.get_setting(name, setting) end
if data.type == "bool" then
formspec = formspec .. [[
checkbox[]] .. x .. "," .. y .. ";" .. setting .. ";" ..
data.label .. ";" .. tostring(field_default) .. [[]
]]
if data.tooltip then
formspec = formspec .. [[
tooltip[]] .. setting .. ";" .. data.tooltip .. [[]
]]
end
elseif data.type == "string" then
y = y + 1
formspec = formspec .. [[
field[]] .. x+0.275 .. "," .. y .. ";3,0.5;" .. setting .. ";" .. data.label .. [[;]] ..
field_default .. [[]
]]
if data.tooltip then
formspec = formspec .. "tooltip[" .. setting .. ";" .. data.tooltip .. "]"
end
if data.dataset then
local formatted_dataset = table.copy(data.dataset)
if data.format then
for i, d in ipairs(formatted_dataset) do
formatted_dataset[i] = data.format(d)
end
end
local dataset_str = table.concat(formatted_dataset, ",")
local dataset_selected_id = 1
for i, d in ipairs(data.dataset) do
if d == field_default then
dataset_selected_id = i
break
end
end
formspec = formspec .. [[
dropdown[]] .. x+3 .. "," .. y-0.45 .. ";3,0.5;" .. "dataset_" .. setting .. ";" ..
dataset_str .. [[;]] .. dataset_selected_id .. [[;true]
]]
end
elseif data.type == "number" then
y = y + 1
formspec = formspec .. [[
field[]] .. x+0.275 .. "," .. y .. ";3,0.5;" .. setting .. ";" .. data.label .. [[;]] ..
tostring(field_default) .. [[]
]]
if data.tooltip then
formspec = formspec .. "tooltip[" .. setting .. ";" .. data.tooltip .. "]"
end
if data.dataset then
local formatted_dataset = table.copy(data.dataset)
if data.format then
for i, d in ipairs(formatted_dataset) do
formatted_dataset[i] = data.format(d)
end
end
local dataset_str = table.concat(formatted_dataset, ",")
local dataset_selected_id = 1
for i, d in ipairs(data.dataset) do
if d == field_default then
dataset_selected_id = i
break
end
end
formspec = formspec .. [[
dropdown[]] .. x+3 .. "," .. y-0.45 .. ";3,0.5;" .. "dataset_" .. setting .. ";" ..
dataset_str .. [[;]] .. dataset_selected_id .. [[;true]
]]
end
elseif data.type == "index" then
y = y + 0.2
local formatted_dataset = table.copy(data.dataset)
if data.format then
for i, d in ipairs(formatted_dataset) do
formatted_dataset[i] = data.format(d)
end
end
local dataset_str = table.concat(formatted_dataset, ",")
local dataset_selected_id = field_default
formspec = formspec .. [[
label[]] .. x .. "," .. y .. ";" .. data.label .. "]"
y = y + 0.4
formspec = formspec .. [[
dropdown[]] .. x .. "," .. y .. ";3,0.5;" .. setting .. ";" ..
dataset_str .. [[;]] .. dataset_selected_id .. [[;true]
]]
if data.tooltip then
formspec = formspec .. [[
tooltip[]] .. setting .. ";" .. data.tooltip .. [[]
]]
end
y = y + 0.2
elseif data.type == "list" then
y = y + 0.3
formspec = formspec .. [[
tablecolumns[color;text]
table[]] .. x-0.0125 .. "," .. y .. ";3.8125,2.5;" .. setting .. ";" ..
mail.get_color("header") .. "," .. data.label .. ",," ..
table.concat(field_default, ",,") .. "]"
y = y + 3.1 tooltip[chat_notifications;]] .. S("Receive a message in the chat when there is a new message") .. [[]
formspec = formspec .. [[ tooltip[onjoin_notifications;]] .. S("Receive a message at login when inbox isn't empty") .. [[]
field[]] .. x+0.275 .. "," .. y .. ";2.975,0.5;field_" .. setting .. [[;;] tooltip[hud_notifications;]] .. S("Show an HUD notification when inbox isn't empty") .. [[]
button[]] .. x+2.75 .. "," .. y-0.325 .. ";0.75,0.5;add_" .. setting .. [[;+] tooltip[sound_notifications;]] .. S("Play a sound when there is a new message") .. [[]
button[]] .. x+3.25 .. "," .. y-0.325 .. ";0.75,0.5;remove_" .. setting .. [[;-] tooltip[auto_marking_read;]] .. S("Mark a message as read when opened") .. [[]
]]
if data.tooltip then button[0,5.5;2.5,0.5;save;]] .. S("Save") .. [[]
formspec = formspec .. "tooltip[field_" .. setting .. ";" .. data.tooltip .. "]" button[2.7,5.5;2.5,0.5;reset;]] .. S("Reset") .. [[]
end ]] .. mail.theme
y = y - 0.4 minetest.show_formspec(name, FORMNAME, formspec)
end
end
formspec = formspec .. mail.theme
core.show_formspec(name, FORMNAME, formspec)
end end
core.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname ~= FORMNAME then if formname ~= FORMNAME then
return return
end end
local playername = player:get_player_name() local playername = player:get_player_name()
for setting, data in pairs(mail.settings) do
if fields[setting] or fields["add_" .. setting] or fields["remove_" .. setting] then
if data.type == "bool" then
mail.selected_idxs[setting][playername] = fields[setting] == "true"
break
elseif data.type == "string" then
if data.dataset and fields["dataset_" .. setting] then
mail.selected_idxs[setting][playername] = data.dataset[tonumber(fields["dataset_" .. setting])]
mail.show_settings(playername)
end
elseif data.type == "number" then
if data.dataset and fields["dataset_" .. setting] then
mail.selected_idxs[setting][playername] = data.dataset[tonumber(fields["dataset_" .. setting])]
mail.show_settings(playername)
end
elseif data.type == "index" then
mail.selected_idxs[setting][playername] = tonumber(fields[setting])
elseif data.type == "list" then
mail.selected_idxs[setting][playername] = mail.selected_idxs[setting][playername] or
mail.get_setting(playername, setting)
if fields[setting] then
local evt = core.explode_table_event(fields[setting])
mail.selected_idxs["index_" .. setting][playername] = evt.row-1
elseif fields["add_" .. setting] then
table.insert(mail.selected_idxs[setting][playername], fields["field_" .. setting])
elseif fields["remove_" .. setting] and mail.selected_idxs["index_" .. setting][playername] then
table.remove(mail.selected_idxs[setting][playername],
mail.selected_idxs["index_" .. setting][playername])
end
mail.show_settings(playername)
end
end
end
if fields.back then if fields.back then
mail.show_mail_menu(playername) mail.show_mail_menu(playername)
return return
elseif fields.groups then
local evt = core.explode_table_event(fields.groups)
mail.selected_idxs.settings_group[playername] = mail.settings_groups[tonumber(evt.row)].name
mail.show_settings(playername)
elseif fields.optionstab == "1" then elseif fields.optionstab == "1" then
mail.selected_idxs.optionstab[playername] = 1 mail.selected_idxs.optionstab[playername] = 1
@ -244,14 +89,47 @@ core.register_on_player_receive_fields(function(player, formname, fields)
mail.show_about(playername) mail.show_about(playername)
return return
elseif fields.chat_notifications then
mail.selected_idxs.chat_notifications[playername] = fields.chat_notifications == "true"
elseif fields.onjoin_notifications then
mail.selected_idxs.onjoin_notifications[playername] = fields.onjoin_notifications == "true"
elseif fields.hud_notifications then
mail.selected_idxs.hud_notifications[playername] = fields.hud_notifications == "true"
elseif fields.sound_notifications then
mail.selected_idxs.sound_notifications[playername] = fields.sound_notifications == "true"
elseif fields.unreadcolorenable then
mail.selected_idxs.unreadcolorenable[playername] = fields.unreadcolorenable == "true"
elseif fields.cccolorenable then
mail.selected_idxs.cccolorenable[playername] = fields.cccolorenable == "true"
elseif fields.trash_move_enable then
mail.selected_idxs.trash_move_enable[playername] = fields.trash_move_enable == "true"
elseif fields.auto_marking_read then
mail.selected_idxs.auto_marking_read[playername] = fields.auto_marking_read == "true"
elseif fields.save then elseif fields.save then
-- save settings -- checkboxes
for setting, _ in pairs(mail.settings) do mail.set_setting(playername, "chat_notifications", mail.selected_idxs.chat_notifications[playername])
local new_value = mail.selected_idxs[setting][playername] mail.set_setting(playername, "onjoin_notifications", mail.selected_idxs.onjoin_notifications[playername])
mail.selected_idxs[setting][playername] = nil mail.set_setting(playername, "hud_notifications", mail.selected_idxs.hud_notifications[playername])
if new_value == nil then new_value = mail.get_setting(playername, setting) end mail.set_setting(playername, "sound_notifications", mail.selected_idxs.sound_notifications[playername])
mail.set_setting(playername, setting, new_value) mail.set_setting(playername, "unreadcolorenable", mail.selected_idxs.unreadcolorenable[playername])
end mail.set_setting(playername, "cccolorenable", mail.selected_idxs.cccolorenable[playername])
mail.set_setting(playername, "trash_move_enable", mail.selected_idxs.trash_move_enable[playername])
mail.set_setting(playername, "auto_marking_read", mail.selected_idxs.auto_marking_read[playername])
-- dropdowns
local defaultsortfield = fields.defaultsortfield or mail.get_setting(playername, "defaultsortfield")
local defaultsortdirection = fields.defaultsortdirection or mail.get_setting(playername, "defaultsortdirection")
local date_format = date_formats[tonumber(fields.date_format)] or mail.get_setting(playername, "date_format")
mail.set_setting(playername, "defaultsortfield", tonumber(defaultsortfield))
mail.set_setting(playername, "defaultsortdirection", tonumber(defaultsortdirection))
mail.set_setting(playername, "date_format", date_format)
-- update visuals -- update visuals
mail.hud_update(playername, mail.get_storage_entry(playername).inbox) mail.hud_update(playername, mail.get_storage_entry(playername).inbox)
mail.show_settings(playername) mail.show_settings(playername)

View file

@ -1,7 +1,7 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
local trash_formspec = "size[8.5,11;]" .. mail.theme .. [[ local trash_formspec = "size[8.5,10;]" .. mail.theme .. [[
tabheader[0.3,1;boxtab;]] .. tabheader[0.3,1;boxtab;]] ..
S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. "," .. S("Trash") .. [[;4;false;false] S("Inbox") .. "," .. S("Outbox").. "," .. S("Drafts") .. "," .. S("Trash") .. [[;4;false;false]
@ -10,13 +10,13 @@ local trash_formspec = "size[8.5,11;]" .. mail.theme .. [[
button[6,1.70;2.5,0.5;restore;]] .. S("Restore") .. [[] button[6,1.70;2.5,0.5;restore;]] .. S("Restore") .. [[]
button[6,2.45;2.5,0.5;delete;]] .. S("Delete") .. [[] button[6,2.45;2.5,0.5;delete;]] .. S("Delete") .. [[]
button[6,3.20;2.5,0.5;empty;]] .. S("Empty") .. [[] button[6,3.20;2.5,0.5;empty;]] .. S("Empty") .. [[]
button[6,8.0;2.5,0.5;contacts;]] .. S("Contacts") .. [[] button[6,6.8;2.5,0.5;contacts;]] .. S("Contacts") .. [[]
button[6,8.8;2.5,0.5;maillists;]] .. S("Mail lists") .. [[] button[6,7.6;2.5,0.5;maillists;]] .. S("Mail lists") .. [[]
button[6,9.7;2.5,0.5;options;]] .. S("Options") .. [[] button[6,8.7;2.5,0.5;options;]] .. S("Options") .. [[]
button_exit[6,10.5;2.5,0.5;quit;]] .. S("Close") .. [[] button_exit[6,9.5;2.5,0.5;quit;]] .. S("Close") .. [[]
tablecolumns[color;text;text] tablecolumns[color;text;text]
table[0,0.7;5.75,10.35;trash;]] .. mail.get_color("header") .. "," .. S("From/To") .. "," .. S("Subject") table[0,0.7;5.75,9.35;trash;]] .. mail.colors.header .. "," .. S("From/To") .. "," .. S("Subject")
function mail.show_trash(name) function mail.show_trash(name)
@ -28,14 +28,14 @@ function mail.show_trash(name)
for _, message in ipairs(messages) do for _, message in ipairs(messages) do
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = core.formspec_escape(message.to) formspec[#formspec + 1] = minetest.formspec_escape(message.to)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if message.subject ~= "" then if message.subject ~= "" then
if string.len(message.subject) > 30 then if string.len(message.subject) > 30 then
formspec[#formspec + 1] = core.formspec_escape(string.sub(message.subject, 1, 27)) formspec[#formspec + 1] = minetest.formspec_escape(string.sub(message.subject, 1, 27))
formspec[#formspec + 1] = "..." formspec[#formspec + 1] = "..."
else else
formspec[#formspec + 1] = core.formspec_escape(message.subject) formspec[#formspec + 1] = minetest.formspec_escape(message.subject)
end end
else else
formspec[#formspec + 1] = S("(No subject)") formspec[#formspec + 1] = S("(No subject)")
@ -49,5 +49,5 @@ function mail.show_trash(name)
else else
formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("Trash is empty") .. "]" formspec[#formspec + 1] = "]label[2.25,4.5;" .. S("Trash is empty") .. "]"
end end
core.show_formspec(name, "mail:trash", table.concat(formspec, "")) minetest.show_formspec(name, "mail:trash", table.concat(formspec, ""))
end end

View file

@ -1,58 +0,0 @@
local generic_colors = {
header = "#999999",
selected = "#72FF63",
important = "#FFD700",
additional = "#CCCCDD",
highlighted = "#608631",
new = "#00F529",
warning = "#FF8800",
disabled = "#332222",
muted = "#CCCCCC",
}
local function get_base_color(c)
return generic_colors[c] or ""
end
local function hex2rgb(hex)
hex = hex:gsub("#","")
return {
r = tonumber("0x" .. hex:sub(1,2)),
g = tonumber("0x" .. hex:sub(3,4)),
b = tonumber("0x" .. hex:sub(5,6))
}
end
local function rgb2hex(rgb)
return "#" .. string.format("%x", rgb.r) .. string.format("%x", rgb.g) .. string.format("%x", rgb.b)
end
local function rgb_colors_mix(colors)
local R = 0
local G = 0
local B = 0
for _, c in ipairs(colors) do
R = R + c.r
G = G + c.g
B = B + c.b
end
R = math.floor(R / #colors + 0.5)
G = math.floor(G / #colors + 0.5)
B = math.floor(B / #colors + 0.5)
return {r=R,g=G,b=B}
end
function mail.get_color(mix)
if type(mix) == "string" then
return get_base_color(mix)
elseif #mix == 1 then
return get_base_color(mix[1])
else
local colors2mix = {}
for _, c in ipairs(mix) do
colors2mix[#colors2mix+1] = hex2rgb(get_base_color(c))
end
local mixed_color = rgb_colors_mix(colors2mix)
return rgb2hex(mixed_color)
end
end

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
function mail.compile_contact_list(name, selected, playernames) function mail.compile_contact_list(name, selected, playernames)
-- TODO: refactor this - not just compiles *a* list, but *the* list for the contacts screen (too inflexible) -- TODO: refactor this - not just compiles *a* list, but *the* list for the contacts screen (too inflexible)
@ -8,11 +8,11 @@ function mail.compile_contact_list(name, selected, playernames)
if playernames == nil then if playernames == nil then
local length = 0 local length = 0
for k, contact, i, l in mail.pairs_by_keys(contacts) do for k, contact, i, l in mail.pairsByKeys(contacts) do
if i == 1 then length = l end if i == 1 then length = l end
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = core.formspec_escape(contact.name) formspec[#formspec + 1] = minetest.formspec_escape(contact.name)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
local note = contact.note local note = contact.note
-- display an ellipsis if the note spans multiple lines -- display an ellipsis if the note spans multiple lines
@ -20,7 +20,7 @@ function mail.compile_contact_list(name, selected, playernames)
if idx ~= nil then if idx ~= nil then
note = string.sub(note, 1, idx-1) .. ' ...' note = string.sub(note, 1, idx-1) .. ' ...'
end end
formspec[#formspec + 1] = core.formspec_escape(note) formspec[#formspec + 1] = minetest.formspec_escape(note)
if type(selected) == "string" then if type(selected) == "string" then
if string.lower(selected) == k then if string.lower(selected) == k then
selected = i selected = i
@ -43,7 +43,7 @@ function mail.compile_contact_list(name, selected, playernames)
for i,c in ipairs(playernames) do for i,c in ipairs(playernames) do
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
formspec[#formspec + 1] = core.formspec_escape(c) formspec[#formspec + 1] = minetest.formspec_escape(c)
formspec[#formspec + 1] = "," formspec[#formspec + 1] = ","
if contacts[string.lower(c)] == nil then if contacts[string.lower(c)] == nil then
formspec[#formspec + 1] = "" formspec[#formspec + 1] = ""
@ -54,7 +54,7 @@ function mail.compile_contact_list(name, selected, playernames)
if idx ~= nil then if idx ~= nil then
note = string.sub(note, 1, idx-1) .. ' ...' note = string.sub(note, 1, idx-1) .. ' ...'
end end
formspec[#formspec + 1] = core.formspec_escape(note) formspec[#formspec + 1] = minetest.formspec_escape(note)
end end
if not selected then if not selected then
if type(selected) == "string" then if type(selected) == "string" then

View file

@ -1,9 +0,0 @@
-- sub files
local MP = core.get_modpath(core.get_current_modname())
dofile(MP .. "/util/normalize.lua")
dofile(MP .. "/util/colors.lua")
dofile(MP .. "/util/contact.lua")
dofile(MP .. "/util/settings.lua")
dofile(MP .. "/util/spam.lua")
dofile(MP .. "/util/time_ago.lua")
dofile(MP .. "/util/uuid.lua")

View file

@ -1,44 +1,18 @@
-- translation local has_canonical_name = minetest.get_modpath("canonical_name")
local S = mail.S
local function recursive_expand_recipient_names(sender, list, is_toplevel, recipients, undeliverable)
for _, name in ipairs(list) do
if not (recipients[name] or undeliverable[name] or (name == sender and not is_toplevel)) then
local succ, value
for _, handler in ipairs(mail.registered_recipient_handlers) do
succ, value = handler(sender, name)
if succ ~= nil then
break
end
end
local vtp = type(value)
if succ then
if vtp == "string" then
recursive_expand_recipient_names(sender, {value}, is_toplevel, recipients, undeliverable)
elseif vtp == "table" then
recursive_expand_recipient_names(sender, value, false, recipients, undeliverable)
elseif vtp == "function" then
recipients[name] = value
else
undeliverable[name] = S("The method of delivery to @1 is invalid.", name)
end
elseif succ == nil then
undeliverable[name] = S("The recipient @1 could not be identified.", name)
else
local reason = tostring(value) or S("@1 rejected your mail.", name)
undeliverable[name] = reason
end
end
end
end
--[[ --[[
return the field normalized (comma separated, single space) return the field normalized (comma separated, single space)
and add individual player names to recipient list and add individual player names to recipient list
--]] --]]
function mail.normalize_players_and_add_recipients(sender, field, recipients, undeliverable) function mail.normalize_players_and_add_recipients(field, recipients, undeliverable)
local order = mail.parse_player_list(field) local order = mail.parse_player_list(field)
recursive_expand_recipient_names(sender, order, true, recipients, undeliverable) for _, recipient_name in ipairs(order) do
if not minetest.player_exists(recipient_name) then
undeliverable[recipient_name] = true
else
recipients[recipient_name] = true
end
end
return mail.concat_player_list(order) return mail.concat_player_list(order)
end end
@ -47,23 +21,29 @@ function mail.parse_player_list(field)
return {} return {}
end end
local separator = ",%s" local separator = ", "
local pattern = "([^" .. separator .. "]+)" local pattern = "([^" .. separator .. "]+)"
-- get individual players -- get individual players
local player_set = {}
local order = {} local order = {}
for name in field:gmatch(pattern) do field:gsub(pattern, function(player_name)
table.insert(order, name) local lower = string.lower(player_name)
if not player_set[lower] then
if has_canonical_name then
player_name = canonical_name.get(player_name) or player_name
end end
player_set[lower] = player_name
order[#order+1] = player_name
end
end)
return order return order
end end
function mail.concat_player_list(order) function mail.concat_player_list(order)
-- turn list of players back into normalized string -- turn list of players back into normalized string
if order == nil or #order == 0 then
return ""
end
return table.concat(order, ", ") return table.concat(order, ", ")
end end

View file

@ -2,7 +2,7 @@
mtt.register("util/normalize_players_and_add_recipients", function(callback) mtt.register("util/normalize_players_and_add_recipients", function(callback)
local recipients = {} local recipients = {}
local undeliverable = {} local undeliverable = {}
local to = mail.normalize_players_and_add_recipients("sender", "player1,player2", recipients, undeliverable) local to = mail.normalize_players_and_add_recipients("player1,player2", recipients, undeliverable)
assert(to == "player1, player2") assert(to == "player1, player2")
assert(not next(undeliverable)) assert(not next(undeliverable))

View file

@ -1,113 +0,0 @@
-- translation
local S = mail.S
mail.settings = {
chat_notifications = {
type = "bool", default = true, group = "notifications", index = 1,
label = S("Chat notifications"), tooltip = S("Receive a message in the chat when there is a new message")
},
onjoin_notifications = {
type = "bool", default = true, group = "notifications", index = 2,
label = S("On join notifications"), tooltip = S("Receive a message at login when inbox isn't empty") },
hud_notifications = {
type = "bool", default = true, group = "notifications", index = 3,
label = S("HUD notifications"), tooltip = S("Show an HUD notification when inbox isn't empty")
},
sound_notifications = {
type = "bool", default = true, group = "notifications", index = 4,
label = S("Sound notifications"), tooltip = S("Play a sound when there is a new message")
},
unreadcolorenable = {
type = "bool", default = true, group = "message_list", index = 1,
label = S("Show unread in different color")
},
cccolorenable = {
type = "bool", default = true, group = "message_list", index = 2,
label = S("Show CC/BCC in different color")
},
defaultsortfield = {
type = "index", default = 3, group = "box_fields", index = 1,
label = S("Default sorting field"), dataset = { S("From/To"), S("Subject"), S("Date") }
},
defaultsortdirection = {
type = "index", default = 1, group = "box_fields", index = 2,
label = S("Default sorting direction"), dataset = { S("Ascending"), S("Descending") }
},
trash_move_enable = {
type = "bool", default = true, group = "other", index = 1,
label = S("Move deleted messages to trash")
},
auto_marking_read = {
type = "bool", default = true, group = "other", index = 2,
label = S("Automatic marking read"), tooltip = S("Mark a message as read when opened")
},
date_format = {
type = "string", default = "%Y-%m-%d %X", group = "date_and_time", index = 3, label = S("Date format"),
dataset = {"%Y-%m-%d %X", "%d/%m/%y %X", "%A %d %B %Y %X"}, format = os.date
},
timezone_offset = {
type = "number", default = 0, group = "date_and_time", index = 4,
label = S("Timezone offset"), tooltip = S("Offset to add to server time."),
},
mute_list = {
type = "list", default = {}, group = "spam", index = 1,
label = S("Mute list")
},
}
mail.settings_groups = {
{ name = "notifications", label = S("Notifications"), index = 1, parent = 0},
{ name = "message_list", label = S("Message list"), index = 2, parent = 0},
{ name = "box_fields", label = S("Fields"), index = 1, parent = "message_list"},
{ name = "spam", label = S("Spam"), index = 3, parent = 0},
{ name = "other", label = S("Other"), index = 4, parent = 0},
{ name = "date_and_time", label = S("Date and Time"), index = 1, parent = "other"}
}
for s, d in pairs(mail.settings) do
mail.selected_idxs[s] = {}
if d.type == "list" then
mail.selected_idxs["index_" .. s] = {}
end
end
function mail.settings.mute_list.check(name, value)
local valid_players = {}
for _, p in ipairs(value) do
if p ~= name and core.player_exists(p) then
table.insert(valid_players, p)
end
end
return valid_players
end
function mail.settings.mute_list.sync(name)
if core.get_modpath("beerchat") then
local players = {}
for other_player, _ in core.get_auth_handler().iterate() do
if beerchat.has_player_muted_player(name, other_player) then
table.insert(players, other_player)
end
end
return players
end
return nil
end
function mail.settings.mute_list.transfer(name, value)
if core.get_modpath("beerchat") then
for other_player, _ in core.get_auth_handler().iterate() do -- unmute all
if not beerchat.execute_callbacks("before_mute", name, other_player) then
return false
end
core.get_player_by_name(name):get_meta():set_string(
"beerchat:muted:" .. other_player, "")
end
for _, other_player in ipairs(value) do -- then mute only players in table
core.get_player_by_name(name):get_meta():set_string(
"beerchat:muted:" .. other_player, "true")
end
return true
end
return nil
end

View file

@ -1,39 +0,0 @@
local function caps_ratio(str)
local total_caps = 0
for i = 1, #str do -- iteration through each character
local c = str:sub(i,i)
if string.lower(c) ~= c then -- do not count digits as spam
total_caps = total_caps + 1
end
end
return total_caps/(#str or 1) -- avoid division by zero
end
local function words_ratio(str, ratio)
local words = {}
local split_str = str:split(" ")
for _, w in ipairs(split_str) do
if not words[w] then
words[w] = 0
else
words[w] = (words[w] or 0) + 1
end
end
for _, n in pairs(words) do
if n/#split_str >= ratio then
return true
end
end
return false
end
function mail.check_spam(message)
local spam_checks = {}
if caps_ratio(message.subject) == 1 or caps_ratio(message.body) > 0.4 then
table.insert(spam_checks, "caps")
end
if words_ratio(message.subject, 0.6) or words_ratio(message.body, 0.2) then
table.insert(spam_checks, "words")
end
return spam_checks
end

View file

@ -1,5 +1,5 @@
-- translation -- translation
local S = mail.S local S = minetest.get_translator("mail")
function mail.time_ago(t) function mail.time_ago(t)
local elapsed = os.time() - t local elapsed = os.time() - t