I've created a new google app through their app maker interface. I've added "Google Admin Directory API" as a service and "Directory" as a data source. I created a page that will hold 2 tables, one that list all users within my domain (this is already working) and another table that lists all of the groups within my domain (not working). How can I achieve this? Can this be done through their widgets or do I have to create a script and programmatically call the admin API to then bind the data to the table?
Since you already enabled the Admin Directory API when using the directory model, all you have to do now is to call the sample code from the server script. In a server script, add the sample code:
function listAllGroups() {
var pageToken;
var page;
do {
page = AdminDirectory.Groups.list({
domain: 'example.com',
maxResults: 100,
pageToken: pageToken
});
var groups = page.groups;
if (groups) {
for (var i = 0; i < groups.length; i++) {
var group = groups[i];
Logger.log('%s (%s)', group.name, group.email);
}
} else {
Logger.log('No groups found.');
}
pageToken = page.nextPageToken;
} while (pageToken);
}
Then you can simply call the server script by using the following in the client scripting:
google.script.run.withSuccessHandler(function(response){
console.log(response);
}).withFailureHandler(function(err){
console.log(err);
}).listAllGroups();
You can check the reference here. I hope this helps!
Related
I've looked extensively and tried to modify multiple sample sets of codes found on different posts in Stack Overflow as well as template documents in Google App Maker, but cannot for the life of me get an export and en email function to work.
UserRecords table:
This is the area where the data is collected and reviewed, the populated table:
These are the data fields I am working with:
This is what the exported Sheet looks like when I go through the motions and do an export through the Deployment tab:
Lastly, this is the email page that I've built based on tutorials and examples I've seen:
What I've learned so far (based on the circles I'm going round in):
Emails seem mostly straight forward, but I don't need to send a message, just an attachment with a subject, similar to using the code:
function sendEmail_(to, subject, body) {
var emailObj = {
to: to,
subject: subject,
htmlBody: body,
noReply: true
};
MailApp.sendEmail(emailObj);
}
Not sure how to change the "body" to the exported document
To straight up export and view the Sheet from a button click, the closest I've found to a solution is in Document Sample but the references in the code speak to components on the page only. I'm not sure how to modify this to use the table, and also what to change to get it as a sheet instead of a doc.
This may seem trivial to some but I'm a beginner and am struggling to wrap my head around what I'm doing wrong. I've been looking at this for nearly a week. Any help will be greatly appreciated.
In it's simplest form you can do a Google sheet export with the following server script (this is based on a model called employees):
function exportEmployeeTable() {
//if only certain roles or individuals can perform this action include proper validation here
var query = app.models.Employees.newQuery();
var results = query.run();
var fields = app.metadata.models.Employees.fields;
var data = [];
var header = [];
for (var i in fields) {
header.push(fields[i].displayName);
}
data.push(header);
for (var j in results) {
var rows = [];
for (var k in fields) {
rows.push(results[j][fields[k].name]);
}
data.push(rows);
}
if (data.length > 1) {
var ss = SpreadsheetApp.create('Employee Export');
var sheet = ss.getActiveSheet();
sheet.getRange(1,1,data.length,header.length).setValues(data);
//here you could return the URL for your spreadsheet back to your client by setting up a successhandler and failure handler
return ss.getUrl();
} else {
throw new app.ManagedError('No Data to export!');
}
}
I'm trying to have my google sheets synced with my firebase database. I'm not very experienced with javaScript, so is it possible using the below method? The idea is that it would automatically sync every time a new row gets created/updated/deleted. I know that I need the script files but not sure how to import them in the .gs file, so that's why it's in the html.
Many thanks!
translate.gs
function saveToFirebase() {
var config = {
apiKey: "MY_API_KEY",
authDomain: "MY_DOMAIN.firebaseapp.com",
databaseURL: "MY_DOMAIN.firebaseio.com",
projectId: "MY_DOMAIN",
storageBucket: "MY_DOMAIN.appspot.com",
messagingSenderId: "MESSAGE_ID"
};
firebase.initializeApp(config);
var database = firebase.database();
database.ref('food/' + MY_USER_UID).set({
name: "pizza funghi",
});
}
sidebar.html
<!DOCTYPE html>
<html>
<head>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-auth.js"></script>
<script src="https://www.gstatic.com/firebasejs/4.12.0/firebase-database.js"></script>
</head>
<body>
</body>
</html>
There is a third-party libarary which integrates with Firebase's REST API. If you're comfortable using it, this becomes pretty straightforward.
First we'll need to create a tab to track changes. We need the identity of those who make changes, so we have to break this into two parts - a simple onEdit trigger which runs as the modifying user, and an installable trigger which I'll call uploadChanges. The latter is what talks to Firebase.
Create a tab called changes
Add a frozen row with the following headers:
Uploaded
User
Value
Install the third party Firebase library
Begin by clicking Resources > Libraries in the script editor, then pasting MYeP8ZEEt1ylVDxS7uyg9plDOcoke7-2l in the "Find a Library" box. Hit Save.
Opt for stability by choosing the latest public release, or choose the latest release (I chose latest while writing this).
Click OK
Now would be a good time to peruse the reference docs so you know what I'm up to in the below instructions :-)
Set up security (I'm assuming you want this script to run as you)
Make your Google account (which runs the script) be at least an Editor for your Firebase project.
Set the appropriate authorization scopes for your App Script project:
Go to File > Project Properties > Scopes in the App Script editor
Select View > Show manifest file (the manifest file is usually hidden by default)
Add https://www.googleapis.com/auth/userinfo.email and https://www.googleapis.com/auth/firebase.database to the oauthScopes array (add it if it's not already there)
Save the manifest file. Next time you run the script you'll get a pop-up asking about permissions.
The equivalent of your translate.gs above, which always just sets your food to 'pizza funghi`, would look like this:
function saveToFirebase() {
var dbUrl = "MY_DOMAIN.firebaseapp.com"; // Set appropriately
var token = ScriptApp.getOAuthToken(); // Depends on security setup above
var firebase = FirebaseApp.getDatabaseByUrl(dbUrl, token);
newData = {
name: "pizza funghi",
};
firebase.setData('food/' + MY_USER_UID, newData);
}
But you said you wanted to update Firebase on every save. To do this you really just want to rip off one of the various onEdit tutorials floating around the net. The resulting onEdit should look something like this:
function onEdit(e) {
// First get stuff about the edit.
// This approach only gets the top left cell of a multi-cell edit.
var editRange = e.range; // The edited range
var newValue = editRange.getValue();
// Next, who is the editor? Remove the `split` for full email.
var username = Session.getActiveUser().getEmail().split('#')[0];
if (username == '') {
username = SOME_REASONABLE_DEFAULT; // Or give up if you wish
}
// Finally save the change
SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('changes')
.appendRow([false, username, newValue]);
}
function uploadChanges() {
// Attach to Firebase
var dbUrl = "MY_DOMAIN.firebaseapp.com"; // Set appropriately
var token = ScriptApp.getOAuthToken(); // Depends on security setup above
var firebase = FirebaseApp.getDatabaseByUrl(dbUrl, token);
// Get content of changes tab
var changeSheet = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('changes');
var changeData = changeSheet.getDataRange()
.getValues();
// Upload all new-to-us changes
for (var i = 1; i < changeData.length; i++) {
if (changeData[i][0]) {
continue; // We already uploaded this one
}
changeData[i][0] = true; // Optimistically assume we'll succeed
var newData = {
name: changeData[i][2]
};
var username = changeData[i][1];
firebase.setData('food/' + username, newData);
}
// Blanket update of change-data sheet to update upload status
changeSheet.getRange(1, 1, changeData.length, changeData[0].length)
.setValues(changeData);
}
Lastly, set up some triggers.
Choose Edit > Current Project's Triggers in the script editor
Add a new trigger for onEdit
Choose onEdit from the leftmost Run dropdown
Choose From spreadsheet in the Events dropdown
Then choose On edit in the rightmost dropdown
Add a new trigger for uploadChanges
Choose uploadChanges from the leftmost Run dropdown
Choose Time-driven from the Run dropdown
Set up a schedule that's appropriate to your needs
EDIT: My original script had you doing everything in onEdit, which tehhowch correctly points out won't work since we're talking to another service. I've updated to stage to a "changes" tab which I include in setup. My new approach maintains a perpetual record of old uploads; for performance you might instead choose to just clear the changes sheet once you've done the upload.
Using GoogleAppMaker how to create a data source from google contacts. There is an employee HR example app but I want to similarly manage contacts (add, modify, delete) and use select criteria.
At this time this task is not trivial in App Maker and it is pretty much generic. We can change question wording to CRUD operations with 3rd party datasources. Let's break it into smaller parts and address them separately.
Read/list contacts
This task is relatively easy. You need to use Calculated Model to proxy Apps Scripts Contacts API response. Once you create model with subset of fields from the Contact response you can create datasource for the model and bind it to List or Table widget. You can also try to find some inspiration in Calculated Model Sample.
// Server side script
function getContacts_() {
var contacts = ContactsApp.getContacts();
var records = contacts.map(function(contact) {
var record = app.models.Contact.newRecord();
record.FirstName = contact.getGivenName();
record.LastName = contact.getFamilyName();
var companies = contact.getCompanies();
if (companies.length > 0) {
var company = companies[0];
record.Organization = company.getCompanyName();
record.Title = company.getJobTitle();
}
var emails = contact.getEmails();
if (emails.length > 0) {
record.Email = emails[0].getAddress();
}
var phones = contact.getPhones();
if (phones.length > 0) {
record.Phone = phones[0].getPhoneNumber();
}
return record;
});
return records;
}
Create/Update/Delete
Since Calculated Models have some limitations, we need to turn on our imagination to create, update and delete records from their datasources. The basic strategy will be calling server side scripts for CUD operations in response to user actions on client side. To get user's input from UI we will need to utilize page's Custom Properties, one property for each Contact field:
Here are some snippets that should explain the idea
Create
// Client script
function onSubmitContactClick(submitButton) {
var props = submitButton.root.properties;
var contact = {
FirstName: props.FirstName,
LastName: props.LastName,
Organization: props.Organization,
...
};
google.script.run
.withSuccessHandler(function() {
// Most likely we'll need to navigate user back to the
// page with contacts list and reload its datasource
// to reflect recent changes, because our `CUD` operations
// are fully detached from the list datasource
app.showPage(app.pages.Contacts);
app.datasources.Contacts.load();
})
.withFailureHandler(function() {
// TODO: Handle error
})
.createContact(contact);
}
// Server script
function createContact(contactDraft) {
var contact = ContactsApp.createContact(contactDraft.FirsName,
contactDraft.LastName,
contactDraft.Email);
contact.addCompany(contactDraft.Organization, contactDraft.Title);
contact.addPhone(ContactsApp.Field.WORK_PHONE, contactDraft.Phone);
}
Update
Idea to update contact records will be very similar to the new contact creation flow, so I skip it for now.
Delete
Assuming that delete button is located inside contacts table row.
// Client script
function onDeleteContactClick(deleteButton) {
var email = deleteButton.datasource.item.Email;
google.script.run
.withSuccessHandler(function() {
// To update contacts list we can either reload the entire
// datasource or explicitly remove deleted item on the client.
// Second option will work way faster.
var contactIndex = deleteButton.parent.childIndex;
app.datasources.Contacts.items.splice(contactIndex, 1);
})
.withFailureHandler(function() {
// TODO: Handle error
})
.deleteContact(contact);
}
// Server script
function deleteContact(email) {
var contact = ContactsApp.getContact(email);
ContactsApp.deleteContact(contact);
}
I am new to Meteor and I want to know how we can ask the user to upload his image while creating an account? I am using basic Meteor accountsfor creating user accounts. I want user to be able to upload his image and also be able to show it when he logs in and on the landing page for my website(i.e even when the user is not logged in!).
I need pointers on the same.
Is there any extra package that helps doing this. A tutorial or a code snippet will help a lot.
Regards.
Use https://github.com/CollectionFS/Meteor-CollectionFS to upload and store image.
'change #profile-image-input': function(event, template) {
var files = event.target.files;
console.log(files);
for (var i = 0, ln = files.length; i < ln; i++) {
Session.set('isUploading', true);
Collections.ProfileImages.insert(files[i], function (err, fileObj) {
if(err){
Session.set('isUploading', false);
toast('Please Select An Image To Upload')
}else{
Session.set('imageUrl',fileObj._id);
}
});
}
},
The Image.insert() returns document object id which you can use to retrieve the profile image, I'd recommend once upload is complete get the _id and store it in user profile.
Here is the problem :
I am currently programming a chatapp based on what i found on github (https://github.com/sasikanth513/chatDemo)
I am refactoring it with iron-router.
When I go to the page (clicking on the link) I get an existing chatroom (that's what I want)
When I refresh the page (F5) I get a new created chatroom ! (what i want is getting the existing chatroom ...)
Here is the code in ironrouter :
Router.route('/chatroom', {
name: 'chatroom',
data: function() {
var currentId = Session.get('currentId'); //id of the other person
var res=ChatRooms.findOne({chatIds:{$all:[currentId,Meteor.userId()]}});
console.log(res);
if(res){
Session.set("roomid",res._id);
}
else{
var newRoom= ChatRooms.insert({chatIds:[currentId, Meteor.userId()],messages:[]});
Session.set('roomid',newRoom);
}
}
});
You can find my github repo with the whole project : https://github.com/balibou/textr
Thanx a lot !
Your route data depends on Session variables which will be erased after a refresh. You have a few options but the easiest would be to put the room id directly into the route: '/chatroom/:_id'. Then you can use this.params._id to fetch the appropriate ChatRooms document. Note that you could still keep '/chatroom' for cases where the room doesn't exist, however you'd need to redirect to '/chatroom/:_id' after the insert.
In meteor, the Session object is empty when the client starts, and loading/refreshing the page via HTTP "restarts" the client. To deal with this issue, you could persist the user's correspondent id in a Meteor.user attribute, so that you could easily do:
Router.route('/chatroom', {
name: 'chatroom',
data: function() {
var currentId = Meteor.user().profile.correspondentId;
var res=ChatRooms.findOne({chatIds:{$all:[currentId,Meteor.userId()]}});
console.log(res);
if(res){
Session.set("roomid",res._id);
}
else{
var newRoom= ChatRooms.insert({chatIds:[currentId, Meteor.userId()],messages:[]});
Session.set('roomid',newRoom);
}
}
});
This would work, with the proper permissions, but I would recommend not allowing the direct update of that value on the client (I don't know if you want users to be able to override their correspondentId). So if you want to secure this process, replace all that code with a server method call, where your updates are safer.
Another (and more common case) solution was given by David Weldon, if you don't mind having ids in your URL (and therefore not a single url)