Browse Source

Initial working state

master
Mahesh Asolkar 3 years ago
commit
cbdd1e3231
  1. 2
      .gitignore
  2. 17
      Makefile
  3. 25
      README.md
  4. 21
      RESEARCH.md
  5. 17
      data/actions.js
  6. 13
      data/jquery-ui.min.js
  7. 4
      data/jquery.min.js
  8. 125
      data/options.html
  9. 3
      data/options.js
  10. 192
      folder_actions-api/api.js
  11. 78
      folder_actions-api/schema.json
  12. BIN
      icons/button.png
  13. BIN
      icons/icon.png
  14. 53
      manifest.json

2
.gitignore

@ -0,0 +1,2 @@
*.xpi
*.zip

17
Makefile

@ -0,0 +1,17 @@
#
# Build extension package
#
VERSION = 2.0
SOURCES = manifest.json data/* icons/*
PACKAGE = ../emptyem-$(VERSION).xpi
EXCLUDES = Makefile README.md *.DS_Store
build: $(PACKAGE)
@echo "Done"
$(PACKAGE): $(SOURCES)
zip -r -FS $(PACKAGE) * -x $(EXCLUDES)
@echo "Built $(PACKAGE)"
clean:
rm $(PACKAGE)

25
README.md

@ -0,0 +1,25 @@
# Empty 'em
This is a simple Thunderbird extension that empties all _Junk_ and _Trash_
folders. The extension provides a menu item (`Tools` -> `Empty 'em`) and a
toolbar button to do so.
## Background
In Thunderbird, I usually have around half-a-dozen email accounts set up. Most
of them [Gmail](http://gmail.com) accounts. Very often, Gmail's Junk filter
catches tons of Junk email and populates the _Junk_ folders in Thunderbird.
After manually cleaning up the _Inbox_ folders and deleting unwanted email, the
_Trash_ folders are also populated.
Manually going through all the _Junk_ and _Trash_ folders to empty them, is
tedious.
That is what prompted me to write this extension. I hope someone out there
finds it helpful too.
# References
* https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Thunderbird_extensions
* https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Thunderbird_extensions/Demo_Addon
* https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Thunderbird_extensions/Building_a_Thunderbird_extension

21
RESEARCH.md

@ -0,0 +1,21 @@
# About New WebExtensions API
## IRC: inforation from #maildev
11:21:30 : moz_mma : In Thunderbird 64.0a1, using ChromeUtils.import() in webextension results in "ReferenceError: ChromeUtils is not defined" error. Any pointers?
12:00:15 : &Fallen : moz_mma: unless you are in an WebExtensions Experiment context, ChromeUtils and all the chrome code is not available
you need to make due with the APIs available, and create wrappers for APIs that are missing
12:04:25 : moz_mma : Fallen, how do I get in WebExtensions Experiment context?
12:04:45 : &Fallen : moz_mma: https://github.com/thundernest/tb-web-ext-experiments/ explains it mostly
for the cloudfile experiment, you would have access in e.g. https://github.com/thundernest/tb-web-ext-experiments/blob/master/cloudfile/cloudfile-api/api.js but not https://github.com/thundernest/tb-web-ext-experiments/blob/master/cloudfile/background/background.js
please be sure to read the intro to that repo so you know what the APIs should look like
12:07:30 : moz_mma : Thanks Fallen
12:14:03 : +aceman : Fallen: the ChromeUtils question above
## Demo add-on
https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Thunderbird_extensions/Demo_Addon
## Utils in comm-central mailnews/base
https://github.com/mozilla/releases-comm-central/tree/master/mailnews/base/util

17
data/actions.js

@ -0,0 +1,17 @@
//
// Empty 'em actions
//
"use strict";
// Browser actions
function emptyEm() {
console.log("Empty-em: Emptying.. Start");
browser.folder_actions.emptyTrashFolders();
browser.folder_actions.emptyJunkFolders();
console.log("Empty-em: Emptying.. Done");
}
// Handlers
browser.browserAction.onClicked.addListener(emptyEm);
// vim: ts=2 sw=2 sts=2 et

13
data/jquery-ui.min.js

File diff suppressed because one or more lines are too long

4
data/jquery.min.js

File diff suppressed because one or more lines are too long

125
data/options.html

@ -0,0 +1,125 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style type="text/css">
body {
font-family: sans-serif;
}
.option-cont {
display: inline-box;
vertical-align: top;
padding-top: 1.2em;
padding-bottom: 1.2em;
border-bottom: thin solid #ddd;
}
.option-info {
display: inline-block;
vertical-align: top;
width: 400px;
}
.option-val {
display: inline-block;
width: 50px;
}
.option-head {
font-weight: bold;
font-size: medium;
}
.option-desc {
font-size: small;
margin-top: 1.2em;
}
</style>
</head>
<body>
<div style="width: 600px">
<label>
<div class="option-cont">
<div class="option-info">
<div class="option-head">Override folder's confirmation setting</div>
<div class="option-desc">
By default, Thunderbird will prompt you to confirm deletion of selected
folders. Check this box to override this and suppress the conform
dialog
</div>
</div>
<div class="option-val">
<input type="checkbox" id="overrideDeleteConfirm" style="margin-bottom:1em"></input>
</div>
</div>
</label>
<label>
<div class="option-cont">
<div class="option-info">
<div class="option-head">Include Trash folders</div>
<div class="option-desc">
Include Trash folders in the process of emptying
</div>
</div>
<div class="option-val">
<input type="checkbox" id="selectTrashDelete" style="margin-bottom:1em"></input>
</div>
</div>
</label>
<label>
<div class="option-cont">
<div class="option-info">
<div class="option-head">Include Junk folders</div>
<div class="option-desc">
Include Junk folders in the process of emptying
</div>
</div>
<div class="option-val">
<input type="checkbox" id="selectJunkDelete" style="margin-bottom:1em"></input>
</div>
</div>
</label>
<label>
<div class="option-cont">
<div class="option-info">
<div class="option-head">Enable console debug messages</div>
<div class="option-desc">
Check this box to enable debug messages in Error Console. Provide
these messages along with bug reports you file
</div>
</div>
<div class="option-val">
<input type="checkbox" id="consoleDebug" style="margin-bottom:1em"></input>
</div>
</div>
</label>
<label>
<div class="option-cont">
<div class="option-info">
<div class="option-head">Disable done notification</div>
<div class="option-desc">
Check this box to disable notification that appears after all
configured folders have been emptied
</div>
</div>
<div class="option-val">
<input type="checkbox" id="disableDoneNotification" style="margin-bottom:1em"></input>
</div>
</div>
</label>
<label>
<div class="option-cont">
<div class="option-info">
<div class="option-head">Also compact emptied folders</div>
<div class="option-desc">
Also compact the folder that was emptied
</div>
</div>
<div class="option-val">
<input type="checkbox" id="alsoCompact" style="margin-bottom:1em"></input>
</div>
</div>
</label>
</div>
<script src="jquery.min.js"></script>
<script src="options.js"></script>
</body>
</html>

3
data/options.js

@ -0,0 +1,3 @@
// -----------------------------------------------
// Options UI and handling
// -----------------------------------------------

192
folder_actions-api/api.js

@ -0,0 +1,192 @@
"use strict";
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.import("resource:///modules/MailServices.jsm");
ChromeUtils.import("resource:///modules/iteratorUtils.jsm");
ChromeUtils.import("resource:///modules/folderUtils.jsm");
// API provider for Folder Actions
class FolderActionsProvider extends ExtensionCommon.EventEmitter {
constructor(extension) {
super();
this.extension = extension;
}
get type() {
return this.extension.id;
}
get displayName() {
return this.extension.manifest.folder_actions.name;
}
get iconClass() {
let { icon } = ExtensionParent.IconDetails.getPreferredIcon(this.extension.manifest.icons, this.extension, 32);
return this.extension.getURL(icon);
}
register() {
let uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
let contractID = "@mozilla.org/mail/folder_actions;1?type=" + this.extension.id.replace(/@/g, "-");
let self = this;
// unregisterFactory does not clear the contract id from Components.classes, therefore re-use
// the class id from the unregistered factory
if (contractID in Cc) {
this.classID = Components.ID(Cc[contractID].number);
} else {
this.classID = Components.ID(uuidgen.generateUUID().toString());
}
let factory = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIFactory]),
createInstance: function(outer, iid) {
if (outer !== null) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return self.QueryInterface(iid);
},
lockFactory: function(doLock) {
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
}
};
this.factory = factory.QueryInterface(Ci.nsIFactory);
registrar.registerFactory(
this.classID, `Cloud file provider for ${this.extension.id}`, contractID, this.factory
);
XPCOMUtils.categoryManager.addCategoryEntry(
"cloud-files", this.extension.id, contractID, false, true
);
}
unregister() {
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.unregisterFactory(this.classID, this.factory);
XPCOMUtils.categoryManager.deleteCategoryEntry("cloud-files", this.extension.id, false);
}
}
FolderActionsProvider.prototype.QueryInterface = ChromeUtils.generateQI([Ci.nsIMsgFolderActionsProvider]);
//
// Implementation of folder_actions API
//
this.folder_actions = class extends ExtensionAPI {
async onManifestEntry(entryName) {
if (entryName == "folder_actions" && !this.provider) {
this.provider = new FolderActionsProvider(this.extension);
this.provider.register();
}
}
onShutdown() {
if (this.provider) {
this.provider.unregister();
}
}
getFolderInfo() {
let data = [];
toArray(fixIterator(MailServices.accounts.accounts,Components.interfaces.nsIMsgAccount)).map(function(account) {
let info = {
server: null,
type: null,
emails: [],
trashFolder: null,
junkFolder: null,
name: null,
};
let server = account.incomingServer;
let find_flagged_folders = function(folder,inf) {
if (folder.getFlag(Ci.nsMsgFolderFlags.Trash)) {
console.log("Folder: " + folder.URI + " has Trash flag ");
inf.trashFolder = folder;
console.log(folder);
} else if (folder.getFlag(Ci.nsMsgFolderFlags.Junk)) {
console.log("Folder: " + folder.URI + " has Junk flag ");
inf.junkFolder = folder;
console.log(folder);
} else {
if (folder.hasSubFolders) {
toArray(fixIterator(folder.subFolders, Ci.nsMsgFolder)).map(function (f) {
find_flagged_folders(f,inf);
});
}
}
};
if (server) {
info.server = server.prettyName;
info.type = server.type;
if (info.type != "none") {
find_flagged_folders(server.rootFolder,info);
}
}
toArray(fixIterator(account.identities, Ci.nsIMsgIdentity)).map(function(id) {
if (id.email) info.emails.push(id.email);
});
data.push(info);
});
console.log(data);
return data;
}
getAPI(context) {
const EventManager = ExtensionCommon.EventManager;
let self = this;
let mw = Components.classes["@mozilla.org/messenger/msgwindow;1"]
.createInstance(Components.interfaces.nsIMsgWindow);;
return {
folder_actions: {
emptyFolder: function(folderName) {
let finfo = self.getFolderInfo();
},
emptyTrashFolders: function() {
let finfo = self.getFolderInfo();
finfo.map(function(info) {
if (info.trashFolder) {
console.log("emptyTrashFolders: Considering " + info.trashFolder.URI);
console.log("emptyTrashFolders: canDeleteMessages? " + info.trashFolder.canDeleteMessages);
console.log("emptyTrashFolders: hasSubFolders? " + info.trashFolder.hasSubFolders);
console.log(info.trashFolder);
info.trashFolder.emptyTrash(null,null);
}
});
},
emptyJunkFolders: function() {
let finfo = self.getFolderInfo();
finfo.map(function(info) {
if (info.junkFolder) {
console.log("emptyTrashFolders: Considering " + info.junkFolder.URI);
console.log("emptyTrashFolders: canDeleteMessages? " + info.junkFolder.canDeleteMessages);
console.log("emptyTrashFolders: hasSubFolders? " + info.junkFolder.hasSubFolders);
console.log(info.junkFolder);
var junk_msgs = Cc["@mozilla.org/array;1"]
.createInstance(Ci.nsIMutableArray);
var enumerator = info.junkFolder.messages;
while (enumerator.hasMoreElements())
{
var msg_hdr = enumerator.getNext().QueryInterface(Ci.nsIMsgDBHdr);
junk_msgs.appendElement(msg_hdr, false);
}
if (junk_msgs.length) {
info.junkFolder.deleteMessages(junk_msgs, mw, false, false, null, true);
}
}
});
}
}
};
}
};
// vim: ts=2 sw=2 sts=2 et

78
folder_actions-api/schema.json

@ -0,0 +1,78 @@
[
{
"namespace": "manifest",
"types": [
{
"$extend": "WebExtensionManifest",
"properties": {
"folder_actions": {
"type": "object",
"additionalProperties": { "$ref": "UnrecognizedProperty" },
"properties": {
"name": { "type": "string", "preprocess": "localize" }
},
"optional": true
}
}
}
]
},
{
"namespace": "folder_actions",
"events": [
{
"name": "onEmptyFolder",
"type": "function",
"description": "Fired when a folder is emptied",
"parameters": [
{
"name": "folderName",
"$ref": "MailFolder",
"description": "The folder to be emptied"
}
]
}
],
"types": [
{
"id": "MailFolder",
"type": "object",
"description": "Information about a mail folder",
"properties": {
"name": {
"type": "string",
"description": "Name of the folder to be emptied"
}
}
}
],
"functions": [
{
"name": "emptyFolder",
"type": "function",
"description": "Empties specified mail folder",
"parameters": [
{
"name": "folderName",
"$ref": "MailFolder",
"description": "The mail folder to empty"
}
]
},
{
"name": "emptyTrashFolders",
"type": "function",
"description": "Empties Trash folders in all accounts",
"parameters": []
},
{
"name": "emptyJunkFolders",
"type": "function",
"description": "Empties Junk folders in all accounts",
"parameters": []
}
]
}
]
// vim: ts=2 sw=2 sts=2 et

BIN
icons/button.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
icons/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

53
manifest.json

@ -0,0 +1,53 @@
{
"manifest_version": 2,
"name": "Empty 'em",
"version": "2.0",
"description": "Empty all your Trash and Junk folders in one fell swoop!",
"homepage_url": "https://github.com/asolkar/emptyem",
"icons": {
"256": "icons/icon.png"
},
"applications": {
"gecko": {
"id": "@emptyem",
"strict_min_version": "63.0"
}
},
"browser_action": {
"default_icon": {
"16": "icons/icon.png",
"32": "icons/icon.png"
},
"default_title": "Empty 'em"
},
// "folder_actions": {
// "name": "EmptyEm"
// },
"options_ui": {
"page": "data/options.html"
},
"background": {
"scripts": ["data/actions.js"]
},
"experiment_apis": {
"folder_actions": {
"schema": "folder_actions-api/schema.json",
"parent": {
"scopes": ["addon_parent"],
"script": "folder_actions-api/api.js",
"paths": [
["folder_actions"]
]
}
}
}
}
// vim: ts=2 sw=2 sts=2 et
Loading…
Cancel
Save