expose and test set_stock_translation to Python

This commit is contained in:
holger krekel
2019-10-09 10:58:06 +02:00
parent bdfd548779
commit da99bba025
10 changed files with 122 additions and 32 deletions

View File

@@ -403,12 +403,14 @@ int dc_set_config (dc_context_t* context, const char*
char* dc_get_config (dc_context_t* context, const char* key); char* dc_get_config (dc_context_t* context, const char* key);
/** /**
* Set (translated) stock string * Set stock string translation.
*
* The function will emit warnings if it returns an error state.
* *
* @param context The context object * @param context The context object
* @param stock_id the integer id of the stock message * @param stock_id the integer id of the stock message (DC_STR_*)
* @param stock_msg the message to be used * @param stock_msg the message to be used
* @return void * @return int (==0 on error, 1 on success)
*/ */
int dc_set_stock_translation(dc_context_t* context, uint32_t, const char* value); int dc_set_stock_translation(dc_context_t* context, uint32_t, const char* value);

View File

@@ -25,6 +25,7 @@ use num_traits::{FromPrimitive, ToPrimitive};
use deltachat::contact::Contact; use deltachat::contact::Contact;
use deltachat::context::Context; use deltachat::context::Context;
use deltachat::dc_tools::{as_path, as_str, dc_strdup, to_string_lossy, OsStrExt, StrExt}; use deltachat::dc_tools::{as_path, as_str, dc_strdup, to_string_lossy, OsStrExt, StrExt};
use deltachat::stock::StockMessage;
use deltachat::*; use deltachat::*;
// as C lacks a good and portable error handling, // as C lacks a good and portable error handling,
@@ -350,19 +351,25 @@ pub unsafe extern "C" fn dc_set_stock_translation(
) -> libc::c_int { ) -> libc::c_int {
if context.is_null() || stock_msg.is_null() { if context.is_null() || stock_msg.is_null() {
eprintln!("ignoring careless call to dc_set_stock_string"); eprintln!("ignoring careless call to dc_set_stock_string");
return; return 0;
} }
let msg = as_str(stock_msg); let msg = as_str(stock_msg).to_string();
let ffi_context = &*context; let ffi_context = &*context;
ffi_context ffi_context
.with_inner(|ctx| match ctx.set_stock_translation(stock_id, msg) { .with_inner(|ctx| match StockMessage::from_u32(stock_id) {
Ok(()) => 1, Some(id) => match ctx.set_stock_translation(id, msg) {
Err(err) => { Ok(()) => 1,
warn!(ctx, "could not set translation: {}", err); Err(err) => {
warn!(ctx, "set_stock_translation failed: {}", err);
0
}
},
None => {
warn!(ctx, "invalid stock message id {}", stock_id);
0 0
} }
}) })
.unwrap_or(()) .unwrap_or(0)
} }
#[no_mangle] #[no_mangle]

View File

@@ -71,6 +71,18 @@ class Account(object):
d[key.lower()] = value d[key.lower()] = value
return d return d
def set_stock_translation(self, id, string):
""" set stock translation string.
:param id: id of stock string (const.DC_STR_*)
:param value: string to set as new transalation
:returns: None
"""
string = string.encode("utf8")
res = lib.dc_set_stock_translation(self._dc_context, id, string)
if res == 0:
raise ValueError("could not set translation string")
def set_config(self, name, value): def set_config(self, name, value):
""" set configuration values. """ set configuration values.

View File

