Initial working state
This commit is contained in:
commit
cbdd1e3231
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.xpi
|
||||
*.zip
|
17
Makefile
Normal file
17
Makefile
Normal file
@ -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
Normal file
25
README.md
Normal file
@ -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
Normal file
21
RESEARCH.md
Normal file
@ -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
Normal file
17
data/actions.js
Normal file
@ -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
vendored
Normal file
13
data/jquery-ui.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
data/jquery.min.js
vendored
Normal file
4
data/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
125
data/options.html
Normal file
125
data/options.html
Normal file
@ -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
Normal file
3
data/options.js
Normal file
@ -0,0 +1,3 @@
|
||||
// -----------------------------------------------
|
||||
// Options UI and handling
|
||||
// -----------------------------------------------
|
192
folder_actions-api/api.js
Normal file
192
folder_actions-api/api.js
Normal file
@ -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
Normal file
78
folder_actions-api/schema.json
Normal file
@ -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
Normal file
BIN
icons/button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
icons/icon.png
Normal file
BIN
icons/icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
53
manifest.json
Normal file
53
manifest.json
Normal file
@ -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…
Reference in New Issue
Block a user