Run Excel VBA sub from javascript in a secure environment - javascript

I am attempting to do some automation of excel taks using javascipt. These workbooks already have VBA modules, so I will need to be able to run them from javascript. My challenge is that when I open a workbook using Javascript I do not get prompted to enable macros. I am not able to change my trust center settings in my work environment and they are currently set to "Disable all macros with notification," which asks the user to enable macros via a pop-up box if the file is opened manually.
How do I allow the user to enable macros when opening a workbook from javascript?
Error message and code below:
SCRIPT1004: Cannot run the macro 'C:\Users...\JPO Mdl
v07.04.022-FY16MM.01.01_A.xlsm!update_links()'. The macro may not be
available in this workbook or all macros may be disabled.
function open_files(A, B, C)
{
var excel = new ActiveXObject("Excel.Application");
excel.Visible=true;
excel.DisplayAlerts = true;
var wbA = excel.Workbooks.Open(document.getElementById(A).value);
var wbB = excel.Workbooks.Open(document.getElementById(B).value);
var wbC = excel.Workbooks.Open(document.getElementById(C).value);
excel.EnableEvents = false
excel.ScreenUpdating = false
excel.Calculation = -4135 //xlCalculationManual enumeration
var wb_collection = [wbA, wbB, wbC];
excel.Application.Run(wbA.fullname + '!update_links()');
}

Related

How to use Chrome profile in Selenium (selenium-webdriver) JavaScript

Since there doesn't seem to be a way to use an existing Chrome window, how do I use the Google account (and all settings and passwords) of the user in the window that Selenium opens? Selenium seems to open windows for itself, but without a Google account, which is an essential part to my program.
My program is very time sensitive, so it needs to be logged in to the websites it accesses automatically, and the program is going to be used by multiple users.
var webdriver = require("selenium-webdriver");
var chrome = require("selenium-webdriver/chrome");
var options = new chrome.Options();
options.addArguments("user-data-dir=C:\\Users\robert.car\\AppData\\Local\\Google\\Chrome\\User Data")
options.addArguments("profile-directory=Profile 1")
var driver = let driver = new webdriver.Builder()
.forBrowser('chrome')
.setChromeOptions(options)
.build();
user-data-dir considers profile as default , and you don't have to specify that . If its something else specify it through profile-directory argument
Step to create a profile:
open : chrome://version in address bar
copy the user dir folder completely to eg c:\tmp\newdir
open the copied user data (newdir) and search for folder called Default . This is the profile folder.
rename the Default folder as "Profile 1"
Now to use this :
options.addArguments("user-data-dir=c:\\tmp\\newdir")
options.addArguments("profile-directory=Profile 1")

Writing to Excel spreadsheet with ASP.Net site using the Excel Javascript API

I have created an ASP.Net webapp using the Empty Template in Visual Studio 2017. The website has many familiar Web Controls such as Button (s), ImageButton, and Label (s). Users open a picture inside the ImageButton control and are able to click inside the control. The webapp calculates a value depending on where the user clicks in the ImageButton, and the values are displayed in the corresponding Label controls. The user is meant to write the values into an open Excel Spreadsheet (this is where the issue lies). For additional context, every action taken by the user is handled by a client-side javascript function-- with the exception of opening the picture. The opening action is handled by C# code belonging to the aspx page.
In the process of writing a similar Excel Web Add In, I found some very helpful code for writing to an Excel Spreadsheet using the Excel JavaScript API.
Here is that very code:
function HighlightCell() {
Excel.run(function (ctx) {
// Create a proxy object for the selected range and load its properties
var sourceRange = ctx.workbook.getSelectedRange().load("values, rowCount, columnCount");
var sheet = ctx.workbook.worksheets.getActiveWorksheet()
// Run the queued-up command, and return a promise to indicate task completion
return ctx.sync()
.then(function () {
var highestRow = 0;
var highestCol = 0;
var highestValue = sourceRange.values[0][0];
// Find the cell to highlight
for (var i = 0; i < sourceRange.rowCount; i++) {
for (var j = 0; j < sourceRange.columnCount; j++) {
if (!isNaN(sourceRange.values[i][j]) && sourceRange.values[i][j] > highestValue) {
highestRow = i;
highestCol = j;
highestValue = sourceRange.values[i][j];
}
}
}
cellToHighlight = sourceRange.getCell(highestRow, highestCol);
cellToHighlight.format.fill.color = "IndianRed";
cellToHighlight.values = 5;
})
.then(ctx.sync);
})
.catch(errorHandler);
}
The code works like a charm within the Excel Web Add In, but it hasn't worked so far within the ASP.Net webapp. From my understanding, it is because the code hasn't been able to retrieve the active workbook / worksheet. This could be because of the disconnect between the server and the client-side from what I know.
Is there any way to open an excel spreadsheet on the client-side with javascript or C#. Can I even use the above code in an ASP.Net webapp?
EDIT: more code
I opened the spreadsheet on the client side with this code:
function excload() {
var selectedFile = document.getElementById('imgupload').files[0];
document.getElementById("frame").src = window.URL.createObjectURL(selectedFile);
}
In this code, imgupload is an HTML file input and "frame" is an iframe element. I'm not sure why when I run it, instead of just opening the spreadsheet in the iframe, it opens the spreadsheet in a new instance of the Excel program on the computer.
Noticed something weird in writing code:
Calling the function HighlightCell--which writes to the cell refreshes the page, while none of the other javascript functions do. This happens even if I add a return false; line to the function and call it from button with _onclick ="HighlightCell(); return false;"
REDUX of "Noticed something weird in writing code":
Managed to call Highlight cell without refresh by using:
$(document).ready(function () {
$('#chosen').click(HighlightCell);
});
But still no writing takes place