@@ -85,14 +85,61 @@ DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
DC_EVENT_GET_STRING = 2091 DC_EVENT_GET_STRING = 2091
DC_EVENT_FILE_COPIED = 2055 DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081 DC_EVENT_IS_OFFLINE = 2081
DC_STR_SELFNOTINGRP = 21
DC_PROVIDER_STATUS_OK = 1 DC_PROVIDER_STATUS_OK = 1
DC_PROVIDER_STATUS_PREPARATION = 2 DC_PROVIDER_STATUS_PREPARATION = 2
DC_PROVIDER_STATUS_BROKEN = 3 DC_PROVIDER_STATUS_BROKEN = 3
DC_STR_NOMESSAGES = 1
DC_STR_SELF = 2
DC_STR_DRAFT = 3
DC_STR_MEMBER = 4
DC_STR_CONTACT = 6
DC_STR_VOICEMESSAGE = 7
DC_STR_DEADDROP = 8
DC_STR_IMAGE = 9
DC_STR_VIDEO = 10
DC_STR_AUDIO = 11
DC_STR_FILE = 12
DC_STR_STATUSLINE = 13
DC_STR_NEWGROUPDRAFT = 14
DC_STR_MSGGRPNAME = 15
DC_STR_MSGGRPIMGCHANGED = 16
DC_STR_MSGADDMEMBER = 17
DC_STR_MSGDELMEMBER = 18
DC_STR_MSGGROUPLEFT = 19
DC_STR_GIF = 23
DC_STR_ENCRYPTEDMSG = 24
DC_STR_E2E_AVAILABLE = 25
DC_STR_ENCR_TRANSP = 27
DC_STR_ENCR_NONE = 28
DC_STR_CANTDECRYPT_MSG_BODY = 29
DC_STR_FINGERPRINTS = 30
DC_STR_READRCPT = 31
DC_STR_READRCPT_MAILBODY = 32
DC_STR_MSGGRPIMGDELETED = 33
DC_STR_E2E_PREFERRED = 34
DC_STR_CONTACT_VERIFIED = 35
DC_STR_CONTACT_NOT_VERIFIED = 36
DC_STR_CONTACT_SETUP_CHANGED = 37
DC_STR_ARCHIVEDCHATS = 40
DC_STR_STARREDMSGS = 41
DC_STR_AC_SETUP_MSG_SUBJECT = 42
DC_STR_AC_SETUP_MSG_BODY = 43
DC_STR_SELFTALK_SUBTITLE = 50
DC_STR_CANNOT_LOGIN = 60
DC_STR_SERVER_RESPONSE = 61
DC_STR_MSGACTIONBYUSER = 62
DC_STR_MSGACTIONBYME = 63
DC_STR_MSGLOCATIONENABLED = 64
DC_STR_MSGLOCATIONDISABLED = 65
DC_STR_LOCATION = 66
DC_STR_STICKER = 67
DC_STR_COUNT = 67
# end const generated # end const generated
def read_event_defines(f): def read_event_defines(f):
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_QR|DC_MSG|DC_STATE_|' rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_QR|DC_MSG|DC_STATE_|DC_STR|'
r'DC_CONTACT_ID_|DC_GCL|DC_CHAT|DC_PROVIDER)\S+)\s+([x\d]+).*') r'DC_CONTACT_ID_|DC_GCL|DC_CHAT|DC_PROVIDER)\S+)\s+([x\d]+).*')
for line in f: for line in f:
m = rex.match(line) m = rex.match(line)

View File

@@ -148,6 +148,27 @@ class TestOfflineChat:
chat.set_name("title2") chat.set_name("title2")
assert chat.get_name() == "title2" assert chat.get_name() == "title2"
def test_group_chat_creation_with_translation(self, ac1):
ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %1$s")
ac1._evlogger.consume_events()
with pytest.raises(ValueError):
ac1.set_stock_translation(const.DC_STR_NEWGROUPDRAFT, "xyz %2$s")
ac1._evlogger.get_matching("DC_EVENT_WARNING")
with pytest.raises(ValueError):
ac1.set_stock_translation(500, "xyz %1$s")
ac1._evlogger.get_matching("DC_EVENT_WARNING")
contact1 = ac1.create_contact("some1@hello.com", name="some1")
contact2 = ac1.create_contact("some2@hello.com", name="some2")
chat = ac1.create_group_chat(name="title1")
chat.add_contact(contact1)
chat.add_contact(contact2)
assert chat.get_name() == "title1"
assert contact1 in chat.get_contacts()
assert contact2 in chat.get_contacts()
assert not chat.is_promoted()
msg = chat.get_draft()
assert msg.text == "xyz title1"
@pytest.mark.parametrize("verified", [True, False]) @pytest.mark.parametrize("verified", [True, False])
def test_group_chat_qr(self, acfactory, ac1, verified): def test_group_chat_qr(self, acfactory, ac1, verified):
ac2 = acfactory.get_configured_offline_account() ac2 = acfactory.get_configured_offline_account()

View File

@@ -257,7 +257,7 @@ const DC_SHOW_EMAILS_ACCEPTED_CONTACTS: usize = 1;
const DC_SHOW_EMAILS_ALL: usize = 2; const DC_SHOW_EMAILS_ALL: usize = 2;
// TODO: Strings need some doumentation about used placeholders. // TODO: Strings need some doumentation about used placeholders.
// These constants are used to request strings using #DC_EVENT_GET_STRING. // These constants are used to set stock translation strings
const DC_STR_NOMESSAGES: usize = 1; const DC_STR_NOMESSAGES: usize = 1;
const DC_STR_SELF: usize = 2; const DC_STR_SELF: usize = 2;

View File

