Office.js, Office.context.mailbox.item.cc.getAsync does not work - outlook

I'm working to create a Outlook add-in using office-js.
I need to get cc and to of an e-mail message.
Since I read these pages, I write this code. However, when cc.getAsync and to.getAsync works nothing whereas body.getAsync returns collect body of the message.
https://learn.microsoft.com/ja-jp/javascript/api/requirement-sets/outlook/requirement-set-1.5/office.context.mailbox.item?view=common-js-preview
https://learn.microsoft.com/en-us/javascript/api/outlook/office.recipients?view=outlook-js-1.5&preserve-view=true
// It does not work though I expect to appear cc of the message on taskpane of my add-in
const item = Office.context.mailbox.item;
item.cc.getAsync(function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
document.getElementById("message").innerHTML = `<h2 class="message">${asyncResult.value}</h2>`;
document.getElementById("message").style.visibility = "visible";
}
});
// same here
const item = Office.context.mailbox.item;
item.to.getAsync(function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
document.getElementById("message").innerHTML = `<h2 class="message">${asyncResult.value}</h2>`;
document.getElementById("message").style.visibility = "visible";
}
});
// on the other hand, I can see the body of the message on my taskpane
item.body.getAsync(Office.CoercionType.Html, function callback(result) {
if (result.status === Office.AsyncResultStatus.Succeeded) {
document.getElementById("message").innerHTML = `<h2 class="message">${result.value}</h2>`;
document.getElementById("message").style.visibility = "visible";
}
});

Related

How do I select a range and change font color of specific words in Outlook web add-ins

