Permalink
Browse files

Implement the "send later" functionality.

The unused "composed" mail status is replaced by "scheduled".
Scheduled messages are associated to a job in the jobs table
with the expected sender's date as argument.
  • Loading branch information...
manitou-mail committed Apr 12, 2017
1 parent 1b89ffa commit 810b9e4cbc68d0c6ce94871bed3862aecd0ba16a
Showing with 320 additions and 66 deletions.
  1. BIN icons/clock.png
  2. +2 −1 src/icons.h
  3. +10 −2 src/mail_listview.cpp
  4. +100 −14 src/message.cpp
  5. +14 −4 src/message.h
  6. +68 −22 src/msg_properties.cpp
  7. +3 −1 src/msg_properties.h
  8. +100 −17 src/newmailwidget.cpp
  9. +20 −2 src/newmailwidget.h
  10. +3 −3 src/selectmail.cpp
View
Binary file not shown.
View
@@ -1,4 +1,4 @@
/* Copyright (C) 2004-2016 Daniel Verite
/* Copyright (C) 2004-2017 Daniel Verite
This file is part of Manitou-Mail (see http://www.manitou-mail.org)
@@ -105,6 +105,7 @@ extern QString gl_xpm_path;
#define ICON16_STATISTICS "statistics.png"
#define ICON16_TRAYICON "mail-trayicon.png"
#define ICON16_CLOCK "clock.png"
#define FT_MAKE_ICON(name) QPixmap(gl_xpm_path+"/"+name)
#define UI_ICON(name) QIcon(gl_xpm_path+"/"+name)
View
@@ -1,4 +1,4 @@
/* Copyright (C) 2004-2016 Daniel Verite
/* Copyright (C) 2004-2017 Daniel Verite
This file is part of Manitou-Mail (see http://www.manitou-mail.org)
@@ -127,8 +127,9 @@ mail_item_model::icon_status(uint status)
static QIcon* iarchived;
static QIcon* isent;
static QIcon* itosend;
static QIcon* isendlater;
// the order of each test is relevant: the "preferred" icons come first
// the order of tests does matter: the "preferred" icons come first
if (status & mail_msg::statusTrashed) {
if (!itrashed)
itrashed = new STATUS_ICON(FT_ICON16_STATUS_TRASHED);
@@ -154,6 +155,13 @@ mail_item_model::icon_status(uint status)
iarchived = new STATUS_ICON(FT_ICON16_STATUS_PROCESSED);
return iarchived;
}
else if ((status & (mail_msg::statusOutgoing|mail_msg::statusScheduled)) ==
(mail_msg::statusOutgoing|mail_msg::statusScheduled))
{
if (!isendlater)
isendlater = new STATUS_ICON(ICON16_CLOCK);
return isendlater;
}
else if (status & mail_msg::statusOutgoing) {
if (!itosend)
itosend = new STATUS_ICON(FT_ICON16_TO_SEND);
View
@@ -1,4 +1,4 @@
/* Copyright (C) 2004-2016 Daniel Verite
/* Copyright (C) 2004-2017 Daniel Verite
This file is part of Manitou-Mail (see http://www.manitou-mail.org)
@@ -645,20 +645,23 @@ mail_msg::get_tags()
// update the message status in the database
bool
mail_msg::update_status(bool force/*=false*/)
mail_msg::update_status(bool force/*=false*/, db_ctxt* dbc)
{
db_cnx db0;
db_cnx* db = dbc ? dbc->m_db : &db0;
bool result = true;
if (force || m_db_status != m_status) {
db_cnx db;
try {
const char* query = "UPDATE mail SET status=:p1,mod_user_id=:o WHERE mail_id=:p2";
sql_stream s(query, db);
sql_stream s(query, *db);
s << m_status << user::current_user_id() << getId();
m_db_status = m_status;
msg_status_cache::update(get_id(), m_status);
}
catch(db_excpt& p) {
DBEXCPT (p);
if (dbc && dbc->propagate_exceptions)
throw p;
DBEXCPT(p);
result = false;
}
}
@@ -761,18 +764,20 @@ mail_msg::set_or_with_status(std::set<mail_msg*>& s, uint or_mask)
}
bool
mail_msg::update_priority()
mail_msg::update_priority(db_ctxt* dbc)
{
bool result = true;
db_cnx db;
db_cnx db0;
db_cnx* db = dbc ? dbc->m_db : &db0;
try {
const char* query;
query="UPDATE mail SET priority=:p1 WHERE mail_id=:p2";
sql_stream s(query, db);
sql_stream s("UPDATE mail SET priority=:p1 WHERE mail_id=:p2", *db);
s << m_pri << get_id();
}
catch(db_excpt& p) {
DBEXCPT (p);
if (dbc && dbc->propagate_exceptions)
throw p;
DBEXCPT(p);
result = false;
}
return result;
@@ -889,7 +894,11 @@ mail_msg::store(ui_feedback* ui)
sr << statusFwded+statusArchived << v[ifwd];
}
}
fields.add("status", statusRead + statusOutgoing);
int msg_status = statusRead + statusOutgoing;
if (!m_send_datetime.isNull())
msg_status = msg_status | statusScheduled;
fields.add("status", msg_status);
if (m_identity_id != 0)
fields.add("identity_id", m_identity_id);
@@ -908,7 +917,7 @@ mail_msg::store(ui_feedback* ui)
if (res)
PQclear(res);
msg_status_cache::update(get_id(), statusRead + statusOutgoing);
msg_status_cache::update(get_id(), msg_status);
if (m_body_html.isEmpty()) {
// plain text only
@@ -932,6 +941,12 @@ mail_msg::store(ui_feedback* ui)
}
m_Attachments.setMailId(m_nMailId);
result=(result && m_Attachments.store(&dbc, ui));
if (result) {
// process scheduled sending
if (!m_send_datetime.isNull())
result = store_send_datetime(m_send_datetime, &dbc);
}
if (result)
db.commit_transaction();
else {
@@ -1360,5 +1375,76 @@ mail_msg::get_sender_timestamp(time_t* t)
DBEXCPT(p);
return false;
}
return true;
return true;
}
bool
mail_msg::fetch_send_datetime(QDateTime* dt)
{
if (!get_id())
return false;
*dt = QDateTime(); // null and invalid
db_cnx db;
try {
sql_stream s("SELECT job_args FROM jobs_queue WHERE mail_id=:p1 AND job_type='send_mail'", db);
s << get_id();
if (!s.eos()) {
QString str_epoch;
s >> str_epoch;
bool ok;
qint64 secs = str_epoch.toLongLong(&ok);
if (ok)
dt->setMSecsSinceEpoch(secs*1000);
}
}
catch (db_excpt p) {
DBEXCPT(p);
return false;
}
return true; // positive result if no database error
}
QDateTime
mail_msg::get_send_datetime()
{
if (m_send_datetime.isNull())
fetch_send_datetime(&m_send_datetime);
return m_send_datetime;
}
/* Instantiate, update or delete the job in the database to send the message at a later date. */
bool
mail_msg::store_send_datetime(QDateTime dt, db_ctxt* dbc)
{
db_cnx db0;
db_cnx* db = dbc ? dbc->m_db : &db0;
try {
sql_stream sj
("DO $$ DECLARE id int:= :id; ts text:= :ts;\n"
"BEGIN\n"
"IF ts IS NOT null THEN "
" UPDATE jobs_queue SET job_args=ts WHERE mail_id=id AND job_type='send_mail';"
" IF NOT FOUND THEN "
" INSERT INTO jobs_queue(mail_id, job_type, job_args) "
" VALUES(id, 'send_mail', ts);"
" END IF;"
" NOTIFY job_request;"
" ELSE"
" DELETE FROM jobs_queue WHERE mail_id=id AND job_type='send_mail';"
" END IF;"
"END $$ language plpgsql", *db);
sj << this->get_id();
if (dt.isNull())
sj << sql_null();
else
sj << (quint64)(999+dt.toMSecsSinceEpoch())/1000;
}
catch(db_excpt& p) {
if (dbc && dbc->propagate_exceptions)
throw p;
DBEXCPT(p);
return false;
}
return true;
}
View
@@ -1,4 +1,4 @@
/* Copyright (C) 2004-2015 Daniel Verite
/* Copyright (C) 2004-2017 Daniel Verite
This file is part of Manitou-Mail (see http://www.manitou-mail.org)
@@ -23,6 +23,7 @@
#include "dbtypes.h"
#include "time.h"
#include <QString>
#include <QDateTime>
#include "addresses.h"
#include "mailheader.h"
@@ -154,6 +155,14 @@ class mail_msg
const date& msg_date() const { return m_cDate; }
bool get_msg_age(const QString unit, int*); // unit="days" or "hours" or "minutes"
bool get_sender_timestamp(time_t*);
/* Scheduled delivery. Instantiate, update or delete the job in the
database to send the message at a later date. */
bool store_send_datetime(QDateTime qt, db_ctxt* dbc=NULL);
bool fetch_send_datetime(QDateTime*);
QDateTime get_send_datetime();
void set_send_datetime(QDateTime d) { m_send_datetime = d; }
void set_note(const QString& s) {
m_mail_note=s;
}
@@ -190,9 +199,9 @@ class mail_msg
// even if the value in memory is the same as the new
// value, overwriting the change another user may have done
// in the meantime in the database
bool update_status(bool force=false);
bool update_status(bool force=false, db_ctxt* dbc=NULL);
bool update_priority();
bool update_priority(db_ctxt* dbc=NULL);
bool store(ui_feedback* ui=NULL);
bool mdelete();
@@ -226,7 +235,7 @@ class mail_msg
statusOutgoing=128,
statusSent=256,
statusReplying=512,
statusComposed=1024,
statusScheduled=1024,
statusAttached=2048, // internal
statusMax=2048
} status_t;
@@ -269,6 +278,7 @@ class mail_msg
int m_pri; // priority
int m_identity_id;
date m_cDate;
QDateTime m_send_datetime;
bool m_body_fetched;
bool m_body_html_fetched;
int m_body_fetched_length;
Oops, something went wrong.

0 comments on commit 810b9e4

Please sign in to comment.