Call Javascript from VBA on Excel on a Mac

I want to create a VBA macro on excel which at a click of button would open a browser (chrome or safari) login to a website, extract the desired float value, then populate a given cell in the sheet with that value.
There are examples online on how to achieve this using internet explorer but this is not available as on a mac. I have also seen guides using Selenium but this doesn't appear to work on mac.
The javascript itself is along the lines of (after opening a browser at a certain website):
document.getElementById("username").value = "username"
document.getElementById("password").value = "password"
document.getElementsByClassName("button")[0].click()
value = parseFloat(document.getElementsByClassName("value")[1].innerText.slice(1))
I've solved this by using a combination of python-selenium and xlwings. My VBA calls RunPython ("import python_script; python_script.fun()")
python_script.py
import xlwings as xw
from selenium import webdriver
def fun():
# Creates a reference to the calling Excel file
wb = xw.Book.caller()
# opens chrome
chrome_driver_path = '/usr/local/bin/chromedriver'
driver = webdriver.Chrome(chrome_driver_path)
# open website and login
driver.get('url')
driver.find_element_by_id('username').send_keys('username')
driver.find_element_by_id('password').send_keys('password')
driver.find_element_by_name('buttonId').click()
# finds member price sum
table_body = driver.find_element_by_xpath("//*[#class='classname']").text
price = float(table_body.split()[2][1:])
# closes chrome
driver.quit()
# changes cell value
sheet = wb.sheets['sheetname']
sheet.range('cell').value = price

Google Apps Script to Open copy of Google Sheet in new window

I am trying to boost my Google knowledge by getting into scripting, and I have researched high and low for a solution to my situation to no avail.
I have a Google Sheet that is serving as a template. When the original file is opened, I want to launch a dialog to enter a student ID#. This number will pass into a specified cell in the template, which results in some auto-populated cells in the sheet (using spreadsheet functions). Once the cells have been populated, I want to make a copy of the file with a specific naming structure and store it in a specified folder in Google Drive. Lastly, I want that new file to open in a new window for further editing.
So far, I can get the dialog box to pop up for the ID#, I can pass that number to the sheet, and I can make a copy of the template and rename it accordingly.
What I CANNOT do is get the new file to open automatically in a new tab. I also need the new file to not run the "onOpen" script.
Here is what I have so far for each of these endeavors.
The code below creates a dialog box to enter a Student ID and passes it to the sheet to a specified cell:
function BuildUI() {
var app = UiApp.createApplication();
app.setTitle('Make a Copy - Please enter the Student ID# below:');
var panel = app.createVerticalPanel();
var textBox = app.createTextBox();
textBox.setName('stuId').setId('StudentID');
var button = app.createButton('Submit');
panel.add(textBox);
panel.add(button);
var clickHandler = app.createServerClickHandler('responses');
button.addClickHandler(clickHandler);
clickHandler.addCallbackElement(panel);
app.add(panel);
var doc = SpreadsheetApp.getActive();
doc.show(app);
}
function responses(e){
var app = UiApp.getActiveApplication();
var textBoxValue = e.parameter.stuId;
var sheet = SpreadsheetApp.getActiveSheet();
var studentID = sheet.getRange('Y4').setValue(textBoxValue);
var dateToday = sheet.getRange('C5').setValue(new Date());
return app.close();
}
The above code works for what I need it to do. I only include it for reference (and suggestions for cleaning it up ;)
This next code block makes a copy of the file with the new values, renames it, and saves it to a designated folder in Google Drive. Again..this works for me, but I include it for reference and suggestions:
function makeCopy(){
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var firstName = sheet.getRange('O4').getDisplayValue();
var lastName = sheet.getRange('E4').getDisplayValue();
var date = sheet.getRange('C5').getDisplayValue();
var stuId = sheet.getRange('Y4');
var grade = sheet.getRange('AH4').getDisplayValue();
var fileName = (lastName+ ", " +firstName+ " - " +stuId+ " - Grade " +grade+ " - " +date);
var destFolder = DriveApp.getFolderById('folderID');
destFolder.setSharing(DriveApp.Access.DOMAIN_WITH_LINK, DriveApp.Permission.VIEW);
DriveApp.getFileById(sheet.getId()).makeCopy(fileName, destFolder);
}
Lastly, my onOpen function strings this all together (plus one other function) to run as soon as the template is opened:
function onOpen(){
clearCells(); //Clear all content left behind by last editor
BuildUI();
makeCopy();
}
Any help in getting this workflow dialed in is greatly appreciated (most of this I have accomplished already, I just include it for you understanding of my overall needs):
User Interface Dialog Box to capture Student ID (done, but could use suggestions for cleaning up)
Pass ID to cell Y4 and current date to cell C5 (done...just included for understanding of overall need)
Make a copy of the original file and rename it to "Last Name, First Name - StuID - Grade - Date and store in specified Folder in Google Drive (done...just included for understanding of overall need)
NEED -- Open newly created file in a new tab in the browser without running the attached scripts (ideally, the scripts will not be included in the copy if possible)
Bonus points for any help in recreating the UI using the HtmlService in Google scripts since the UI Service is deprecated and functionality may go away the moment I figure all this out.
Thanks in advance for any help on this tall order.
You could capture the google file object in the makeCopy() with
gFile = DriveApp.getFileById(sheet.getId()).makeCopy(fileName, destFolder);
return gFile;
then add this function to open a new sheet:
function openNewSheet(gFile) {
var spreadsheetId = gFile.getId();
var url = "https://docs.google.com/spreadsheets/d/"+spreadsheetId;
//SpreadsheetApp.getUi().alert('url is ' + url);
var html = "<script>window.open('" + url + "');google.script.host.close();</script>";
var userInterface = HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModalDialog(userInterface, "Open Sheet");
}
Finally you can add openNewSheet() to the end of onOpen()
Some credit goes to this video: https://www.youtube.com/watch?v=2y7Y5hwmPc4

