From df89a39cfd34d0d70609daa214d3b618bb6223bd Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 13 Nov 2015 13:07:16 +0000 Subject: [PATCH] Allow multiple recipients of KDM emails (#745). --- ChangeLog | 4 ++ src/lib/cinema.cc | 16 +++++-- src/lib/cinema.h | 8 ++-- src/lib/cinema_kdms.cc | 2 +- src/lib/emailer.cc | 11 +++-- src/lib/emailer.h | 7 +-- src/lib/send_problem_report_job.cc | 5 +- src/tools/dcpomatic_kdm_cli.cc | 9 +++- src/wx/cinema_dialog.cc | 74 ++++++++++++++++++++++++++---- src/wx/cinema_dialog.h | 18 ++++++-- src/wx/editable_list.h | 9 +++- src/wx/email_dialog.cc | 45 ++++++++++++++++++ src/wx/email_dialog.h | 32 +++++++++++++ src/wx/screens_panel.cc | 6 +-- src/wx/wscript | 1 + 15 files changed, 208 insertions(+), 39 deletions(-) create mode 100644 src/wx/email_dialog.cc create mode 100644 src/wx/email_dialog.h diff --git a/ChangeLog b/ChangeLog index c0cce1c03..a1c4e053a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2015-11-13 c.hetherington + + * Allow multiple recipients of KDM emails (#745). + 2015-11-12 Carl Hetherington * Version 2.5.2 released. diff --git a/src/lib/cinema.cc b/src/lib/cinema.cc index 11bc888da..ebd716009 100644 --- a/src/lib/cinema.cc +++ b/src/lib/cinema.cc @@ -21,15 +21,18 @@ #include "screen.h" #include #include +#include using std::list; +using std::string; using boost::shared_ptr; Cinema::Cinema (cxml::ConstNodePtr node) : name (node->string_child ("Name")) - , email (node->string_child ("Email")) { - + BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Email")) { + emails.push_back (i->content ()); + } } /* This is necessary so that we can use shared_from_this in add_screen (which cannot be done from @@ -48,10 +51,13 @@ void Cinema::as_xml (xmlpp::Element* parent) const { parent->add_child("Name")->add_child_text (name); - parent->add_child("Email")->add_child_text (email); - for (list >::const_iterator i = _screens.begin(); i != _screens.end(); ++i) { - (*i)->as_xml (parent->add_child ("Screen")); + BOOST_FOREACH (string i, emails) { + parent->add_child("Email")->add_child_text (i); + } + + BOOST_FOREACH (shared_ptr i, _screens) { + i->as_xml (parent->add_child ("Screen")); } } diff --git a/src/lib/cinema.h b/src/lib/cinema.h index 3bf958d82..aec4c83ec 100644 --- a/src/lib/cinema.h +++ b/src/lib/cinema.h @@ -33,15 +33,15 @@ class Screen; /** @class Cinema * @brief A description of a Cinema for KDM generation. * - * This is a cinema name, contact email address and a list of + * This is a cinema name, contact email addresses and a list of * Screen objects. */ class Cinema : public boost::enable_shared_from_this { public: - Cinema (std::string const & n, std::string const & e) + Cinema (std::string const & n, std::list const & e) : name (n) - , email (e) + , emails (e) {} Cinema (cxml::ConstNodePtr); @@ -54,7 +54,7 @@ public: void remove_screen (boost::shared_ptr); std::string name; - std::string email; + std::list emails; std::list > screens () const { return _screens; } diff --git a/src/lib/cinema_kdms.cc b/src/lib/cinema_kdms.cc index eb71b3202..15aaaa731 100644 --- a/src/lib/cinema_kdms.cc +++ b/src/lib/cinema_kdms.cc @@ -152,7 +152,7 @@ CinemaKDMs::email ( } boost::algorithm::replace_all (body, "$SCREENS", screens.str().substr (0, screens.str().length() - 2)); - Emailer email (config->kdm_from(), i.cinema->email, subject, body); + Emailer email (config->kdm_from(), i.cinema->emails, subject, body); if (!config->kdm_cc().empty ()) { email.add_cc (config->kdm_cc ()); diff --git a/src/lib/emailer.cc b/src/lib/emailer.cc index 916d17182..885171e2f 100644 --- a/src/lib/emailer.cc +++ b/src/lib/emailer.cc @@ -38,7 +38,7 @@ using std::cout; using std::pair; using boost::shared_ptr; -Emailer::Emailer (string from, string to, string subject, string body) +Emailer::Emailer (string from, list to, string subject, string body) : _from (from) , _to (to) , _subject (subject) @@ -106,7 +106,7 @@ Emailer::send (shared_ptr job) stringstream email; email << "Date: " << date_buffer << "\r\n" - << "To: " << _to << "\r\n" + << "To: " << address_list (_to) << "\r\n" << "From: " << _from << "\r\n"; if (!_cc.empty ()) { @@ -194,7 +194,10 @@ Emailer::send (shared_ptr job) curl_easy_setopt (curl, CURLOPT_MAIL_FROM, _from.c_str()); - struct curl_slist* recipients = curl_slist_append (0, _to.c_str()); + struct curl_slist* recipients = 0; + BOOST_FOREACH (string i, _to) { + curl_slist_append (recipients, i.c_str()); + } BOOST_FOREACH (string i, _cc) { recipients = curl_slist_append (recipients, i.c_str()); } @@ -251,7 +254,7 @@ Emailer::send (shared_ptr job) CURLMcode mc = curl_multi_fdset (mcurl, &fdread, &fdwrite, &fdexcep, &maxfd); if (mc != CURLM_OK) { - throw KDMError (String::compose ("Failed to send KDM email to %1", _to)); + throw KDMError (String::compose ("Failed to send KDM email to %1", address_list (_to))); } int rc; diff --git a/src/lib/emailer.h b/src/lib/emailer.h index 89993ca48..c8ebac7b6 100644 --- a/src/lib/emailer.h +++ b/src/lib/emailer.h @@ -23,7 +23,7 @@ class Emailer { public: - Emailer (std::string from, std::string to, std::string subject, std::string body); + Emailer (std::string from, std::list to, std::string subject, std::string body); void add_cc (std::string cc); void add_bcc (std::string bcc); @@ -38,11 +38,12 @@ public: size_t get_data (void* ptr, size_t size, size_t nmemb); int debug (CURL* curl, curl_infotype type, char* data, size_t size); -private: static std::string address_list (std::list addresses); +private: + std::string _from; - std::string _to; + std::list _to; std::string _subject; std::string _body; std::list _cc; diff --git a/src/lib/send_problem_report_job.cc b/src/lib/send_problem_report_job.cc index f0c34f590..8142b173c 100644 --- a/src/lib/send_problem_report_job.cc +++ b/src/lib/send_problem_report_job.cc @@ -81,7 +81,10 @@ SendProblemReportJob::run () add_file (body, "metadata.xml"); } - Emailer emailer (_from, "carl@dcpomatic.com", "DCP-o-matic problem report", body); + list to; + to.push_back ("carl@dcpomatic.com"); + + Emailer emailer (_from, to, "DCP-o-matic problem report", body); emailer.send (shared_from_this ()); set_progress (1); diff --git a/src/tools/dcpomatic_kdm_cli.cc b/src/tools/dcpomatic_kdm_cli.cc index aa3022c30..c49642c8e 100644 --- a/src/tools/dcpomatic_kdm_cli.cc +++ b/src/tools/dcpomatic_kdm_cli.cc @@ -27,6 +27,7 @@ #include "lib/cinema_kdms.h" #include "lib/config.h" #include "lib/exceptions.h" +#include "lib/emailer.h" #include "lib/safe_stringstream.h" #include #include @@ -189,7 +190,7 @@ int main (int argc, char* argv[]) if (cinemas) { list > cinemas = Config::instance()->cinemas (); for (list >::const_iterator i = cinemas.begin(); i != cinemas.end(); ++i) { - cout << (*i)->name << " (" << (*i)->email << ")\n"; + cout << (*i)->name << " (" << Emailer::address_list ((*i)->emails) << ")\n"; } exit (EXIT_SUCCESS); } @@ -263,7 +264,11 @@ int main (int argc, char* argv[]) list > cinemas = Config::instance()->cinemas (); list >::const_iterator i = cinemas.begin(); - while (i != cinemas.end() && (*i)->name != cinema_name && (*i)->email != cinema_name) { + while ( + i != cinemas.end() && + (*i)->name != cinema_name && + find ((*i)->emails.begin(), (*i)->emails.end(), cinema_name) == (*i)->emails.end()) { + ++i; } diff --git a/src/wx/cinema_dialog.cc b/src/wx/cinema_dialog.cc index a54dd7335..e3dff8a48 100644 --- a/src/wx/cinema_dialog.cc +++ b/src/wx/cinema_dialog.cc @@ -19,19 +19,59 @@ #include "cinema_dialog.h" #include "wx_util.h" +#include using std::string; +using std::vector; +using std::copy; +using std::back_inserter; +using std::list; +using std::cout; +using boost::bind; -CinemaDialog::CinemaDialog (wxWindow* parent, string title, string name, string email) - : TableDialog (parent, std_to_wx (title), 2, 1, true) +static string +column (string s) { - add (_("Name"), true); - _name = add (new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (256, -1))); + return s; +} + +CinemaDialog::CinemaDialog (wxWindow* parent, string title, string name, list emails) + : wxDialog (parent, wxID_ANY, std_to_wx (title)) +{ + wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL); + SetSizer (overall_sizer); + + wxGridBagSizer* sizer = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP); + int r = 0; + + add_label_to_sizer (sizer, this, _("Name"), true, wxGBPosition (r, 0)); + _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (500, -1)); + sizer->Add (_name, wxGBPosition (r, 1)); + ++r; + + add_label_to_sizer (sizer, this, _("Email addresses for KDM delivery"), true, wxGBPosition (r, 0), wxGBSpan (1, 2)); + ++r; + + copy (emails.begin(), emails.end(), back_inserter (_emails)); + + vector columns; + columns.push_back (wx_to_std (_("Address"))); + _email_list = new EditableList ( + this, columns, bind (&CinemaDialog::get_emails, this), bind (&CinemaDialog::set_emails, this, _1), bind (&column, _1) + ); + + sizer->Add (_email_list, wxGBPosition (r, 0), wxGBSpan (1, 2), wxEXPAND); + ++r; - add (_("Email address for KDM delivery"), true); - _email = add (new wxTextCtrl (this, wxID_ANY, std_to_wx (email), wxDefaultPosition, wxSize (256, -1))); + overall_sizer->Add (sizer, 1, wxEXPAND | wxALL, DCPOMATIC_DIALOG_BORDER); - layout (); + wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL); + if (buttons) { + overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder()); + } + + overall_sizer->Layout (); + overall_sizer->SetSizeHints (this); } string @@ -40,8 +80,22 @@ CinemaDialog::name () const return wx_to_std (_name->GetValue()); } -string -CinemaDialog::email () const +void +CinemaDialog::set_emails (vector e) +{ + _emails = e; +} + +vector +CinemaDialog::get_emails () const +{ + return _emails; +} + +list +CinemaDialog::emails () const { - return wx_to_std (_email->GetValue()); + list e; + copy (_emails.begin(), _emails.end(), back_inserter (e)); + return e; } diff --git a/src/wx/cinema_dialog.h b/src/wx/cinema_dialog.h index 56beaa5bd..7d27bd94b 100644 --- a/src/wx/cinema_dialog.h +++ b/src/wx/cinema_dialog.h @@ -17,18 +17,26 @@ */ -#include #include "table_dialog.h" +#include "editable_list.h" +#include "email_dialog.h" +#include +#include +#include -class CinemaDialog : public TableDialog +class CinemaDialog : public wxDialog { public: - CinemaDialog (wxWindow *, std::string, std::string name = "", std::string email = ""); + CinemaDialog (wxWindow *, std::string, std::string name = "", std::list emails = std::list ()); std::string name () const; - std::string email () const; + std::list emails () const; private: + std::vector get_emails () const; + void set_emails (std::vector); + wxTextCtrl* _name; - wxTextCtrl* _email; + EditableList* _email_list; + std::vector _emails; }; diff --git a/src/wx/editable_list.h b/src/wx/editable_list.h index be79f67e6..bb749a4a4 100644 --- a/src/wx/editable_list.h +++ b/src/wx/editable_list.h @@ -17,9 +17,15 @@ */ +#include "wx_util.h" #include #include +#include +#include +/** @param T type of things being edited. + * @param S dialog to edit a thing. + */ template class EditableList : public wxPanel { @@ -78,8 +84,9 @@ public: _list->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&EditableList::selection_changed, this)); _list->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&EditableList::selection_changed, this)); _list->Bind (wxEVT_SIZE, boost::bind (&EditableList::resized, this, _1)); - selection_changed (); + refresh (); + selection_changed (); } void refresh () diff --git a/src/wx/email_dialog.cc b/src/wx/email_dialog.cc new file mode 100644 index 000000000..fa9e319f6 --- /dev/null +++ b/src/wx/email_dialog.cc @@ -0,0 +1,45 @@ +/* + Copyright (C) 2015 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "email_dialog.h" +#include "wx_util.h" + +using std::string; +using boost::shared_ptr; + +EmailDialog::EmailDialog (wxWindow* parent) + : TableDialog (parent, _("Email address"), 2, 1, true) +{ + add (_("Email address"), true); + _email = add (new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxSize (400, -1))); + + layout (); +} + +void +EmailDialog::set (string address) +{ + _email->SetValue (std_to_wx (address)); +} + +string +EmailDialog::get () const +{ + return wx_to_std (_email->GetValue ()); +} diff --git a/src/wx/email_dialog.h b/src/wx/email_dialog.h new file mode 100644 index 000000000..698cbef69 --- /dev/null +++ b/src/wx/email_dialog.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2015 Carl Hetherington + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "table_dialog.h" + +class EmailDialog : public TableDialog +{ +public: + EmailDialog (wxWindow *); + + void set (std::string); + std::string get () const; + +private: + wxTextCtrl* _email; +}; diff --git a/src/wx/screens_panel.cc b/src/wx/screens_panel.cc index 5bdfe6c6c..4f8e3ad4d 100644 --- a/src/wx/screens_panel.cc +++ b/src/wx/screens_panel.cc @@ -171,7 +171,7 @@ ScreensPanel::add_cinema_clicked () { CinemaDialog* d = new CinemaDialog (this, "Add Cinema"); if (d->ShowModal () == wxID_OK) { - shared_ptr c (new Cinema (d->name(), d->email())); + shared_ptr c (new Cinema (d->name(), d->emails())); Config::instance()->add_cinema (c); add_cinema (c); } @@ -188,10 +188,10 @@ ScreensPanel::edit_cinema_clicked () pair > c = selected_cinemas().front(); - CinemaDialog* d = new CinemaDialog (this, "Edit cinema", c.second->name, c.second->email); + CinemaDialog* d = new CinemaDialog (this, "Edit cinema", c.second->name, c.second->emails); if (d->ShowModal () == wxID_OK) { c.second->name = d->name (); - c.second->email = d->email (); + c.second->emails = d->emails (); _targets->SetItemText (c.first, std_to_wx (d->name())); Config::instance()->changed (); } diff --git a/src/wx/wscript b/src/wx/wscript index 3b61eb5c6..821043b40 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -39,6 +39,7 @@ sources = """ content_properties_dialog.cc content_sub_panel.cc dcp_panel.cc + email_dialog.cc image_sequence_dialog.cc isdcf_metadata_dialog.cc dir_picker_ctrl.cc -- 2.30.2