Skip to content
This repository has been archived by the owner on Nov 3, 2021. It is now read-only.

Commit

Permalink
Bug 832924 - [Email] Support POP3 accounts. r=asuth
Browse files Browse the repository at this point in the history
  • Loading branch information
mcav committed Nov 21, 2013
1 parent 298e6c5 commit 92b708e
Show file tree
Hide file tree
Showing 29 changed files with 16,889 additions and 3,424 deletions.
78 changes: 64 additions & 14 deletions apps/email/js/cards/message_list.js
Expand Up @@ -5,6 +5,7 @@ define(function(require) {
var templateNode = require('tmpl!./message_list.html'),
msgHeaderItemNode = require('tmpl!./msg/header_item.html'),
deleteConfirmMsgNode = require('tmpl!./msg/delete_confirm.html'),
largeMsgConfirmMsgNode = require('tmpl!./msg/large_message_confirm.html'),
common = require('mail_common'),
model = require('model'),
htmlCache = require('html_cache'),
Expand Down Expand Up @@ -1124,7 +1125,7 @@ MessageListCard.prototype = {
unreadNode.classList.add('msg-header-unread-section-unread');
dateNode.classList.add('msg-header-date-unread');
}
// star
// starmail
var starNode = msgNode.getElementsByClassName('msg-header-star')[0];
if (message.isStarred)
starNode.classList.add('msg-header-star-starred');
Expand Down Expand Up @@ -1169,20 +1170,49 @@ MessageListCard.prototype = {
return;
}

Cards.pushCard(
'message_reader', 'default', 'animate',
{
// The header here may be undefined here, since the click
// could be on a cached HTML node before the back end has
// started up. It is OK if header is not available as the
// message_reader knows how to wait for the back end to
// start up to get the header value later.
header: header,
// Use the property on the HTML, since the click could be
// from a cached HTML node and the real data object may not
// be available yet.
messageSuid: messageNode.dataset.id
function pushMessageCard() {
Cards.pushCard(
'message_reader', 'default', 'animate',
{
// The header here may be undefined here, since the click
// could be on a cached HTML node before the back end has
// started up. It is OK if header is not available as the
// message_reader knows how to wait for the back end to
// start up to get the header value later.
header: header,
// Use the property on the HTML, since the click could be
// from a cached HTML node and the real data object may not
// be available yet.
messageSuid: messageNode.dataset.id
});
}

// If the message is really big, warn them before they open it.
// Ideally we'd only warn if you're on a cell connection
// (metered), but as of now `navigator.connection.metered` isn't
// implemented.

// This number is somewhat arbitrary, based on a guess that most
// plain-text/HTML messages will be smaller than this. If this
// value is too small, users get warned unnecessarily. Too large
// and they download a lot of data without knowing. Since we
// currently assume that all network connections are metered,
// they'll always see this if they get a large message...
var LARGE_MESSAGE_SIZE = 1 * 1024 * 1024;

// watch out, header might be undefined here (that's okay, see above)
if (header && header.bytesToDownloadForBodyDisplay > LARGE_MESSAGE_SIZE) {
this.showLargeMessageWarning(
header.bytesToDownloadForBodyDisplay, function(result) {
if (result) {
pushMessageCard();
} else {
// abort
}
});
} else {
pushMessageCard();
}
},

onHoldMessage: function(messageNode, event) {
Expand Down Expand Up @@ -1256,6 +1286,26 @@ MessageListCard.prototype = {
);
},

/**
* Show a warning that the given message is large.
* Callback is called with cb(true|false) to continue.
*/
showLargeMessageWarning: function(size, cb) {
var dialog = largeMsgConfirmMsgNode.cloneNode(true);
// TODO: If UX designers want the size included in the warning
// message, add it here.
ConfirmDialog.show(dialog,
{ // Confirm
id: 'msg-large-message-ok',
handler: function() { cb(true); }
},
{ // Cancel
id: 'msg-large-message-cancel',
handler: function() { cb(false); }
}
);
},

onMoveMessages: function() {
// TODO: Batch move back-end mail api is not ready now.
// Please verify this function when api landed.
Expand Down
99 changes: 76 additions & 23 deletions apps/email/js/cards/message_reader.js
Expand Up @@ -124,6 +124,9 @@ function MessageReaderCard(domNode, mode, args) {

// event handler for body change events...
this.handleBodyChange = this.handleBodyChange.bind(this);

// whether or not we've built the body DOM the first time
this._builtBodyDom = false;
}
MessageReaderCard.prototype = {
_contextMenuType: {
Expand Down Expand Up @@ -184,13 +187,7 @@ MessageReaderCard.prototype = {
},

handleBodyChange: function(evt) {
switch (evt.changeType) {
case 'bodyReps':
if (this.body.bodyRepsDownloaded) {
this.buildBodyDom();
}
break;
}
this.buildBodyDom(evt.changeDetails);
},

onBack: function(event) {
Expand Down Expand Up @@ -697,7 +694,16 @@ MessageReaderCard.prototype = {
header);
},

buildBodyDom: function() {
/**
* Render the DOM nodes for bodyReps and the attachments container.
* If we have information on which parts of the message changed,
* only update those DOM nodes; otherwise, update the whole thing.
*
* @param {object} changeDetails
* @param {array} changeDetails.bodyReps An array of changed item indexes.
* @param {array} changeDetails.attachments An array of changed item indexes.
*/
buildBodyDom: function(/* optional */ changeDetails) {
var body = this.body;
var domNode = this.domNode;

Expand All @@ -707,20 +713,59 @@ MessageReaderCard.prototype = {
showEmbeddedImages = body.embeddedImageCount &&
body.embeddedImagesDownloaded;

iframeShims.bindSanitizedClickHandler(rootBodyNode,
this.onHyperlinkClick.bind(this),
rootBodyNode,
null);

// The first time we build the body DOM, do one-time bootstrapping:
if (!this._builtBodyDom) {
iframeShims.bindSanitizedClickHandler(rootBodyNode,
this.onHyperlinkClick.bind(this),
rootBodyNode,
null);
this._builtBodyDom = true;
}

// If we have fully downloaded one body part, the user has
// something to read so get rid of the spinner.
// XXX: Potentially improve the UI to show if we're still
// downloading the rest of the body even if we already have some
// of it.
if (reps.length && reps[0].isDownloaded) {
// remove progress bar if we've retrieved the first rep
var progressNode = rootBodyNode.querySelector('progress');
if (progressNode) {
progressNode.parentNode.removeChild(progressNode);
}
}

// The logic below depends on having removed the progress node!

for (var iRep = 0; iRep < reps.length; iRep++) {
var rep = reps[iRep];

// Create an element to hold this body rep. Even if we aren't
// updating this rep right now, we need to have a placeholder.
var repNode = rootBodyNode.childNodes[iRep];
if (!repNode) {
repNode = rootBodyNode.appendChild(document.createElement('div'));
}

// Skip updating this rep if it's not updated.
if (changeDetails && changeDetails.bodyReps &&
changeDetails.bodyReps.indexOf(iRep) === -1) {
continue;
}

// Wipe out the existing contents of the rep node so we can
// replace it. We can just nuke innerHTML since we add click
// handlers on the rootBodyNode, and for text/html parts the
// listener is a child of repNode so it will get destroyed too.
repNode.innerHTML = '';

if (rep.type === 'plain') {
this._populatePlaintextBodyNode(rootBodyNode, rep.content);
this._populatePlaintextBodyNode(repNode, rep.content);
}
else if (rep.type === 'html') {
var iframeShim = iframeShims.createAndInsertIframeForContent(
rep.content, this.scrollContainer, rootBodyNode, null,
rep.content, this.scrollContainer, repNode, null,
'interactive', this.onHyperlinkClick.bind(this));
var iframe = iframeShim.iframe;
var bodyNode = iframe.contentDocument.body;
Expand All @@ -733,16 +778,10 @@ MessageReaderCard.prototype = {
if (showEmbeddedImages)
body.showEmbeddedImages(bodyNode, this.iframeResizeHandler);
}

if (iRep === 0) {
// remove progress bar
var progressNode = rootBodyNode.querySelector('progress');
if (progressNode) {
progressNode.parentNode.removeChild(progressNode);
}
}
}

// The image logic checks embedded image counts, so this should be
// able to run every time:
// -- HTML-referenced Images
var loadBar = domNode.getElementsByClassName('msg-reader-load-infobar')[0];
if (body.embeddedImageCount && !body.embeddedImagesDownloaded) {
Expand Down Expand Up @@ -784,6 +823,20 @@ MessageReaderCard.prototype = {
filesizeTemplate =
attTemplate.getElementsByClassName('msg-attachment-filesize')[0];
for (var iAttach = 0; iAttach < body.attachments.length; iAttach++) {

// Create an element to hold this attachment.
var attNode = attachmentsContainer.childNodes[iAttach];
if (!attNode) {
attNode = attachmentsContainer.appendChild(
document.createElement('li'));
}

// Skip updating this attachment if it's not updated.
if (changeDetails && changeDetails.attachments &&
changeDetails.attachments.indexOf(iAttach) === -1) {
continue;
}

var attachment = body.attachments[iAttach], state;
var extension = attachment.filename.split('.').pop();

Expand All @@ -800,7 +853,7 @@ MessageReaderCard.prototype = {
attachment.sizeEstimateInBytes);

var attachmentNode = attTemplate.cloneNode(true);
attachmentsContainer.appendChild(attachmentNode);
attachmentsContainer.replaceChild(attachmentNode, attNode);
attachmentNode.getElementsByClassName('msg-attachment-download')[0]
.addEventListener('click',
this.onDownloadAttachmentClick.bind(
Expand Down
10 changes: 10 additions & 0 deletions apps/email/js/cards/msg/large_message_confirm.html
@@ -0,0 +1,10 @@
<form role="dialog" class="msg-large-message-confirm" data-type="confirm">
<section>
<h1 data-l10n-id="confirm-dialog-title">ConfirmatioN</h1>
<p><span data-l10n-id="message-large-message-confirm"></span></p>
</section>
<menu>
<button id="msg-large-message-cancel" data-l10n-id="message-large-message-cancel">CanceL</button>
<button id="msg-large-message-ok" data-l10n-id="message-large-message-ok">OK</button>
</menu>
</form>
3 changes: 2 additions & 1 deletion apps/email/js/cards/settings_account.js
Expand Up @@ -41,7 +41,8 @@ function SettingsAccountCard(domNode, mode, args) {

// ActiveSync, IMAP and SMTP are protocol names, no need to be localized
this.nodeFromClass('tng-account-type').textContent =
(this.account.type === 'activesync') ? 'ActiveSync' : 'IMAP+SMTP';
(this.account.type === 'activesync') ? 'ActiveSync' :
(this.account.type === 'imap+smtp') ? 'IMAP+SMTP' : 'POP3+SMTP';

// Handle default account checkbox. If already a default, then the
// checkbox cannot be unchecked. The default is changed by going to an
Expand Down
3 changes: 2 additions & 1 deletion apps/email/js/cards/settings_account_credentials.js
Expand Up @@ -7,7 +7,8 @@ var templateNode = require('tmpl!./settings_account_credentials.html'),
Cards = common.Cards;

/**
* Per-account credentials settings, it can be activesync or imap+smtp
* Per-account credentials settings, it can be activesync, imap+smtp,
* or pop3+smtp
*/
function SettingsAccountCredentialsCard(domNode, mode, args) {
this.domNode = domNode;
Expand Down
3 changes: 2 additions & 1 deletion apps/email/js/cards/settings_account_servers.js
Expand Up @@ -7,7 +7,8 @@ var templateNode = require('tmpl!./settings_account_servers.html'),
Cards = common.Cards;

/**
* Per-account server settings, it can be activesync or imap+smtp
* Per-account server settings, it can be activesync, imap+smtp, or
* pop3+smtp
*/
function SettingsAccountServerCard(domNode, mode, args) {
this.domNode = domNode;
Expand Down
11 changes: 11 additions & 0 deletions apps/email/js/cards/setup_fix_gmail.html
@@ -0,0 +1,11 @@
<div class="card-setup-fix-gmail card">
<div class="sup-account-header top-header">
<h2 class="sup-account-header-label header-label">ImaP/PoP3 DisableD</h2>
</div>
<div class="scrollregion-below-header">
<h2 class="sup-enable-label sup-form-label">EnablE ImaP/PoP3</h2>
<span class="sup-gmail-account"></span>

<button class="sup-dismiss-btn sup-form-btn">RetrY</button>
</div>
</div>
51 changes: 51 additions & 0 deletions apps/email/js/cards/setup_fix_gmail.js
@@ -0,0 +1,51 @@
/*global define*/
define(function(require) {

var templateNode = require('tmpl!./setup_fix_gmail.html'),
common = require('mail_common'),
mozL10n = require('l10n!'),
Cards = common.Cards;

/**
* Tells the user how to enable IMAP/POP3 for Gmail
*/
function SetupFixGmail(domNode, mode, args) {
this.domNode = domNode;
this.account = args.account;
this.restoreCard = args.restoreCard;

var accountNode =
domNode.getElementsByClassName('sup-gmail-account')[0];
accountNode.textContent = this.account.name;

var incomingType = (this.account.type === 'imap+smtp' ? 'imap' : 'pop3');
domNode.getElementsByClassName('sup-account-header-label')[0].textCountent =
mozL10n.get('setup-gmail-' + incomingType + '-header');
domNode.getElementsByClassName('sup-enable-label')[0].textCountent =
mozL10n.get('setup-gmail-' + incomingType + '-message');
domNode.getElementsByClassName('sup-dismiss-btn')[0].textCountent =
mozL10n.get('setup-gmail-' + incomingType + '-retry');

var useButton = domNode.getElementsByClassName('sup-dismiss-btn')[0];
useButton.addEventListener('click', this.onDismiss.bind(this), false);
}
SetupFixGmail.prototype = {
die: function() {
// no special cleanup required
},

onDismiss: function() {
this.account.clearProblems();
Cards.removeCardAndSuccessors(this.domNode, 'animate', 1,
this.restoreCard);
}
};
Cards.defineCardWithDefaultMode(
'setup_fix_gmail',
{ tray: false },
SetupFixGmail,
templateNode
);

return SetupFixGmail;
});
14 changes: 0 additions & 14 deletions apps/email/js/cards/setup_fix_gmail_imap.html

This file was deleted.

0 comments on commit 92b708e

Please sign in to comment.