Have a listbox populate with every folder in myDrive - google-apps-script

I am trying to define where my file is to be saved to. I am starting out at the end user and moving inwards, so that means the start of the UI. I have the Label and the listbox but I am having troubles populating the listbox with the folders and sub folders that are in my Google Drive. The end product will be: Click the drop down menu -> chose folder to save file into -> click "submit" and it saves it into the folder.
Here is what i have so far: (Don't mind all my notes. I have zero JS experience and no programming experience so i am learning from the code that i already have from the original template maker. This is all of the code if you need it: http://pastebin.com/rbvu5Pie )
//look here for code about the listbox to show folders
grid.setWidget(2, 0, app.createLabel('Folder:')); //makes the label "folder" next to the listbox
var list = app.createListBox(); //defines what to do when i say list
grid.setWidget(2, 1, list); //puts the listbox to the right of the label
var folder = DocsList.getAllFolders()[0]; //defines that when i say "folder" it is supposed to get all folders
for (var i = 0; i < folder.length; i++) {
list.addItem(folder[i].getName(),folder[i].getId())
}
//this is the end of the code for the listbox showing folders
Thanks for your help everyone, i really appreciate it!

Here is a test code that seems to work. I didn't test it thoroughly but it seems to do what it has to do...
I left the textBox with ID visible to make it easier to debug but you should set it invisible in the final code.
There are probably a few improvements to add but it gives the general idea...
function doGet(){
var app = UiApp.createApplication();
var curFN = app.createTextBox().setText('MyDrive/').setName('curFN').setId('curFN').setWidth('400');
var curFID = app.createTextBox().setText('x').setName('curFID').setId('curFID').setWidth('400');
var list = app.createListBox().setName('list').setId('list').addItem('please select a folder','x');
var grid = app.createGrid(3,2).setText(0,0,'Choose a folder in your drive').setWidget(0,1,curFN).setWidget(2,1,curFID).setWidget(1,1,list);
var folders = DocsList.getRootFolder().getFolders();
for (var i = 0; i < folders.length; i++) {
list.addItem(folders[i].getName(),folders[i].getId())
}
var handler = app.createServerHandler('folderSelect').addCallbackElement(grid);
list.addChangeHandler(handler);
app.add(grid);
return app;
}
function folderSelect(e){
var app = UiApp.getActiveApplication();
var currentFN = e.parameter.curFN;
var currentFID = e.parameter.list;
Logger.log(currentFID);
var list = app.getElementById('list');
var curFN = app.getElementById('curFN');
var curFID = app.getElementById('curFID');
if(currentFID=='x'){currentFID=DocsList.getRootFolder().getId() ; curFN.setText('MyDrive/')};
var startFolder = DocsList.getFolderById(currentFID);
var folders = startFolder.getFolders();
list.clear().addItem('no other subFolder','x').addItem('Go back to Root','x');
if(folders.length>0){list.clear(); list.addItem('please select a subFolder','x')};
for (var i = 0; i < folders.length; i++) {
list.addItem(folders[i].getName(),folders[i].getId())
}
curFN.setText(currentFN+DocsList.getFolderById(currentFID).getName()+'/');
if(currentFID==DocsList.getRootFolder().getId()){curFN.setText('MyDrive/')};
curFID.setText(currentFID);
return app;
}
App online here, needs authorization for Docslist Service

Related

Uploading Google Spreadsheet to a Google Site

I'd like to begin by stating that the end goal is to display our company directory (a list of our employees names/job title/extension#/office location/email), which is in a Google Sheet, on a page in one of our Google Sites.
I tried to use Google's embed function, and it works... but it is very clunky, does not have a "Sort" function, and it just looks weird.
I pulled a Google Apps Script from somewhere online like 3 months ago and it actually did pull in a way that made me happy:
(This is as it appears currently on the Google Sites page. So in this screenshot, the embedded Sheet is at the top. The Sheet when, imported via the script, is below. Yes, they are both on the same page. I'm in testing!)
This is the code I used (I THINK - I don't remember how I implemented it):
function myFunction() {
}
function onOpen(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(ScriptProperties.getProperty("page url") == null){
ss.addMenu("List page", [{name: "Create list", functionName: "create_list"},null,
{name: "Fetch list items", functionName: "fetch_items"}]);
}
else{
ss.addMenu("List page", [{name: "Push Items", functionName: "push_items"}]);
}
}
function create_list() {
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var parent_page = Browser.inputBox("URL of the parent page:");
var title = Browser.inputBox("Choose a name for your list page:");
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(parent_page).createListPage(title, title.split(' ').join(''), '', data[0]);
ScriptProperties.setProperty("page url", list.getUrl());
onOpen();
push_items();
}
function push_items(){
var done = false;
while(!done){
try{
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var list = SitesApp.getPageByUrl(ScriptProperties.getProperty("page url"));
var list_items = list.getListItems();
for(i in list_items){
list_items[i].deleteListItem();
}
for(var i = 1; i < data.length; i++){
var item = list.addListItem(data[i]);
}
done = true;
}
catch(e){
}
}
SpreadsheetApp.getActiveSpreadsheet().toast(ScriptProperties.getProperty("page url"), "List page updated", 10);
}
function fetch_items(){
var url = Browser.inputBox("URL of your list page:");
var col_number = Browser.inputBox("Number of columns in the list:");
var data = new Array();
var list_items = SitesApp.getPageByUrl(url).getListItems();
for(i in list_items){
var row = new Array();
for(j = 0; j < col_number; j++){
row.push(list_items[i].getValueByIndex(j));
}
data.push(row);
}
SpreadsheetApp.getActiveSheet().getRange(1, 1, data.length, data[0].length).setValues(data);
}
[I do not take credit for writing this!]
So I would like to ask (since this ceases to make much sense to me) is if this is viable code for a Google Apps Script, and if so, how do I implement it to output Sheet data similarly in the same type of format as in the screenshot?
Alternatively, is there a better way to display this Sheet data in Google Sheets?
A totally different alternative would be to use Romain Vialard's "awesome tables" gadget. It works... awesome, and it is really easy to use. Besides, it admits filters, ...

An alternative way to pass data (eg, array values) through handler without using ScriptProperties

I built an app in which I use ScriptProperties to store data from a handler to its but(e) function. This was working nice, until other people started using the same spreadsheet at the same time. So often happens that one person is taking a time thinking about what item choose from a checkbox menu and another person uses the same function, changing the data stored at scriptProperties and affecting the use of the function by the first person.
What is the best way to fix it, using an alternative way to pass information through the handler?
Here one sample of one of theese functions (in which I'm using ScriptProperties to pass the values ofletterSpreadsheetId and recipientArray):
function letter(letterSpreadsheetId){
ScriptProperties.setProperty('letterSpreadsheetId', letterSpreadsheetId); // different people may have different letterSpreadsheetId;
ScriptProperties.setProperty('letter', 1); // to be used in another function
var activeSheet = ss.getActiveSheet();
var app = UiApp.createApplication().setHeight(400).setWidth(600);
var panel = app.createVerticalPanel(); // you can embed that in a form panel
var label = app.createLabel("Choose a receiver").setStyleAttribute("fontSize", 18);
app.add(label);
var sheet = SpreadsheetApp.openById(letterSpreadsheetId).getSheetByName("receivers");
var recipientArray = sheet.getRange(2, 1, sheet.getLastRow(), sheet.getLastColumn()).getValues();
var item3Panel = app.createHorizontalPanel();
item3Panel.add(app.createLabel("receiver"));
var listBox = app.createListBox().setName('item3');
for(var i = 0; i < (recipientArray.length); i++){
listBox.addItem(recipientArray[i][1]);
}
item3Panel.add(listBox);
var recipientArrayStr = JSON.stringify(recipientArray);
ScriptProperties.setProperty('recipientArr', recipientArrayStr);
var handlerBut = app.createServerHandler("butAnswerLetter").addCallbackElement(panel);
var but = app.createButton("submit").setId("submitButton2").addClickHandler(handlerBut);
panel.add(item1Panel)
.add(item2Panel)
.add(item3Panel)
.add(but)
.add(app.createLabel().setId("answer"));
var scroll = app.createScrollPanel().setPixelSize(600, 400).setTitle("My title 1");
scroll.add(panel);
app.add(scroll);
ss.show(app);
}
function butAnswerLetter(e){
var letterSpreadsheetId = ScriptProperties.getProperty('letterSpreadsheetId');
var recipient = e.parameter.item3;
ScriptProperties.setProperty('recipient', recipient);
var recipientArrayRecovery = ScriptProperties.getProperty('recipientArr');
var recipientArray = JSON.parse(recipientArrayRecovery);
for(var i=0;i<recipientArray.length;i++){
if(recipient == recipientArray[i][1]){
var usedRecipientArray = recipientArray[i];
}
}
You have 2 possibilities (that I know), either use userProperties instead of script-Properties as these are associated with the user but it will require the user to login and authorize, or - and this will work in every case even if the app is accessed anonymously, use the tags that you can write on almost any widget.
the syntax is quite simple, here is a small code example :
function doGet(){
var app = UiApp.createApplication().setTitle('test_TAG');
var list = app.createListBox(true).setVisibleItemCount(5).setPixelSize(30,450).setName('list');
var handler = app.createServerHandler('show').addCallbackElement(list);
list.addChangeHandler(handler);
var data = [];
for(var n = 0;n<20;n++){
list.addItem(n+' ');
data.push('available value = '+Number(n+1));
}
list.setTag(data.toString());
app.add(list);
return app
}
function show(e){
var app = UiApp.getActiveApplication();
var data = e.parameter.list_tag.split()
var selected = e.parameter.list;
app.add(app.createTextBox().setText(selected).setPixelSize(200,20));
app.add(app.createTextArea().setText(data.join()).setPixelSize(200,300));
return app;
}
testable here
Edit
following Zig's pertinent comment :
I forgot to mention the hidden widget (or a textBox / area set to invisible, useful for debugging when you want to check what it contains ! ) that is also useable of course...
The comment about a user having multiple windows showing the same app is also worth mentioning !
All in all you have 3 possibilities after all !
(thanks to Zig Mandel)

How to make a listBox trigger a handler when there is only one item?

Here is a script that demonstrate the issue I have (test online here)
var lbArray1 = ['item1', 'item2', 'item3', 'item4', 'item5'];
function doGet(e){
var app = UiApp.createApplication();
var vPanel = app.createVerticalPanel().setStyleAttribute('padding','20px')
app.add(vPanel);
var lb1 = app.createListBox().setName('lb1').setId('lb1').setVisibleItemCount(1);
var lb2 = app.createListBox().setName('lb2').setId('lb2').setVisibleItemCount(4);
var lb3 = app.createListBox().setName('lb3').setId('lb3').setVisibleItemCount(1);
lb3.addItem(lbArray1[0]);
for(var i in lbArray1){
lb1.addItem(lbArray1[i]);
lb2.addItem(lbArray1[i]);
}
var msg = app.createLabel('waiting for trigger').setId('msg');
vPanel.add(lb1).add(lb2).add(lb3).add(msg);
var Handler = app.createServerHandler('test').addCallbackElement(vPanel);
lb1.addChangeHandler(Handler);
lb2.addClickHandler(Handler);
lb3.addClickHandler(Handler);// I tried different trigger modes without success
return app;
}
function test(e){
var app = UiApp.getActiveApplication();
var msg = app.getElementById('msg');
msg.setText('triggered by '+e.parameter.source)
return app;
}
ListBox 3 has only one item and shows only one item (I have this situation in an app that uses a popup to show folders content while adapting the list size to its content, sometimes I can have just 1 file in a folder)
ListBox 3 never triggers the handler unless I change the setVisibleItemCount to 2 or more...
the code I use to adapt the list size goes simply like that :
...
ODlist.setVisibleItemCount(numItem > 6 ? 6 : numItem > 1 ? numItem : 2)
...
and I really would prefer to set the last number to 1, it would be looking so nice ;-) but I can't .
Any workaround idea ?
EDIT : for now I found that using lb3.addMouseOverHandler(Handler); is a useable solution but this handler causes some issues when more than one item is visible... I'd rather find something more elegant.
I understand if this isn't want you want, but it was my solution to the problem. I just simply added a list item that was a user prompt like: "Select a Spreadsheet". From my code:
var files = DocsList.getFolder("Incoming Product Data").getFiles();
fileChooser.addItem("Select a Spreadsheet");
for (var i = 0; i < files.length; i++) {
fileChooser.addItem(files[i].getId());
The only other thing I had to do was then build an if statement into the trigger, so that if someone select a file, but then switches back to the prompt "Select a Spreadsheet" that it doesn't throw an error by attempting to handle the prompt as an actual fileId.
Best I could do.

Google Apps Scrollpanel not updating with information

My script is iterating through some spreadsheet information. I read the documentation and the scrollpanel is only allowed one child. I therefore wrapped the horizontal panel information inside a scrollpanel but for some reason the scrollpanel never shows up with contents just the colored background. Any ideas why this may be?
var myscrollpanel = app.createScrollPanel().setPixelSize(100, 100);
myscrollpanel.setWidth("100%");
myscrollpanel.setStyleAttribute("background", "silver");
var vpanel = app.createVerticalPanel();
for (i=1; i <= mylastrow;++i)
{
var cellsname= mydatarange.getCell(i,1).getValue().toLowerCase();
// Browser.msgBox(cellsname );
// Browser.msgBox(searchstr.toString());
if (cellsname.toString() == searchstr.toString())
{
var panelrow = app.createHorizontalPanel();
var ddate = app.createTextBox();
var sbehavior = app.createTextBox();
var sconsequence = app.createTextBox();
var scomment = app.createLabel();
var steacher = app.createTextBox();
ddate.setText(mydatarange.getCell(i,5).getValue());
sbehavior.setText(mydatarange.getCell(i,8).getValue());
sconsequence.setText(mydatarange.getCell(i,6).getValue());
scomment.setText(mydatarange.getCell(i,10).getValue());
steacher.setText(mydatarange.getCell(i,7).getValue());
panelrow.add(ddate);
panelrow.add(sbehavior);
panelrow.add(sconsequence);
panelrow.add(steacher);
panelrow.add(scomment);
vpanel.add(panelrow);
app.add(panelrow);
cnt = ++cnt;
}
}
// Browser.msgBox(cnt);
myscrollpanel.add(vpanel);
app.add(myscrollpanel);
// return myscrollpanel;
return app ;// update UI
there are a few "anomalies" in your code that could cause some issues, I suggest a couple of changes (see code below).
Another point is that it is not a good idea to read a spreadsheet in a loop, it is mush more efficient to read the whole range in one time and then use the array values to iterate.
Here is a code proposal, I didn't test it (and it will probably need some debugging) but put a few comments to explain the changes.
var myscrollpanel = app.createScrollPanel()//.setPixelSize(100, 100);// 100 pixel is quite small to put all the items you add !!
myscrollpanel.setWidth("100%");//
myscrollpanel.setStyleAttribute("background", "silver");// this will not affect the widgets you add to the panel...
var vpanel = app.createVerticalPanel();
var data = mydatarange.getValues();// get all data in an array
for (i=0; i <data.length;++i) // and work directly with array values (indexed to 0 instead of cells indexed to 1)
{
var cellsname= data[i][0].toString().toLowerCase();
if (cellsname == searchstr.toString()){
var panelrow = app.createHorizontalPanel();
var ddate = app.createTextBox();
var sbehavior = app.createTextBox();
var sconsequence = app.createTextBox();
var scomment = app.createLabel();
var steacher = app.createTextBox();
ddate.setText(data[i][4].getValue());
sbehavior.setText(data[i][7].getValue());
sconsequence.setText(data[i][5].getValue());
scomment.setText(data[i][9].getValue());
steacher.setText(data[i][6].getValue());
panelrow.add(ddate);
panelrow.add(sbehavior);
panelrow.add(sconsequence);
panelrow.add(steacher);
panelrow.add(scomment);
vpanel.add(panelrow);// add only to the panel, not to the app or the row will be outside the panel
cnt = ++cnt;
}
}
myscrollpanel.add(vpanel);
app.add(myscrollpanel);
return app ;// update UI
}

trouble using e.parameter in a script

I have a script that is published as a service for a web app and i'm trying to build search functionality in it. the scipt is intended to allow the user to enter a term in a text box called searchBox, click search and show the row(s) of a spreadsheet containing that term.
function searchClickHandler(e) {
var app = UiApp.getActiveApplication();
var searchResultPanel = app.getElementById('searchResultPanel');
var searchResultPane = app.getElementById('searchResultPane');
var searchCloseButton = app.getElementById('searchCloseButton');
var searchTerm = e.parameter.searchBox;
if (searchTerm.toString().length == 6) {
var searchList = ArrayLib.filterByText(dataValues, 1, searchTerm.toString());
var searchTrim = new Array();
for (var i = 0; i < searchList.length; i++) {
var searchTrim1 = searchList[i].slice(1, 6);
var searchTrim2 = searchList[i].slice(8, 10);
var searchTrim3 = searchList[i].slice(17, 19);
searchTrim.push(searchTrim1.concat(searchTrim2,searchTrim3));
}
}
there are a few other else if's below that and then the handler should show the results but e.parameter.searchBox is coming back undefined. if i manually set searchTerm the script runs fine.
i am using e.parameter successfully in another handler in the same script so i am at a loss on this one.
thanks in advance.
The code you show is not really relevant to help... did you give a name to the "searchBox" when you created it or is it its ID ?
The e.parameter uses the name of the widget as reference.
EDIT (following comments): in the GUI builder try to add every callbackElement by their names (for widgets or ID for panels) separated by commas like this :