I'm using the 1.3 requirement set for setting/getting the body value of an appointment/meeting: Office.context.mailbox.item.body.getAsync(...) and Office.context.mailbox.item.body.setAsync(...)
This works just fine literally everywhere and for all account types(Exchange on-premise and Outlook/Office365 accounts) except Outlook 2016 for MAC, where it fails to apply the html/text value of the body.
Checking the requirement sets (https://dev.office.com/reference/add-ins/outlook/tutorial-api-requirement-sets) MAC Outlook is listed as supporting all the requirement set versions(1.1 to 1.5) so it should support also the getAsync and setAsync methods for body property.
Any idea why it does not work?
UPDATE:
For setting value:
function applyBody() {
const $dBod = $.Deferred();
try {
Office.context.mailbox.item.body.setAsync(_appointmentInfo.body.value, { coercionType: _appointmentInfo.body.type }, function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
$dBod.resolve();
}
else {
$dBod.reject(translate.getTranslation('ERROR_SETTING_BODY'));
}
});
}
catch (e) {
$dBod.reject(e);
}
return $dBod.promise();
}
For getting value:
function getBody() {
const $dBod = $.Deferred();
getBodyType()
.done(function (bodyType) {
try {
Office.context.mailbox.item.body.getAsync(bodyType, function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
$dBod.resolve({ value: asyncResult.value, type: bodyType });
}
else {
$dBod.reject(translate.getTranslation('ERROR_GETTING_BODY_VALUE'));
}
});
}
catch (e) {
$dBod.reject(e);
}
})
.fail($dBod.reject);
return $dBod.promise();
}
For getting body type:
function getBodyType() {
const $dBod = $.Deferred();
try {
Office.context.mailbox.item.body.getTypeAsync(function (asyncResult) {
if (asyncResult.status === Office.AsyncResultStatus.Succeeded) {
$dBod.resolve(asyncResult.value);
}
else {
$dBod.reject(translate.getTranslation('ERROR_GETTING_BODY_TYPE'));
}
});
}
catch (e) {
$dBod.reject(e);
}
return $dBod.promise();
}
UPDATE2:
Method I use for concatenating initial body value with my html:
const filterNullsAndUndefined = function (array, char) {
return array.filter(function (val) { return val; }).join(char);
};
I call it like this filterNullsAndUndefined([intialBodyValue, myHtml],'');
Where in my case the initialBodyValue is the value I get using body.getAsync(...) before appending myHtml to it( in order to preserve any text the user inserted before myHtml template gets appended).
If I set myHtml directly on the body (overwrite) it works.
UPDATE 3:
Below the result we got when we appended the word 'Christmass':
appointment body as text
We are able to reproduce the issue. Adding a <b>Text</b> after the </html> works on Outlook 2016 desktop, but not on the Mac. We will be fixing this soon.
Related
I've used this approach, it should give me the Expected Output which I've mentioned below.
But, due to asynchronous execution, it is giving the Actual Output.
So, Please help me to solve the same.
See, I'm calling one async function, inside i'm running three nested map loops. And in the third loop I'm connecting the database and fetching values. Based on the value, I'm generating the values of the Object which is empty at the first. And once the function called, it is triggering the then method.That's where the problem arises, I need that then function to be executed after the called function executed completely. This is the complete problem statement
Given the following code:
let messageObject = {};
async function storeManager() {
// storing the defects or modifying
console.log('hii from storeManager()');
await Promise.all(
Object.keys(filledDefects).map((defectName) => {
Object.keys(filledDefects[defectName]).map((subDefectName) => {
Object.keys(filledDefects[defectName][subDefectName]).map(
async (zone) => {
const result = await dbConnectedPool.query(
`SELECT * FROM defect_table WHERE body_number=${enteredBodyNumber} AND category='${selectedCategory}' AND subcategory='${selectedSubCategory}' AND defect='${defectName}' AND subdefect='${subDefectName}' AND zone = ${zone.replace(
'_',
''
)}`
);
if (result.rows.length == 0) {
// block to save defects record for the first time
console.log(
`INSERT INTO defect_table (body_number,mode,category,subcategory,defect,subdefect,zone,defectCount,date,time,username) VALUES (${enteredBodyNumber},'${mode}','${selectedCategory}','${selectedSubCategory}','${defectName}','${subDefectName}',${zone.replace(
'_',
''
)},${
filledDefects[defectName][subDefectName][zone]
},'${date}','${time}','${username}');`
);
await dbConnectedPool.query(
`INSERT INTO defect_table (body_number,mode,category,subcategory,defect,subdefect,zone,defectCount,date,time,username) VALUES (${enteredBodyNumber},'${mode}','${selectedCategory}','${selectedSubCategory}','${defectName}','${subDefectName}',${zone.replace(
'_',
''
)},${
filledDefects[defectName][subDefectName][zone]
},'${date}','${time}','${username}');`
);
mod.set(
messageObject,
`Newly Saved Zone.${zone}.${defectName}.${subDefectName}`,
filledDefects[defectName][subDefectName][zone]
);
console.log('inside: ', messageObject);
} else {
// block to modify existing defect records
console.log(
`UPDATE defect_table SET defectCount=${
filledDefects[defectName][subDefectName][zone]
},date='${date}',time='${time}',username='${username}' WHERE body_number=${enteredBodyNumber} AND category='${selectedCategory}' AND subcategory='${selectedSubCategory}' AND defect='${defectName}' AND subdefect='${subDefectName}' AND zone=${zone.replace(
'_',
''
)}`
);
await dbConnectedPool.query(
`UPDATE defect_table SET defectCount=${
filledDefects[defectName][subDefectName][zone]
},date='${date}',time='${time}',username='${username}' WHERE body_number=${enteredBodyNumber} AND category='${selectedCategory}' AND subcategory='${selectedSubCategory}' AND defect='${defectName}' AND subdefect='${subDefectName}' AND zone=${zone.replace(
'_',
''
)}`
);
mod.set(
messageObject,
`Overwritten Zone.${zone}.${defectName}.${subDefectName}`,
filledDefects[defectName][subDefectName][zone]
);
console.log('inside: ', messageObject);
}
// checking whether already record exists with same aspects
}
);
});
})
);
console.log('bye from storeManager()');
}
storeManager().then(() => {
console.log('message outside:', messageObject);
});
Expected Output:
hii from storeManager()
bye from storeManager()
UPDATE defect_table SET defectCount=12,date='2022-10-12',time='12:52:33',username='Vasanth'
WHERE body_number=1234 AND category='LH SHELL BODY MAIN-LINE' AND subcategory='FENDER - LH
SBML' AND defect='Surface' AND subdefect='Dent' AND zone=210
inside: { 'Overwritten Zone': { _210: { Surface: [Object] } } }
UPDATE defect_table SET defectCount=12,date='2022-10-12',time='12:52:33',username='Vasanth'
WHERE body_number=1234 AND category='LH SHELL BODY MAIN-LINE' AND subcategory='FENDER - LH
SBML' AND defect='Surface' AND subdefect='Dent' AND zone=215
inside: {
'Overwritten Zone': { _210: { Surface: [Object] }, _215: { Surface: [Object] } }
}
message outside: {
'Overwritten Zone': { _210: { Surface: [Object] }, _215: { Surface: [Object] } }
}
Actuall Output:
hii from storeManager()
bye from storeManager()
message outside: {}
UPDATE defect_table SET defectCount=12,date='2022-10-12',time='12:52:33',username='Vasanth'
WHERE body_number=1234 AND category='LH SHELL BODY MAIN-LINE' AND subcategory='FENDER - LH
SBML' AND defect='Surface' AND subdefect='Dent' AND zone=210
inside: { 'Overwritten Zone': { _210: { Surface: [Object] } } }
UPDATE defect_table SET defectCount=12,date='2022-10-12',time='12:52:33',username='Vasanth'
WHERE body_number=1234 AND category='LH SHELL BODY MAIN-LINE' AND subcategory='FENDER - LH
SBML' AND defect='Surface' AND subdefect='Dent' AND zone=215
inside: {
'Overwritten Zone': { _210: { Surface: [Object] }, _215: { Surface: [Object] } }
}
You'll need to use Promise.all everywhere you are producing an array of promises, not just on the outermost call. And you'll need to make the map callbacks actually return those promises!
await Promise.all(Object.entries(filledDefects).map(async ([defectName, defect]) => {
await Promise.all(Object.entries(defect).map(async ([subDefectName, subDefect]) => {
await Promis.all(Object.entries(subDefect).map(async ([zoneName, zone]) => {
await …;
}));
}));
}));
Alternatively you can also write this without some of the async/await:
await Promise.all(Object.entries(filledDefects).map(([defectName, defect]) =>
Promise.all(Object.entries(defect).map(async ([subDefectName, subDefect]) =>
Promis.all(Object.entries(subDefect).map(async ([zoneName, zone]) => {
await …;
}));
));
));
Promise.all expects an array of promises as argument, but you pass it an array of undefined values instead. That means Promise.all will return a promise that is resolved immediately.
Two causes for this problem:
the outer map callbacks don't have a return statement (so they map each defectName and each subDefectName to undefined)
If you would return the result of the inner .map calls, then each defectName maps to an array (of arrays ...), which still isn't what you need. You don't want a nested array, but a flat array, so use return Object.keys().flatMap instead of Object.keys().map
I am trying to return a random number from the random-number-csprng API and it sends the value to the console, but not outside of the module. How can I compare the value from the module inside another module?
I have tried to return the number parameter from the .then() function but it still does not get outside of the function.
const Promise = require("bluebird");
const randInt = require("random-number-csprng");
class project {
constructor(uname) {
this.uname = uname;
}
randomNumber(lowest, highest)
{
Promise.try(() => {
return randInt(lowest, highest);
}).then(number => {
console.log("Your random number:", number);
}).catch({code: "RandomGenerationError"}, err => {
console.log("Something went wrong!");
});
}
checkRandom()
{
console.log(`This is a test: ${this.randomNumber(1,100)}`);
if(this.randomNumber(1, 100) > 1)
{
console.log(`Works!`);
}
else
{
console.log(`Does not work!`);
}
}
}
Output
This is a test: undefined
Your random number: 65
Your random number: 71
I expected the output to be 65 on the undefined log, but seems like it does not get stored outside of the Promise.try()
I see you followed the example code on their documentation a bit too literally. You need to return the promise from the method, and consume it asynchronously by awaiting it:
const randInt = require('random-number-csprng');
class Project {
constructor(uname) {
this.uname = uname;
}
randomNumber(lowest, highest) {
return randInt(lowest, highest);
}
async checkRandom() {
const randomValue = await this.randomNumber(1,100);
console.log(`This is a test: ${randomValue}`);
if (randomValue > 1) {
console.log('Works!');
} else {
console.log('Does not work!');
}
}
}
I am following the azure device twin tutorial https://learn.microsoft.com/en-us/azure/iot-hub/iot-hub-csharp-node-twin-how-to-configure
I can make this work to update an existing reported property. What I am not sure about is how can I add a new reported property.
Specifically the code looks snippet looks like:
var currentTelemetryConfig = twin.properties.reported.telemetryConfig;
currentTelemetryConfig.pendingConfig =
twin.properties.desired.telemetryConfig;
currentTelemetryConfig.status = "Pending";
telemetryConfig: currentTelemetryConfig
var patch = {
telemetryConfig: currentTelemetryConfig
};
twin.properties.reported.update(patch, function(err) {
if (err) {
console.log(err);
} else {
console.log('success');
}
}
I can easily understand how this works for the existing property update (in this case the telemetryConfig) but what would it look like if the change I was trying to make was to a entirely new property?
How would it work if I decide at some point the I want a a new desired property called "favourite_colour" : "blue"?
In the azure backend I can add this but how do I dynamically build the var patch variable?
I tried this but it returned an error:
twin.properties.reported.update(twin.properties.desired, function(err) {
if (err) {
console.log('Could not report properties');
} else {
console.log('Success');
}
});
This is what my twin looks like:
"properties": {
"desired": {
"active": true,
"pws": "xyz",
"$metadata": {
"$lastUpdated": "2018-03-27T18:21:57.010036Z",
"$lastUpdatedVersion": 5,
"active": {
"$lastUpdated": "2018-03-27T18:21:57.010036Z",
"$lastUpdatedVersion": 5
},
"pws": {
"$lastUpdated": "2018-03-27T18:21:57.010036Z",
"$lastUpdatedVersion": 5
}
},
"$version": 5
},
"reported": {
"telemetryConfig": 6,
"$metadata": {
"$lastUpdated": "2018-03-27T18:56:05.2445399Z",
"telemetryConfig": {
"$lastUpdated": "2018-03-27T18:56:05.2445399Z"
}
},
"$version": 5
}
}
}
I'm guessing you want to:
Add new reported property on device side (favourite_colour)
I first recommend you to read this sample in GitHub.
Main this sample shows different way to listen for updates in twin on different level. The top most, where any update in device twin will trigger an event; or specific property (favourite_color).
I've edited the sample from the Microsoft document you provided to work with favorite_color.
'use strict';
var Client = require('azure-iot-device').Client;
var Protocol = require('azure-iot-device-mqtt').Mqtt;
var connectionString = '{iot hub connection string}';
var client = Client.fromConnectionString(connectionString, Protocol);
var initConfigChange = function(twin, patch) {
twin.properties.reported.update(patch, function(err) {
if (err) {
console.log('Could not report properties');
} else {
console.log('Reported pending config change: ' + JSON.stringify(patch));
setTimeout(function() {completeConfigChange(twin, patch);}, 30000);
}
});
}
var completeConfigChange = function(twin, patch) {
if (patch.telemetryConfig) {
// Same as Sample
} else if (patch.favourite_colour) {
var currentfavourite_colour = twin.properties.reported.favourite_colour;
currentfavourite_colour.color = currentfavourite_colour.pendingConfig.color;
currentfavourite_colour.status = "Success";
delete currentfavourite_colour.pendingConfig;
var patch = {
favourite_colour: currentfavourite_colour
};
patch.favourite_colour.pendingConfig = null;
}
twin.properties.reported.update(patch, function(err) {
if (err) {
console.error('Error reporting properties: ' + err);
} else {
console.log('Reported completed config change: ' + JSON.stringify(patch));
}
});
};
client.open(function(err) {
if (err) {
console.error('could not open IotHub client');
} else {
client.getTwin(function(err, twin) {
if (err) {
console.error('could not get twin');
} else {
console.log('retrieved device twin');
twin.properties.reported.favourite_colour = {
color: "green"
}
twin.on('properties.desired', function(desiredChange) {
console.log("received change: "+JSON.stringify(desiredChange));
if (desiredChange.telemetryConfig) {
// Same as sample
} else if (desiredChange.favourite_colour) {
var currentfavourite_colour = twin.properties.reported.favourite_colour;
currentfavourite_colour.pendingConfig = twin.properties.desired.favourite_colour;
currentfavourite_colour.status = "Pending Color";
var patch = {
favourite_colour: currentfavourite_colour
};
initConfigChange(twin, patch);
}
});
}
});
}
});
What I did was use a if else statement to check the reported property; as I mentioned there are other ways to do this, check the GitHub code I provided earlier. Once I have the matched reported property I can update the in the same way as the existing sample.
I want to override the existing magento2 JS Component in my theme for some more customization.
Magento_Checkout/js/view/minicart.js
Above JS component i want to override and i want to add some more operation on the remove button event.
You can try "map" of require js. I used this and working for me. following is the requirejs-config.js inside my theme.
var config = {
map: {
'*': {
'Magento_Checkout/js/view/minicart':'js/custom/minicart'
}
}
};
Modified minicart.js file is placed inside "web/js/custom" folder inside my theme.
Just Go to your theme Override Magento_Checkout there, then under web folder make path as same as core module then add your js file & do required changes. It will reflect on frontend.
You can also extend an existing Magento JS without overwriting the whole file in your module add the require-config.js
app/code/MyVendor/MyModule/view/frontend/requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/view/minicart': {
'MyVendor_MyModule/js/minicart': true
}
}
}
};
Then add the minicart.js
app/code/MyVendor/MyModule/view/frontend/web/js/minicart.js
define([], function () {
'use strict';
return function (Component) {
return Component.extend({
/**
* #override
*/
initialize: function () {
var self = this;
return this._super();
},
MyCustomFunction: function () {
return "my function";
}
});
}
});
define(['jquery'],function ($) {
'use strict';
var mixin = {
/**
*
* #param {Column} elem
*/
initSidebar: function () {
var sidebarInitialized = false, miniCart;
miniCart = $('[data-block=\'minicart\']');
if (miniCart.data('mageSidebar')) {
miniCart.sidebar('update');
}
if (!$('[data-role=product-item]').length) {
return false;
}
miniCart.trigger('contentUpdated');
if (sidebarInitialized) {
return false;
}
sidebarInitialized = true;
miniCart.sidebar({
'targetElement': 'div.block.block-minicart',
'url': {
'checkout': window.checkout.checkoutUrl,
'update': window.checkout.updateItemQtyUrl,
'remove': window.checkout.removeItemUrl,
'loginUrl': window.checkout.customerLoginUrl,
'isRedirectRequired': window.checkout.isRedirectRequired
},
'button': {
'checkout': '#top-cart-btn-checkout',
'remove': '#mini-cart a.action.delete',
'increacseqty':'#mini-cart a.action.increase-qty',
'decreaseqty':'#mini-cart a.action.decrease-qty',
'close': '#btn-minicart-close'
},
'showcart': {
'parent': 'span.counter',
'qty': 'span.counter-number',
'label': 'span.counter-label'
},
'minicart': {
'list': '#mini-cart',
'content': '#minicart-content-wrapper',
'qty': 'div.items-total',
'subtotal': 'div.subtotal span.price',
'maxItemsVisible': window.checkout.minicartMaxItemsVisible
},
'item': {
'qty': ':input.cart-item-qty',
'button': ':button.update-cart-item'
},
'confirmMessage': $.mage.__('Are you sure you would like to remove this item from the shopping cart??')
});
return this._super();
}
};
return function (minicart) { // target == Result that Magento_Ui/.../columns returns.
return minicart.extend(mixin); // new result that all other modules receive
};
});
Presently I have set time interval in such a way that every 1 seconds,a function is executed.The problem is that,i am displaying notification through this function.There are notification buttons in notification.When I click on the notification action button,mulitple windows are being open.I found out that it is because I have set Timer.But in my extension , timer is necessary in order to check the output of a server file everytime.Anyone please help me.Is there any other way to deal this problem
Here is my background.js
var myNotificationID = null;
var oldChromeVersion = !chrome.runtime;
setInterval(function() {
updateIcon();
}, 1000);
function onInit() {
updateIcon();
if (!oldChromeVersion) {
chrome.alarms.create('watchdog',{periodInMinutes:5,delayInMinutes: 0});
}
}
function onAlarm(alarm) {
if (alarm && alarm.name == 'watchdog') {
onWatchdog();
}
else {
updateIcon();
}
}
function onWatchdog() {
chrome.alarms.get('refresh', function(alarm) {
if (alarm) {
console.log('Refresh alarm exists. Yay.');
}
else {
updateIcon();
}
});
}
if (oldChromeVersion) {
updateIcon();
onInit();
}
else {
chrome.runtime.onInstalled.addListener(onInit);
chrome.alarms.onAlarm.addListener(onAlarm);
}
function updateIcon(){
if(//something)
//something
else{
chrome.notifications.create(
'id1',{
type: 'basic',
iconUrl: '/calpine_not_logged_in.png',
title: 'Warning : Attendance',
message: 'Please mark your Attendance !',
buttons: [{ title: 'Mark',
iconUrl: '/tick.jpg'
},{ title: 'Ignore',
iconUrl: '/cross.jpg'}],
priority: 0},
function(id) { myNotificationID = id;}
);
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
chrome.notifications.onClosed.addListener(function() {
notification.close();
});
}
} }
});
}
}
onInit();
Here i found out that,when i remove the delayInminutes and laso the set timeineterval it opens only one tab,as i want.But both of them are necessary to do continuous checking of a server file.because whole my operation is based on the server file output.Is there any way to cope with this problem.Is there any way to set time interval for only that function?
The problem has nothing to do with what you "suspect". The problem is that you add a listener for chrome.notifications.onButtonClicked events inside the updateIcon() function. So this is what happens:
Every second you execute updateIcon().
Inside updateIcon() you set a new listener that listens for notification-buttons being clicked.
So, after 1 second there will be 1 listener, after 2 seconds there will be 2 listeners, after n seconds there will be n listeners.
When you click the button, each listener will catch the onButtonClicked event and open a new window. (So there will be so many windows as many seconds have elapsed since you loaded your extension.
How to fix this:
You need to create the listener only once (and not every second). To remove the following piece of code from inside the updateIcon() function:
chrome.notifications.onButtonClicked.addListener(function(notifId, btnIdx) {
if (notifId === myNotificationID) {
if (btnIdx === 0) {
window.open("http://www.calpinemate.com/");
} else if (btnIdx === 1) {
notification.close();
}
}
});
And place it at the end of your background-page (just before onInit();). Make sure you don't place it inside any function.