In Outlook web add-in, I want to select text from email body, keep it as tracked object and then apply styling on it like we do in Word web add-in. In the Word, I do it the following way:
//used to store current selected range
var selection=null;
$scope.getSelectedText = function () {
Word.run(function (context) {
if (selection) {
context.trackedObjects.remove(selection);
}
selection = context.document.getSelection();
context.trackedObjects.add(selection);
context.load(selection, 'text');
return context.sync()
.then(function () {
if (!selection.text) {
systemService.errorHandler("Please select any text from the document");
return false;
}
}).then(context.sync);
})
.catch(systemService.errorHandler);
};
//highlight Words in the selected range or body
$scope.highlightWords = function (item) {
var color = systemService.getColor(item.gradeText);
var filteredWords = JSON.parse(localStorage.getItem("List")) || [];
var promise = new OfficeExtension.Promise(function (resolve, reject) { resolve(null); });
Word.run(function (context) {
var selectedRange = (selection) ? selection : context.document.body;
selectedRange.load('text');
return context.sync().then(function () {
if (selectedRange.text == "") {
systemService.showNotification("Alert:", "No text found in the document!");
return;
}
filteredWords.forEach(function (word) {
promise = promise.then(function () {
return highlightRange(word, color);
})
});
});
}).catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
function highlightRange(word, color) {
return Word.run(selection, function (context) {
var range = (selection) ? selection : context.document.body;
var searchResults = null;
searchResults = range.search(word, { ignorePunct: true, matchCase: false, matchWholeWord: true });
searchResults.load('font');
return context.sync().then(function () {
for (var i = 0; i < searchResults.items.length; i++) {
searchResults.items[i].font.color = color;
searchResults.items[i].font.bold = true;
}
}).then(context.sync());
}).catch(function (error) {
console.log('Error: ' + JSON.stringify(error));
if (error instanceof OfficeExtension.Error) {
console.log('Debug info: ' + JSON.stringify(error.debugInfo));
}
});
}
//highlight Words in the selected range or body
I have tried multiple ways to do but there is a problem while keeping track of the selected object. In Word, we can save the range and manipulate it overtime as needed, but in Outlook, the user have to keep the content selected in order to perform operation on it (like changing font color etc.). Also, do we have any font property available for outlook web add-ins like Word?

Chain filter conditions dynamically

How to chain multiple conditions in RethinkDB? This is what I got right now and what works if I only pass live or sports as a parameter. As soon as I pass the live and sports parameter, sports obviously always overwrites the filter variable and the live parameter is ignored.
app.get('/bets', function (req, res) {
var live = req.query.live;
var sports = req.query.sports;
var filter = {};
if (live === undefined) {
filter = r.or(r.row('live').eq(0), r.row('live').eq(1));
} else {
filter.live = parseInt(live);
}
if (sports !== undefined) {
var sports = sports.split(',');
filter = function (doc) {
return r.expr(sports).contains(doc("sport"));
}
}
r.table('bets').filter(filter).limit(100).run(connection, function(err, cursor) {
// ...
});
});
You can chain filters with RethinkDB.
Something along the lines of this (warning, untested) :
app.get('/bets', function (req, res) {
var live = req.query.live;
var sports = req.query.sports;
var liveFilter, sportFilter;
if (live === undefined) {
liveFilter = r.or(r.row('live').eq(0), r.row('live').eq(1));
} else {
liveFilter = function (doc) { return true; };
}
if (sports !== undefined) {
sports = sports.split(','); // no need to redefine the variable here
sportFilter = function (doc) {
return r.expr(sports).contains(doc("sport"));
}
} else {
sportFilter = function (doc) { return true; };
}
r.table('bets')
.filter(liveFilter) // apply the first filter
.filter(sportsFilter) // apply the second filter
.limit(100)
.run(connection, function(err, cursor) {
// ...
});
});
Alternatively you could make one filter function that would handle both the live and sport filters (equally untested, this is to get you started) :
app.get('/bets', function (req, res) {
var live = req.query.live;
var sports = req.query.sports.split(',');
var filter = function(doc){
var sportPass, livePass;
if (live === undefined) {
livePass = r.or(r.row('live').eq(0), r.row('live').eq(1))(doc);
} else {
livePass = parseInt(live); // not sure what you meant by filter.live here
}
if (sports !== undefined) {
sportPass = r.expr(sports).contains(doc("sport"));
}
return sportPass && livePass;
};
r.table('bets').filter(filter).limit(100).run(connection, function(err, cursor) {
// ...
});
});

Firefox extension: Refer to object defined in bootstrap.js from XUL file

I'm trying to contribute to a Firefox extension I use, but I have no idea what I'm doing :)
I've got a dialog powered by a XUL document to gather some data from the user. That's all fine and good. But when the user confirms the dialog, I need to call a function defined on an object that's defined in my bootstrap.js file. Is that possible to do directly? e.g., in the XUL file:
<prefpanes id="my-pane" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script>
function myCallback() {
PassFF.myAction(); // PassFF is undefined here
};
</script>
<prefpane>
<button id="my-button" oncommand="myCallback();" />
</prefpane>
</prefpanes>
If that's not possible, is it possible to register a callback when I open the document in the first place? Something like this:
var dialog = window.openDialog("chrome://passff/content/mypane.xul",
"my_pane_name",
"chrome,titlebar,toolbar,modal");
dialog.addEventListener('close', function(event) {
PassFF.myAction(); // PassFF is defined here, but the event doesn't fire
});
I've tried things like importing the bootstrap.js script in my XUL document with another script tag, adding another "content" line to the manifest to expose the file, different events (unload, command, click) but couldn't figure out any of these approaches either.
Any tips?
You can use, for example, platform observer mechanics.
this is "signals.js" file (module):
let EXPORTED_SYMBOLS = ["addSignalListener", "removeSignalListener", "emitSignal", "signalNamePrefix"];
const {utils:Cu, classes:Cc, interfaces:Ci, results:Cr} = Components;
function genUUID () {
let uuidgen = Cc["#mozilla.org/uuid-generator;1"].createInstance(Ci.nsIUUIDGenerator);
if (!uuidgen) throw new Error("no UUID generator available!");
return uuidgen.generateUUID().toString().replace(/[^-a-z0-9]/ig, "");
}
const signalNamePrefix = "signal-"+genUUID()+"-";
const obs = Cc["#mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
let observers = {};
function addSignalListener (name, cback) {
if (typeof(name) !== "string" || !name) throw new Error("invalid signal name");
if (typeof(cback) !== "function") throw new Error("callback function expected");
// check if already here
if (cback in observers) {
let names = observers[cback];
if (name in names) return; // nothing to do
} else {
observers[cback] = {};
}
let observer = {
observe: function (subject, topic, data) {
topic = topic.substr(signalNamePrefix.length); // remove prefix
if (data && data.length) {
try {
data = JSON.parse(data);
} catch (e) {
Cu.reportError(e);
return;
}
} else {
data = null;
}
cback(topic, data);
},
};
obs.addObserver(observer, signalNamePrefix+name, false);
observers[cback][name] = observer;
}
function removeSignalListener (name, cback) {
if (typeof(name) !== "string" || !name) throw new Error("invalid signal name");
if (typeof(cback) !== "function") throw new Error("callback function expected");
// find observer
let names = observers[cback];
if (names === undefined) return; // nothing to do
if (!(name in names)) return; // nothing to do
try { obs.removeObserver(observers[cback][name], name); } catch (e) {}
}
function emitSignal (name, data) {
if (typeof(name) !== "string" || !name) throw new Error("invalid signal name");
data = (typeof(data) === "undefined" ? null : (data !== null ? JSON.stringify(data) : null));
obs.notifyObservers(null, signalNamePrefix+name, data);
}
sample use:
Components.utils.import("chrome://myext/content/signals.js");
dump(signalNamePrefix+"\n");
addSignalListener("mysignal", function (signame, data) {
dump("MYSIGNAL("+signame+"): "+data+"\n");
for (let [k, v] of Iterator(data)) dump(" ["+k+"]=["+v+"]\n");
});
emitSignal("mysignal", {any:42, data:[669], here:"wow!"});
here just import "signals.js" in your "bootstrap.js", and add signal listener. then import "signals.js" in your dialog js, and emit signal with the data you need.

Backbone collection fetch error with no information

I have a strange problem with the fetch of a backbone collection I am working with. In one particular instance of my code I perform a fetch (exactly how I do it in other areas of the code which all work fine), the fetch never seems to make it to the server and the developer tools shows the request as red with the word (canceled) in the status/text field.
I've walked this through into the backbone sync method and I see the $.ajax being built and everything looks fine. Has anyone run into this problem?
here is my code if it helps, this is a function that calls two .ashx services to first check for a file's existence then to open it. The part that isn't working for me is the "me.collection.fetch().
openDocument: function () {
var me = this,
fileId = me.model.get('id'),
userId = Dashboard.Data.Models.UserModel.get("UserInfo").User_ID,
fileRequest = '/genericHandlers/DownloadFile.ashx?id=' + fileId + '&userId=' + userId,
fileCheck = '/genericHandlers/CheckFileExistance.ashx?id=' + fileId + '&userId=' + userId;
//hide tooltip
me.hideButtonTooltips();
// Check for file existance
$.ajax({
url: fileCheck
})
.done(function (data) {
if (data && data === "true") {
document.location.href = fileRequest;
me.collection.fetch();
} else if (!!data && data === "false") {
"This file is no longer available.".notify('error');
}
})
.fail(function (data) {
"Something went wrong during the File Existance check".notify('error');
"Something went wrong during the File Existance check".log(userId, 'error', 'Docs');
});
},
my collection:
// docsCollection.js - The collection of ALL the documents available to a given user
// Document Collection
Dashboard.Collections.DocsCollection = Backbone.Collection.extend({
model: Dashboard.Models.DocumentUploadModel,
url: function () {
return 'apps/docs/Docs/' + this.userId;
},
initialize: function (options) {
this.userId = options.userId;
this.deferredFetch = this.fetch();
},
comparator: function (model) {
return -(new Date(model.get('expirationDate')));
},
getDaysSinceViewedDocuments: function () {
return this.filter(function (model) {
return model.get('daysSinceViewed') !== null;
});
},
getNewDocuments: function () {
return this.filter(function (model) {
return model.get('isNew');
});
},
getExpiredDocuments: function () {
return this.filter(function (model) {
return model.get('isExpired');
});
}
});
and my model:
Dashboard.Models.DocumentUploadModel = Backbone.Model.extend({
defaults: {
fileArray: [],
name: '',
description: '',
accesses: [],
tags: [],
expirationDate: ''
},
initialize: function () {
this.set({
userId: Dashboard.Data.Models.UserModel.get("UserInfo").User_ID,
expirationDate: (this.isNew()) ? buildExpirationDate() : this.get('expirationDate')
}, { silent: true });
function buildExpirationDate() {
var date = new Date((new Date()).getTime() + 24 * 60 * 60 * 1000 * 7),
dateString = "{0}/{1}/{2}".format(date.getMonth() + 1, date.getDate(), date.getFullYear());
return dateString;
}
},
firstFile: function () {
return this.get('fileArray')[0];
},
validate: function (attributes) {
var errors = [];
if (attributes.name === '' || attributes.name.length === 0)
errors.push({
input: 'input.txtName',
message: "You must enter a name."
});
if (attributes.description === '' || attributes.description.length === 0)
errors.push({
input: 'textarea.taDescription',
message: "You must enter a description."
});
if (errors.length > 0)
return errors;
return;
},
sync: function (method, model, options) {
var formData = new FormData(),
files = model.get("fileArray"),
$progress = $('progress'),
success = options.success,
error = options.error;
// Nothing other than create or update right now
if (method !== "create" && method !== "update")
return;
// Build formData object
formData.append("name", model.get("name"));
formData.append("description", model.get("description"));
formData.append("accesses", model.get("accesses"));
formData.append("tags", model.get("tags"));
formData.append("expirationDate", model.get("expirationDate"));
formData.append("userId", model.get("userId"));
formData.append("isNew", model.isNew());
// if not new then capture id
if (!model.isNew())
formData.append('id', model.id);
for (var i = 0; i < files.length; i++) {
formData.append('file', files[i]);
}
xhr = new XMLHttpRequest();
xhr.open('POST', '/genericHandlers/UploadDocsFile.ashx');
xhr.onload = function () {
if (xhr.status === 200) {
if (success)
success();
} else {
if (error)
error();
}
}
if ($progress.length > 0) {
xhr.upload.onprogress = function (evt) {
var complete;
if (evt.lengthComputable) {
// Do the division but if you cant put 0
complete = (evt.loaded / evt.total * 100 | 0);
$progress[0].value = $progress[0].innerHTML = complete;
}
}
}
xhr.send(formData);
},
upload: function (changedAttrs, options) {
this.save("create", changedAttrs, options);
}
});
You're assigning a value to document.location.href before you try to fetch your collection:
document.location.href = fileRequest;
me.collection.fetch();
Changing document.location.href will change the whole page and in the process, any currently running JavaScript will get shutdown so I wouldn't expect your me.collection.fetch() to ever get executed.

Firefox, Mozilla validator error

I am getting this one error when I use the Mozilla validator:
This is the JS file:
const STATE_START = Components.interfaces.nsIWebProgressListener.STATE_START;
const STATE_STOP = Components.interfaces.nsIWebProgressListener.STATE_STOP;
// Version changes:
// It used to get the lists from a PHP file, but that was putting too much of a strain on the servers
// now it uses xml files.
// Randomizes the servers to load balance
// Mozilla editor suggested no synchronous file gets, so changed it to asynchronous
// Added one more server to help with the updates (Ilovemafiaafire.net)
// Edited some redirect code that some idiots were spreading FUD about.
var xmlDoc = null;
var quickFilter_100_count_redirect_url='http://www.mafiaafire.com/help_us.php';
var countXmlUrl = 0;
//var xmlUrl = 'http://elxotica.com/xml-update/xml-list.php';
var xmlUrl = new Array(4);
xmlUrl[0] = 'http://mafiaafire.com/xml-update/mf_xml_list.xml';
xmlUrl[1] = 'http://ifucksexygirls.com/xml-update/mf_xml_list.xml';
xmlUrl[2] = 'http://ezee.se/xml-update/mf_xml_list.xml';
xmlUrl[3] = 'http://ilovemafiaafire.net/mf_xml_list.xml';
xmlUrl.sort(function() {return 0.5 - Math.random()})
var realXmlUrl = xmlUrl[countXmlUrl];
var notificationUrl = 'http://mafiaafire.com/xml-update/click_here_for_details.php';
var root_node = null;
var second_node = null;
var timervar = null;
var mafiaafireFilterUrl = '';
//Calling the interface for preferences
var prefManager = Components.classes["#mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefBranch);
var quickfilter_mafiaafire =
{
// get the domain name from the current url
get_domain_name:function()
{
var urlbar = window.content.location.href;
domain_name_parts = urlbar.match(/:\/\/(.[^/]+)/)[1].split('.');
if(domain_name_parts.length >= 3){
domain_name_parts[0] = '';
}
var dn = domain_name_parts.join('.');
if(dn.indexOf('.') == 0)
return dn.substr(1);
else
return dn;
},
// send ajax request to server for loading the xml
request_xml:function ()
{
//alert(countXmlUrl);
http_request = false;
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType) {
http_request.overrideMimeType('text/xml');
}
if (!http_request)
{
return false;
}
http_request.onreadystatechange = this.response_xml;
http_request.open('GET', realXmlUrl, true);
http_request.send(null);
xmlDoc = http_request.responseXML;
},
// receive the ajax response
response_xml:function ()
{
if (http_request.readyState == 4)
{
if(http_request.status == 404 && countXmlUrl<=3)
{
countXmlUrl++;
//alert(xmlUrl[countXmlUrl]);
realXmlUrl = xmlUrl[countXmlUrl];
quickfilter_mafiaafire.request_xml();
}
if (http_request.status == 200)
{
xmlDoc = http_request.responseXML;
}
}
},
filterUrl:function()
{
var urlBar = window.content.location.href;
//check if url bar is blank or empty
if (urlBar == 'about:blank' || urlBar == '' || urlBar.indexOf('http')<0)
return false;
//1. get domain
processing_domain = this.get_domain_name();
//alert(processing_domain);
//Couldn't fetch the XML config, so returning gracefully
if(xmlDoc == null)
return false;
try
{
root_node = '';
// Parsing the xml
root_node = xmlDoc.getElementsByTagName('filter');
for(i=0;i<=root_node.length;i++)
{
second_node = '';
second_node = root_node[i];
if(second_node.getElementsByTagName('realdomain')[0].firstChild.nodeValue == processing_domain)
{
this.notificationBox();
mafiaafireFilterUrl = '';
mafiaafireFilterUrl = second_node.getElementsByTagName('filterdomain')[0].firstChild.nodeValue;
timervar = setTimeout("quickfilter_mafiaafire.redirectToAnotherUrl()",1500);
//window.content.location.href = second_node.getElementsByTagName('filterdomain')[0].firstChild.nodeValue;
//this.redirectToAnotherUrl(this.filterUrl);
//timervar = setInterval("quickfilter_mafiaafire.redirectToAnotherUrl(quickfilter_mafiaafire.filterUrl)",1000);
}
}
}
catch(e){
//alert(e.toString());
}
},
// This function is called for showing the notification
notificationBox:function()
{
try{
// Firefox default notification interface
var notificationBox = gBrowser.getNotificationBox();
notificationBox.removeAllNotifications(false);
notificationBox.appendNotification('You are being redirected', "", "chrome://quickfilter/content/filter.png", notificationBox.PRIORITY_INFO_HIGH, [{
accessKey: '',
label: ' click here for details',
callback: function() {
// Showing the notification Bar
window.content.location.href = notificationUrl;
}
}]);
}catch(e){}
},
redirectToAnotherUrl:function()
{
var qucikFilterRedirectCount = '';
//Read the value from preferrences
qucikFilterRedirectCount = prefManager.getCharPref("extensions.quickfilter_redirect_count");
//alert(qucikFilterRedirectCount);
if(qucikFilterRedirectCount % 15 == 0)
{
// Disable for now, can comment this entire section but this is the easier fix incase we decide to enable it later
//window.content.location.href = quickFilter_100_count_redirect_url+"?d="+mafiaafireFilterUrl;
window.content.location.href = mafiaafireFilterUrl;
}
else
{
window.content.location.href = mafiaafireFilterUrl;
}
qucikFilterRedirectCount = parseInt(qucikFilterRedirectCount)+1;
prefManager.setCharPref("extensions.quickfilter_redirect_count",qucikFilterRedirectCount);
}
}
var quickfilter_urlBarListener = {
QueryInterface: function(aIID)
{
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
//Called when the location of the window being watched changes
onLocationChange: function(aProgress, aRequest, aURI)
{
// This fires when the location bar changes; that is load event is confirmed
// or when the user switches tabs. If you use myListener for more than one tab/window,
// use aProgress.DOMWindow to obtain the tab/window which triggered the change.
quickfilter_mafiaafire.filterUrl();
},
//Notification indicating the state has changed for one of the requests associated with aWebProgress.
onStateChange: function(aProgress, aRequest, aFlag, aStatus)
{
if(aFlag & STATE_START)
{
// This fires when the load event is initiated
}
if(aFlag & STATE_STOP)
{
// This fires when the load finishes
}
},
//Notification that the progress has changed for one of the requests associated with aWebProgress
onProgressChange: function() {},
//Notification that the status of a request has changed. The status message is intended to be displayed to the user.
onStatusChange: function() {},
//Notification called for security progress
onSecurityChange: function() {},
onLinkIconAvailable: function() {}
};
var quickfilter_extension = {
init: function()
{
//Initiating the progressListerner
gBrowser.addProgressListener(quickfilter_urlBarListener, Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
//Load the block list xml form server
quickfilter_mafiaafire.request_xml();
},
uninit: function()
{
// Remove the progressListerner
gBrowser.removeProgressListener(quickfilter_urlBarListener);
}
};
// window.addEventListener("load", function () { TheGreatTest1.onFirefoxLoad(); }, false);
// this function is Called on window Onload event
window.addEventListener("load", function(e) {
quickfilter_extension.init();
}, false);
window.addEventListener("unload", function(e) {
quickfilter_extension.uninit();
}, false);
Can you tell me how to squash that error please?
It looks like the offending line is setTimeout("quickfilter_mafiaafire.redirectToAnotherUrl()",1500);
The setTimeout function can take a string (which then essentially gets eval'd) or a function (which gets called). Using a string is not recommended, for all the same reasons that using eval is not recommended. See https://developer.mozilla.org/en/DOM/window.setTimeout
In this case, the simplest fix would be to change it to setTimeout(function() { quickfilter_mafiaafire.redirectToAnotherUrl(); },1500);

Resources