How to read and write to a file (Javascript) in ui automation?

I want to identify few properties during my run and form a json object which I would like to write to a ".json"file and save it on the disk.
var target = UIATarget.localTarget();
var properties = new Object();
var jsonObjectToRecord = {"properties":properties}
jsonObjectToRecord.properties.name = "My App"
UIALogger.logMessage("Pretty Print TEST Log"+jsonObjectToRecord.properties.name);
var str = JSON.stringify(jsonObjectToRecord)
UIALogger.logMessage(str);
// -- CODE TO WRITE THIS JSON TO A FILE AND SAVE ON THE DISK --
I tried :
// Sample code to see if it is possible to write data
// onto some file from my automation script
function WriteToFile()
{
set fso = CreateObject("Scripting.FileSystemObject");
set s = fso.CreateTextFile("/Volumes/DEV/test.txt", True);
s.writeline("HI");
s.writeline("Bye");
s.writeline("-----------------------------");
s.Close();
}
AND
function WriteFile()
{
// Create an instance of StreamWriter to write text to a file.
sw = new StreamWriter("TestFile.txt");
// Add some text to the file.
sw.Write("This is the ");
sw.WriteLine("header for the file.");
sw.WriteLine("-------------------");
// Arbitrary objects can also be written to the file.
sw.Write("The date is: ");
sw.WriteLine(DateTime.Now);
sw.Close();
}
But still unable to read and write data to file from ui automation instruments
Possible Workaround ??
To redirect to the stdout if we can execute a terminal command from my ui automation script. So can we execute a terminal command from the script ?
Haven't Tried :
1. Assuming we can include the library that have those methods and give it a try .
Your assumptions are good, But the XCode UI Automation script is not a full JavaScript.
I don't think you can simply program a normal browser based JavaScript in the XCode UI Automation script.
set fso = CreateObject("Scripting.FileSystemObject");
Is not a JavaScript, it is VBScript which will only work in Microsoft Platforms and testing tools like QTP.
Scripting.FileSystemObject
Is an ActiveX object which only exists in Microsoft Windows
Only few JavaScript functions like basic Math, Array,...etc..Are provided by the Apple JavaScript library, so you are limited to use only the classes provided here https://developer.apple.com/library/ios/documentation/DeveloperTools/Reference/UIAutomationRef/
If you want to do more scripting then Try Selenium IOS Driver http://ios-driver.github.io/ios-driver/
Hey so this is something that I was looking into for a project but never fully got around to implementing so this answer will be more of a guide of what to do than step by step copy and paste.
First you're going to need to create a bash script that writes to a file. This can be as simple as
!/bin/bash
echo $1 >> ${filename.json}
Then you call this from inside your Xcode Instruments UIAutomation tool with
var target = UIATarget.localTarget();
var host = target.host();
var result = host.performTaskWithPathArgumentsTimeout("your/script/path", ["Object description in JSON format"], 5);
Then after your automation ends you can load up the file path on your computer to look at the results.
EDIT: This will enable to write to a file line by line but the actual JSON formatting will be up to you. Looking at some examples I don't think it would be difficult to implement but obviously you'll need to give it some thought at first.

Categories

Resources