@@ -63,7 +63,7 @@ pub struct Context {
pub running_state: Arc<RwLock<RunningState>>, pub running_state: Arc<RwLock<RunningState>>,
/// Mutex to avoid generating the key for the user more than once. /// Mutex to avoid generating the key for the user more than once.
pub generating_key_mutex: Mutex<()>, pub generating_key_mutex: Mutex<()>,
pub translated_stockstrings: HashMap<usize, String>, pub translated_stockstrings: Arc<RwLock<HashMap<usize, String>>>,
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
@@ -146,7 +146,7 @@ impl Context {
probe_imap_network: Arc::new(RwLock::new(false)), probe_imap_network: Arc::new(RwLock::new(false)),
perform_inbox_jobs_needed: Arc::new(RwLock::new(false)), perform_inbox_jobs_needed: Arc::new(RwLock::new(false)),
generating_key_mutex: Mutex::new(()), generating_key_mutex: Mutex::new(()),
translated_stockstrings: HashMap::new(), translated_stockstrings: Arc::new(RwLock::new(HashMap::new())),
}; };
ensure!( ensure!(

View File

@@ -805,19 +805,12 @@ mod tests {
assert!(msg.contains("-----END PGP MESSAGE-----\n")); assert!(msg.contains("-----END PGP MESSAGE-----\n"));
} }
fn ac_setup_msg_cb(ctx: &Context, evt: Event) -> libc::uintptr_t {
match evt {
Event::GetString {
id: StockMessage::AcSetupMsgBody,
..
} => unsafe { "hello\r\nthere".strdup() as usize },
_ => logging_cb(ctx, evt),
}
}
#[test] #[test]
fn otest_render_setup_file_newline_replace() { fn otest_render_setup_file_newline_replace() {
let t = test_context(Some(Box::new(ac_setup_msg_cb))); let t = dummy_context();
t.ctx
.set_stock_translation(StockMessage::AcSetupMsgBody, "hello\r\nthere".to_string())
.unwrap();
configure_alice_keypair(&t.ctx); configure_alice_keypair(&t.ctx);
let msg = render_setup_file(&t.ctx, "pw").unwrap(); let msg = render_setup_file(&t.ctx, "pw").unwrap();
println!("{}", &msg); println!("{}", &msg);

View File

@@ -57,7 +57,7 @@ pub mod qr;
pub mod securejoin; pub mod securejoin;
mod smtp; mod smtp;
pub mod sql; pub mod sql;
mod stock; pub mod stock;
mod token; mod token;
#[macro_use] #[macro_use]
mod wrapmime; mod wrapmime;

View File

@@ -125,7 +125,7 @@ impl Context {
/// Set the stock string for the [StockMessage]. /// Set the stock string for the [StockMessage].
/// ///
pub fn set_stock_translation( pub fn set_stock_translation(
&mut self, &self,
id: StockMessage, id: StockMessage,
stockstring: String, stockstring: String,
) -> Result<(), Error> { ) -> Result<(), Error> {
@@ -143,7 +143,10 @@ impl Context {
id.fallback() id.fallback()
); );
} }
self.translated_stockstrings.insert(id as usize, stockstring); self.translated_stockstrings
.write()
.unwrap()
.insert(id as usize, stockstring);
Ok(()) Ok(())
} }
@@ -152,8 +155,13 @@ impl Context {
/// Return a translation (if it was set with set_stock_translation before) /// Return a translation (if it was set with set_stock_translation before)
/// or a default (English) string. /// or a default (English) string.
pub fn stock_str(&self, id: StockMessage) -> Cow<str> { pub fn stock_str(&self, id: StockMessage) -> Cow<str> {
match self.translated_stockstrings.get(&(id as usize)) { match self
Some(x) => Cow::Borrowed(x), .translated_stockstrings
.read()
.unwrap()
.get(&(id as usize))
{
Some(ref x) => Cow::Owned(x.to_string()),
None => Cow::Borrowed(id.fallback()), None => Cow::Borrowed(id.fallback()),
} }
} }
@@ -274,7 +282,7 @@ mod tests {
#[test] #[test]
fn test_set_stock_translation() { fn test_set_stock_translation() {
let mut t = dummy_context(); let t = dummy_context();
t.ctx t.ctx
.set_stock_translation(StockMessage::NoMessages, "xyz".to_string()) .set_stock_translation(StockMessage::NoMessages, "xyz".to_string())
.unwrap(); .unwrap();
@@ -283,7 +291,7 @@ mod tests {
#[test] #[test]
fn test_set_stock_translation_wrong_replacements() { fn test_set_stock_translation_wrong_replacements() {
let mut t = dummy_context(); let t = dummy_context();
assert!(t assert!(t
.ctx .ctx
.set_stock_translation(StockMessage::NoMessages, "xyz %1$s ".to_string()) .set_stock_translation(StockMessage::NoMessages, "xyz %1$s ".to_string())