diff --git a/bin/domain/imag-contact/Cargo.toml b/bin/domain/imag-contact/Cargo.toml index 2e43223d..054b3440 100644 --- a/bin/domain/imag-contact/Cargo.toml +++ b/bin/domain/imag-contact/Cargo.toml @@ -33,6 +33,3 @@ libimagentryref = { version = "0.5.0", path = "../../../lib/entry/libimagentryre libimagentryedit = { version = "0.5.0", path = "../../../lib/entry/libimagentryedit" } libimaginteraction = { version = "0.5.0", path = "../../../lib/etc/libimaginteraction" } -[dev-dependencies] -regex = "0.2" - diff --git a/bin/domain/imag-contact/src/create.rs b/bin/domain/imag-contact/src/create.rs new file mode 100644 index 00000000..c097e196 --- /dev/null +++ b/bin/domain/imag-contact/src/create.rs @@ -0,0 +1,443 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::collections::BTreeMap; +use std::process::exit; +use std::io::Write; +use std::io::stdout; +use std::path::PathBuf; +use std::fs::OpenOptions; + +use vobject::vcard::Vcard; +use vobject::write_component; +use toml_query::read::TomlValueReadExt; +use toml::Value; +use uuid::Uuid; + +use libimagrt::runtime::Runtime; +use libimagerror::trace::MapErrTrace; +use libimagerror::trace::trace_error; +use libimagerror::trace::trace_error_exit; +use libimagutil::warn_result::WarnResult; +use libimagentryref::refstore::RefStore; +use libimagentryref::flags::RefFlags; + +const TEMPLATE : &'static str = include_str!("../static/new-contact-template.toml"); + +#[cfg(test)] +mod test { + use toml::Value; + use super::TEMPLATE; + + const TEMPLATE_WITH_DATA : &'static str = include_str!("../static/new-contact-template-test.toml"); + + #[test] + fn test_validity_template_toml() { + let _ : Value = ::toml::de::from_str(TEMPLATE).unwrap(); + } + + #[test] + fn test_validity_template_toml_without_comments() { + let _ : Value = ::toml::de::from_str(TEMPLATE_WITH_DATA).unwrap(); + } +} + +macro_rules! ask_continue { + () => { + if ::libimaginteraction::ask::ask_bool("Edit tempfile", Some(true)) { + continue; + } else { + exit(1); + } + }; +} + +pub fn create(rt: &Runtime) { + let scmd = rt.cli().subcommand_matches("create").unwrap(); + let mut template = String::from(TEMPLATE); + + let (mut dest, location) : (Box, Option) = { + if let Some(mut fl) = scmd.value_of("file-location").map(PathBuf::from) { + if fl.is_file() { + error!("File does exist, cannot create/override"); + exit(1); + } else if fl.is_dir() { + fl.set_file_name(Uuid::new_v4().hyphenated().to_string()); + } + + let file = OpenOptions::new() + .write(true) + .create_new(true) + .open(fl.clone()) + .map_warn_err_str("Cannot create/open destination File. Stopping.") + .map_err_trace_exit_unwrap(1); + + (Box::new(file), Some(fl)) + } else { + (Box::new(stdout()), None) + } + }; + + loop { + ::libimagentryedit::edit::edit_in_tmpfile(&rt, &mut template) + .map_warn_err_str("Editing failed.") + .map_err_trace_exit_unwrap(1); + + if template == TEMPLATE || template.is_empty() { + error!("No (changed) content in tempfile. Not doing anything."); + exit(2); + } + + match ::toml::de::from_str(&template) { + Err(e) => { + error!("Error parsing template"); + trace_error(&e); + ask_continue!(); + }, + + Ok(toml) => { + debug!(""); + let mut vcard = Vcard::default(); + + { // parse name + debug!("Parsing name"); + let firstname = read_str_from_toml(&toml, "name.first"); + trace!("firstname = {:?}", firstname); + + let lastname = read_str_from_toml(&toml, "name.last"); + trace!("lastname = {:?}", lastname); + + vcard = vcard.with_name(parameters!(), + read_str_from_toml(&toml, "name.prefix"), + firstname.clone(), + read_str_from_toml(&toml, "name.additional"), + lastname.clone(), + read_str_from_toml(&toml, "name.suffix")); + + if let (Some(first), Some(last)) = (firstname, lastname) { + trace!("Building fullname: '{} {}'", first, last); + vcard = vcard.with_fullname(format!("{} {}", first, last)); + } + } + + { // parse nicknames + debug!("Parsing nicknames"); + match toml.read("nickname").map_err_trace_exit_unwrap(1) { + Some(&Value::Array(ref ary)) => { + for (i, element) in ary.iter().enumerate() { + let nicktype = match read_str_from_toml(element, "type") { + Some(p) => { + let mut m = BTreeMap::new(); + m.insert("TYPE".into(), p); + m + }, + None => BTreeMap::new(), + }; + + let name = match read_str_from_toml(element, "name") { + None => { + error!("Key 'nickname.[{}].name' missing", i); + ask_continue!() + }, + Some(p) => p, + }; + + trace!("nick type = {:?}", nicktype); + trace!("name = {:?}", name); + + vcard = vcard.with_nickname(nicktype, name); + } + }, + + Some(&Value::String(ref name)) => { + vcard = vcard.with_nickname(parameters!(), name.clone()); + } + + Some(_) => { + error!("Type Error: Expected Array or String at 'nickname'"); + ask_continue!(); + }, + None => { + // nothing + }, + } + } + + { // parse organisation + debug!("Parsing organisation"); + + if let Some(orgs) = read_strary_from_toml(&toml, "organisation.name") { + trace!("orgs = {:?}", orgs); + vcard = vcard.with_org(orgs); + } else { + error!("Key 'organisation.name' missing"); + ask_continue!(); + } + + if let Some(title) = read_str_from_toml(&toml, "organisation.title") { + trace!("title = {:?}", title); + vcard = vcard.with_title(title); + } + + if let Some(role) = read_str_from_toml(&toml, "organisation.role") { + trace!("role = {:?}", role); + vcard = vcard.with_role(role); + } + } + + { // parse phone + debug!("Parse phone"); + match toml.read("phone").map_err_trace_exit_unwrap(1) { + Some(&Value::Array(ref ary)) => { + for (i, element) in ary.iter().enumerate() { + let phonetype = match read_str_from_toml(element, "type") { + Some(p) => p, + None => { + error!("Key 'phones.[{}].type' missing", i); + ask_continue!() + } + }; + + let number = match read_str_from_toml(element, "number") { + Some(p) => p, + None => { + error!("Key 'phones.[{}].number' missing", i); + ask_continue!() + } + }; + + trace!("phonetype = {:?}", phonetype); + trace!("number = {:?}", number); + + vcard = vcard.with_tel(parameters!("TYPE" => phonetype), number); + } + }, + + Some(_) => { + error!("Expected Array at 'phones'."); + ask_continue!() + }, + None => { + // nothing + }, + } + } + + { // parse address + debug!("Parsing address"); + match toml.read("addresses").map_err_trace_exit_unwrap(1) { + Some(&Value::Array(ref ary)) => { + for (i, element) in ary.iter().enumerate() { + let adrtype = match read_str_from_toml(element, "type") { + None => { + error!("Key 'adresses.[{}].type' missing", i); + ask_continue!() + }, + Some(p) => p, + }; + trace!("adrtype = {:?}", adrtype); + + let bx = read_str_from_toml(element, "box"); + let extended = read_str_from_toml(element, "extended"); + let street = read_str_from_toml(element, "street"); + let code = read_str_from_toml(element, "code"); + let city = read_str_from_toml(element, "city"); + let region = read_str_from_toml(element, "region"); + let country = read_str_from_toml(element, "country"); + + trace!("bx = {:?}", bx); + trace!("extended = {:?}", extended); + trace!("street = {:?}", street); + trace!("code = {:?}", code); + trace!("city = {:?}", city); + trace!("region = {:?}", region); + trace!("country = {:?}", country); + + vcard = vcard.with_adr( + parameters!("TYPE" => adrtype), + bx, extended, street, code, city, region, country + ); + } + }, + + Some(_) => { + error!("Type Error: Expected Array at 'addresses'"); + ask_continue!(); + }, + None => { + // nothing + }, + } + } + + { // parse email + debug!("Parsing email"); + match toml.read("email").map_err_trace_exit_unwrap(1) { + Some(&Value::Array(ref ary)) => { + for (i, element) in ary.iter().enumerate() { + let mailtype = match read_str_from_toml(element, "type") { + None => { + error!("Error: 'email.[{}].type' missing", i); + ask_continue!() + }, + Some(p) => p, + }; // TODO: Unused, because unsupported by vobject + + let mail = match read_str_from_toml(element, "addr") { + None => { + error!("Error: 'email.[{}].addr' missing", i); + ask_continue!() + }, + Some(p) => p, + }; + + trace!("mailtype = {:?} (UNUSED)", mailtype); + trace!("mail = {:?}", mail); + + vcard = vcard.with_email(mail); + } + }, + + Some(_) => { + error!("Type Error: Expected Array at 'email'"); + ask_continue!(); + }, + None => { + // nothing + }, + } + } + + { // parse others + debug!("Parsing others"); + if let Some(categories) = read_strary_from_toml(&toml, "others.categories") { + vcard = vcard.with_categories(categories); + } else { + debug!("No categories"); + } + + if let Some(webpage) = read_str_from_toml(&toml, "others.webpage") { + vcard = vcard.with_url(webpage); + } else { + debug!("No webpage"); + } + + if let Some(note) = read_str_from_toml(&toml, "others.note") { + vcard = vcard.with_note(note); + } else { + debug!("No note"); + } + + } + + //println!("{:#?}", vcard); + if template == TEMPLATE || template.is_empty() { + if ::libimaginteraction::ask::ask_bool("Abort contact creating", Some(false)) { + exit(1); + } else { + continue; + } + } + + let vcard_string = write_component(&vcard); + if let Err(e) = dest.write_all(&vcard_string.as_bytes()) { + warn!("Error while writing out vcard content"); + trace_error_exit(&e, 1); + } + + break; + } + } + } + + if let Some(location) = location { + if !scmd.is_present("dont-track") { + let flags = RefFlags::default() + .with_content_hashing(true) + .with_permission_tracking(false); + + RefStore::create(rt.store(), location, flags) + .map_err_trace_exit_unwrap(1); + + info!("Created entry in store"); + } else { + info!("Not creating entry in store"); + } + } else { + info!("Cannot track stdout-created contact information"); + } + + info!("Ready"); +} + +fn read_strary_from_toml(toml: &Value, path: &'static str) -> Option> { + match toml.read(path).map_warn_err_str(&format!("Failed to read value at '{}'", path)) { + Ok(Some(&Value::Array(ref vec))) => { + let mut v = Vec::new(); + for elem in vec { + match *elem { + Value::String(ref s) => v.push(s.clone()), + _ => { + error!("Type Error: '{}' must be Array", path); + return None + }, + } + } + + Some(v) + } + Ok(Some(&Value::String(ref s))) => { + warn!("Having String, wanting Array ... going to auto-fix"); + Some(vec![s.clone()]) + }, + Ok(Some(_)) => { + error!("Type Error: '{}' must be Array", path); + None + }, + Ok(None) => None, + Err(_) => None, + } +} + +fn read_str_from_toml(toml: &Value, path: &'static str) -> Option { + let v = toml.read(path) + .map_warn_err_str(&format!("Failed to read value at '{}'", path)); + + match v { + Ok(Some(&Value::String(ref s))) => Some(s.clone()), + Ok(Some(_)) => { + error!("Type Error: '{}' must be String", path); + None + }, + Ok(None) => { + error!("Expected '{}' to be present, but is not.", path); + None + }, + Err(e) => { + trace_error(&e); + None + } + } +} + +#[cfg(test)] +mod test_entry { + // TODO +} + diff --git a/bin/domain/imag-contact/src/main.rs b/bin/domain/imag-contact/src/main.rs index 17cd5237..505ef918 100644 --- a/bin/domain/imag-contact/src/main.rs +++ b/bin/domain/imag-contact/src/main.rs @@ -32,8 +32,6 @@ while_true, )] -#[cfg(test)] extern crate regex; - extern crate clap; #[macro_use] extern crate log; #[macro_use] extern crate version; @@ -54,7 +52,6 @@ extern crate libimagentryref; extern crate libimagentryedit; use std::process::exit; -use std::collections::BTreeMap; use std::path::PathBuf; use handlebars::Handlebars; @@ -75,8 +72,12 @@ use libimagentryref::reference::Ref; use libimagentryref::refstore::RefStore; mod ui; +mod util; +mod create; use ui::build_ui; +use util::build_data_object_for_handlebars; +use create::create; fn main() { let rt = generate_runtime_setup("imag-contact", @@ -93,6 +94,7 @@ fn main() { "list" => list(&rt), "import" => import(&rt), "show" => show(&rt), + "create" => create(&rt), _ => { error!("Unknown command"); // More error handling }, @@ -211,108 +213,6 @@ fn show(rt: &Runtime) { info!("Ok"); } -fn build_data_object_for_handlebars<'a>(i: usize, hash: String, vcard: &Vcard) -> BTreeMap<&'static str, String> { - let mut data = BTreeMap::new(); - { - data.insert("i" , format!("{}", i)); - - /// The hash (as in libimagentryref) of the contact - data.insert("id" , hash); - - data.insert("ADR" , vcard.adr() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("ANNIVERSARY" , vcard.anniversary() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("BDAY" , vcard.bday() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("CATEGORIES" , vcard.categories() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("CLIENTPIDMAP" , vcard.clientpidmap() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("EMAIL" , vcard.email() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("FN" , vcard.fullname() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("GENDER" , vcard.gender() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("GEO" , vcard.geo() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("IMPP" , vcard.impp() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("KEY" , vcard.key() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("LANG" , vcard.lang() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("LOGO" , vcard.logo() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("MEMBER" , vcard.member() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("N" , vcard.name() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("NICKNAME" , vcard.nickname() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("NOTE" , vcard.note() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("ORG" , vcard.org() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("PHOTO" , vcard.photo() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("PRIOD" , vcard.proid() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("RELATED" , vcard.related() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("REV" , vcard.rev() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("ROLE" , vcard.role() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("SOUND" , vcard.sound() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("TEL" , vcard.tel() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("TITLE" , vcard.title() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("TZ" , vcard.tz() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("UID" , vcard.uid() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - - data.insert("URL" , vcard.url() - .into_iter().map(|c| c.raw().clone()).collect()); - - data.insert("VERSION" , vcard.version() - .map(|c| c.raw().clone()).unwrap_or(String::new())); - } - - data -} - fn get_contact_print_format(config_value_path: &'static str, rt: &Runtime, scmd: &ArgMatches) -> Handlebars { let fmt = scmd .value_of("format") diff --git a/bin/domain/imag-contact/src/ui.rs b/bin/domain/imag-contact/src/ui.rs index e2f9c367..f3442a09 100644 --- a/bin/domain/imag-contact/src/ui.rs +++ b/bin/domain/imag-contact/src/ui.rs @@ -70,4 +70,24 @@ pub fn build_ui<'a>(app: App<'a, 'a>) -> App<'a, 'a> { .value_name("FORMAT") .help("Format to format the contact when printing it")) ) + + .subcommand(SubCommand::with_name("create") + .about("Create a contact file (.vcf) and track it in imag.") + .version("0.1") + .arg(Arg::with_name("file-location") + .short("F") + .long("file") + .takes_value(true) + .required(false) + .multiple(false) + .value_name("PATH") + .help("Create this file. If a directory is passed, a file with a uuid as name will be created. vcf contents are dumped to stdout if this is not passed.")) + .arg(Arg::with_name("dont-track") + .short("T") + .long("no-track") + .takes_value(false) + .required(false) + .multiple(false) + .help("Don't track the new vcf file if one is created.")) + ) } diff --git a/bin/domain/imag-contact/src/util.rs b/bin/domain/imag-contact/src/util.rs new file mode 100644 index 00000000..e85a6a8a --- /dev/null +++ b/bin/domain/imag-contact/src/util.rs @@ -0,0 +1,124 @@ +// +// imag - the personal information management suite for the commandline +// Copyright (C) 2015, 2016 Matthias Beyer and contributors +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; version +// 2.1 of the License. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// + +use std::collections::BTreeMap; +use vobject::vcard::Vcard; + +pub fn build_data_object_for_handlebars<'a>(i: usize, hash: String, vcard: &Vcard) -> BTreeMap<&'static str, String> { + let mut data = BTreeMap::new(); + { + data.insert("i" , format!("{}", i)); + + /// The hash (as in libimagentryref) of the contact + data.insert("id" , hash); + + data.insert("ADR" , vcard.adr() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("ANNIVERSARY" , vcard.anniversary() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("BDAY" , vcard.bday() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("CATEGORIES" , vcard.categories() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("CLIENTPIDMAP" , vcard.clientpidmap() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("EMAIL" , vcard.email() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("FN" , vcard.fullname() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("GENDER" , vcard.gender() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("GEO" , vcard.geo() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("IMPP" , vcard.impp() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("KEY" , vcard.key() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("LANG" , vcard.lang() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("LOGO" , vcard.logo() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("MEMBER" , vcard.member() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("N" , vcard.name() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("NICKNAME" , vcard.nickname() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("NOTE" , vcard.note() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("ORG" , vcard.org() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("PHOTO" , vcard.photo() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("PRIOD" , vcard.proid() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("RELATED" , vcard.related() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("REV" , vcard.rev() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("ROLE" , vcard.role() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("SOUND" , vcard.sound() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("TEL" , vcard.tel() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("TITLE" , vcard.title() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("TZ" , vcard.tz() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("UID" , vcard.uid() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + + data.insert("URL" , vcard.url() + .into_iter().map(|c| c.raw().clone()).collect()); + + data.insert("VERSION" , vcard.version() + .map(|c| c.raw().clone()).unwrap_or(String::new())); + } + + data +} + diff --git a/bin/domain/imag-contact/static/new-contact-template-test.toml b/bin/domain/imag-contact/static/new-contact-template-test.toml new file mode 100644 index 00000000..65831405 --- /dev/null +++ b/bin/domain/imag-contact/static/new-contact-template-test.toml @@ -0,0 +1,90 @@ +# Contact template for imag-contact version 0.5.0 +# +# This file is explicitely _not_ distributed under the terms of the original imag license, but +# public domain. +# +# Use this TOML formatted template to create a new contact. + +[name] + +# every entry may contain a string or a list of strings +# E.G.: +# first = "Foo" +# last = [ "bar", "bar", "a" ] +prefix = "test" +first = "test" +additional = "test" +last = "test" +suffix = "test" + +[person] + +# Birthday +# Format: YYYY-MM-DD +birthday = "2017-01-01" + +# Nickname +# "type" is optional +[[nickname]] +type = "work" +name = "boss" + +[organisation] + +# Organisation name +# May contain a string or a list of strings +name = "test" + +# Organisation title and role +# May contain a string or a list of strings +title = "test" + +# Role at organisation +# May contain a string or a list of strings +role = "test" + +# allowed types: +# vcard 3.0: At least one of bbs, car, cell, fax, home, isdn, msg, modem, +# pager, pcs, pref, video, voice, work +# vcard 4.0: At least one of home, work, pref, text, voice, fax, cell, video, +# pager, textphone +phone = [ + { "type" = "home", "number" = "0123 123456789" }, +] + +# +# Email addresses +# +email = [ + { "type" = "home", "addr" = "examle@examplemail.org" }, +] + +# post addresses +# +# allowed types: +# vcard 3.0: At least one of dom, intl, home, parcel, postal, pref, work +# vcard 4.0: At least one of home, pref, work +[[addresses]] +type = "home" +box = "test" +extended = "test" +street = "test" +code = "test" +city = "test" +region = "test" +country = "test" + +[other] + +# categories or tags +# May contain a string or a list of strings +categories = "test" + +# Web pages +# May contain a string or a list of strings +webpage = "test" + +# Notes +# May contain a string or a list of strings +note = "test" + diff --git a/bin/domain/imag-contact/static/new-contact-template.toml b/bin/domain/imag-contact/static/new-contact-template.toml new file mode 100644 index 00000000..67dbc8b8 --- /dev/null +++ b/bin/domain/imag-contact/static/new-contact-template.toml @@ -0,0 +1,90 @@ +# Contact template for imag-contact version 0.5.0 +# +# This file is explicitely _not_ distributed under the terms of the original imag license, but +# public domain. +# +# Use this TOML formatted template to create a new contact. + +[name] + +# every entry may contain a string or a list of strings +# E.G.: +# first = "Foo" +# last = [ "bar", "bar", "a" ] +#prefix = "" +first = "" +#additional = "" +last = "" +#suffix = "" + +[person] + +# Birthday +# Format: YYYY-MM-DD +#birthday = "" + +# Nickname +# "type" is optional +#[[nickname]] +#type = "work" +#name = "boss" + +[organisation] + +# Organisation name +# May contain a string or a list of strings +#name = "" + +# Organisation title and role +# May contain a string or a list of strings +#title = "" + +# Role at organisation +# May contain a string or a list of strings +#role = "" + +# allowed types: +# vcard 3.0: At least one of bbs, car, cell, fax, home, isdn, msg, modem, +# pager, pcs, pref, video, voice, work +# vcard 4.0: At least one of home, work, pref, text, voice, fax, cell, video, +# pager, textphone +#phone = [ +# { "type" = "home", "number" = "0123 123456789" }, +#] + +# +# Email addresses +# +#email = [ +# { "type" = "home", "addr" = "examle@examplemail.org" }, +#] + +# post addresses +# +# allowed types: +# vcard 3.0: At least one of dom, intl, home, parcel, postal, pref, work +# vcard 4.0: At least one of home, pref, work +#[[addresses]] +#type = "home" +#box = "" +#extended = "" +#street = "" +#code = "" +#city = "" +#region = "" +#country = "" + +[other] + +# categories or tags +# May contain a string or a list of strings +#categories = "" + +# Web pages +# May contain a string or a list of strings +#webpage = "" + +# Notes +# May contain a string or a list of strings +#note = "" +