Copy/rename google sheet, share & get share id with script in form - google-sheets

Is it possible for a script to copy/rename an (unrelated) Google spreadsheet, share it with a given email (preferably testing for the existence of a google account associated with the given email first), and save the shared url? I have spent hours and can't find any part of the answer to this: nothing on copying/renaming, nothing on share id. I may be missing some keyword or something. I realize I'll probably be downvoted for a general question, but this is the only Google Script support out there, I think. If you give me a thread, I'll follow it.

So: I had to figure this out, and finally hired someone to code this snippet for me, here's what I got. This works pretty great for me. I did a tiny bit of customization.
Here's the requirement:
Given 2 Google Sheets, GTemplate & GTracker (these are two separate spreadsheets, not tabs in the same spreadsheet). Create a form (that reports to a tab/sheet in GTracker) which a user (anyone) fills in online with an email "UserEmail", and a string "UserID". Upon submission:
1) Make a copy of GTemplate, and rename it to "GTUserID" (if GTUserID already exists, name it GTUserID1, 2, or whatever)
2) Check that the submitted email has an associated Google Account
3) Share GTUserID with UserEmail
4) Save the URL of GTUserID to GTracker
5) Send an email to UserEmail confirming success or failure of above
//************ Edit here *******************************
var stremplateID = '1PBO9KhoZa9iX3Uik-FxXGnvhgs0BoNQUJmV95UUg56o';
//******************************************************
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Initialization')
.addItem('Initialize', 'setTrigger')
.addToUi();
}
function setTrigger(){
var arr= ScriptApp.getProjectTriggers();
for (var i= arr.length; i>0; i--){
ScriptApp.deleteTrigger(arr[i-1])
}
ScriptApp.newTrigger('onFormSubmit')
.forSpreadsheet(SpreadsheetApp
.getActiveSpreadsheet().getId())
.onFormSubmit()
.create()
}
function onFormSubmit(e) {
try {
//Logger.log(JSON.stringify(e))
var folder;
var strEmail = e.namedValues.Email;
var strUID = e.namedValues['User ID'];
//Logger.log(strEmail)
//Logger.log(strUID)
var oldFile = DriveApp.getFileById(stremplateID);
var folders = oldFile.getParents();
while (folders.hasNext()) {
folder = folders.next();
break;
}
if ((typeof folder) != "object") {
folder = DriveApp.getRootFolder();
}
var bolFlag = false;
var bolFlag1 = false;
var i = 0;
var myRegexp = new RegExp('[^a-zA-Z0-9.-]','g');
var strProcUID=String(strUID).replace(myRegexp, "_");
//Logger.log(strProcUID)
var strFilename = strProcUID;
while (!bolFlag) {
var files = folder.getFiles();
while (files.hasNext()) {
var file = files.next();
if (file.getName() == strFilename) {
bolFlag1 = true;
}
}
if (!bolFlag1) {
bolFlag = true;
} else {
i = i + 1;
strFilename = strProcUID + i;
bolFlag1 = false;
}
}
var newFile = oldFile.makeCopy(strFilename, folder);
newFile.addEditors(strEmail);
var link = newFile.getUrl();
var sh = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var row = sh.getLastRow();
var col = sh.getLastColumn();
sh.getRange(row, col, 1, 1).setValue(link);
var body = 'Dear ' + strUID + ',\n' +
'Your request has been processed successfully.\n' +
'The file can be seen here:\n' +
link + '\n' +
'Regards,\n ' +
'Admin';
GmailApp.sendEmail(strEmail, 'Request Processed', body);
} catch (e) {
var body = '\n' +
'An error occurred while processing the request:\n' +
'User ID: ' + strUID + '\n ' +
'Email: ' + strEmail + '\n ' +
'Error: ' + e;
GmailApp.sendEmail(Session.getEffectiveUser().getEmail(), 'Error processing a request', body);
var body = 'Dear ' + strUID + ',\n' +
'Sorry, an error occurred while processing your request.\n' +
'Regards,\n ' +
'Admin';
GmailApp.sendEmail(strEmail, 'Error processing the request', body);
}
}
Perhaps this will be helpful to someone else. The things it does that I couldn't find was copying a copied/shared Google Sheet URL into a different sheet (for creating daughter shared documents for different projects, initiated by the project teams, still owned by the admin account, with internal fields readily accessible since we've got the URL). I hope that's clear and helpful.

Related

Firebase Dynamic Links on Unity iOS says "Deep Link does not contain valid required params"

I'm using Firebase Dynamic Links for Unity, and I've got it working well with Android. I've even got a solution for Desktop, where the fallback link takes users to a webpage where I can provide instructions to the user for how to get their link content on Desktop.
On iOS, however, I always get errors like this when trying dynamic links:
[Firebase/Analytics][I-ACS023001] Deep Link does not contain valid required params. URL params: {
"_cpb" = 1;
"_cpt" = cpit;
"_fpb" = "CIAIEIAGGgVlbi11cw==";
"_iipp" = 1;
"_iumchkactval" = 1;
"_iumenbl" = 1;
"_osl" = "https://cgs.link/zu_tiles_hime?_iipp=1";
"_plt" = 260;
"_uit" = 1064;
apn = "com.finoldigital.cardgamesim";
cid = 8062347334713659136;
ibi = "com.finoldigital.CardGameSim";
isi = 1392877362;
link = "https://www.cardgamesimulator.com/link%%3Furl%%3Dhttps://www.cardgamesimulator.com/games/zu_tiles_hime/zu_tiles_hime.json";
sd = "Play Zu Tile: Hime on CGS!";
si = "https://www.cardgamesimulator.com/games/zu_tiles_hime/Banner.png";
st = "Card Game Simulator - Zu Tiles: Hime";
}
I saw in another issue that it could be because of ?, =, and & symbols in the link, so I url-encoded those, but I am still getting the same error.
For reference, my code for iOS is effectively:
private void Start()
{
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
var dependencyStatus = task.Result;
if (dependencyStatus != DependencyStatus.Available)
{
Debug.LogError("Could not resolve all Firebase dependencies: " + dependencyStatus);
return;
}
DynamicLinks.DynamicLinkReceived += OnDynamicLinkReceived;
});
}
I immediately log in OnDynamicLinkReceived, so this callback is clearly never happening. Does anybody know what I am doing wrong, or what I could do to get the dynamic link received callback?
For anyone who runs into the same issue:
I solved this by modifying my build script to add the values for FirebaseDynamicLinksCustomDomains and FirebaseAppDelegateProxyEnabled to Info.plist as part of my build process.
PostProcess code:
var pbxProjectPath = PBXProject.GetPBXProjectPath(buildPath);
var pbxProject = new PBXProject(); pbxProject.ReadFromFile(pbxProjectPath);
const string targetName = "Unity-iPhone"; var targetGuid = pbxProject.GetUnityMainTargetGuid();
var src = AssetDatabase.GetAssetPath(file);
var fileName = Path.GetFileName(src);
var dst = buildPath + "/" + targetName + "/" + fileName;
if (!File.Exists(dst)) FileUtil.CopyFileOrDirectory(src, dst); pbxProject.AddFile(targetName + "/" + fileName, fileName);
pbxProject.AddBuildProperty(targetGuid, "CODE_SIGN_ENTITLEMENTS", targetName + "/" + fileName);
pbxProject.WriteToFile(pbxProjectPath);
var plistPath = buildPath + "/Info.plist";
var plistDocument = new PlistDocument(); plistDocument.ReadFromString(File.ReadAllText(plistPath));
var rootDict = plistDocument.root;
rootDict.SetBoolean("FirebaseAppDelegateProxyEnabled", false);
PlistElementArray customDomains = rootDict.CreateArray("FirebaseDynamicLinksCustomDomains");
customDomains.AddString("https://cgs.link");
File.WriteAllText(plistPath);

Script for sending email attachments from google script

Here is the script for the button i am working on. the script creates the pdf and folder, Sends email but doesn't grab the attachment.
I have removed the file id and url ect.
The main section i am having problems is the attachment.
Really appreciate any help with this one.
function getpdf2(){
SpreadsheetApp.flush();
//make pdf
var theurl = ''
+ '' //the file ID
+ '/export?exportFormat=pdf&format=pdf'
+ '&size=LETTER'
+ '&portrait=true'
+ '&fitw=true'
+ '&top_margin=0.50'
+ '&bottom_margin=0.50'
+ '&left_margin=0.50'
+ '&right_margin=0.50'
+ '&sheetnames=false&printtitle=false'
+ '&pagenum=false'
+ '&gridlines=false'
+ '&fzr=FALSE'
+ '&gid='
+ ''; //the sheet's Id
var token = ScriptApp.getOAuthToken();
var docurl = UrlFetchApp.fetch(theurl, { headers: { 'Authorization': 'Bearer ' + token }
});
var fileid = DriveApp.createFile(docurl.getBlob()).setName('Quote mike.pdf').getId();
//var blob = spreadsheetFile.getAs('application/pdf');
var pdf = docurl.getBlob().setName('Quote mike.pdf');
var pdf = docurl.getBlob().getAs('application/pdf').setName('Quote mike.pdf');
var filetodel = DriveApp.getFileById(fileid);
DriveApp.getRootFolder().createFolder("mike"); //comment if folder exists
// if folder exists use next
if (DriveApp.getFoldersByName("mike").hasNext()){
var folder = DriveApp.getFoldersByName("mike").next();
filetodel.makeCopy(folder);
var address = Browser.inputBox('Enter Email Address', Browser.Buttons.OK_CANCEL);
//function sendEmailWithAttachments(){
//var fileid = SpreadsheetApp.getActiveSpreadsheet()
//getAs(MimeType.PDF);
MailApp.sendEmail({to:address,
subject: "QUOTE",
body:"Thank You!!",
attachments: [fileid[0].getAs(MimeType.pdf)]
})
DriveApp.removeFile(filetodel);
}
}

Creating a hyperlink in active cell from uploaded file

I'm designing a shared Google Sheets for our team to keep track of each piece of content we produce. I want to implement a feature that allows people to upload a preview clip and have a hyperlink automatically created within the active cell.
My script so far serves up HTML as a user interface with a file upload and name entry. This part works fine and allows anyone to upload straight to Google Drive.
I've been having trouble getting it to automatically create a hyperlink in the active cell to the uploaded file. Been searching around, but haven't had a great deal of luck.
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('File Upload')
.addItem('Open', 'openDialog')
.addToUi();
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('form.html')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Upload A File');
}
function uploadFiles(form) {
try {
var dropbox = "Clips";
var folder, folders = DriveApp.getFoldersByName(dropbox);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var blob = form.myFile;
var file = folder.createFile(blob);
file.setDescription("Uploaded by " + form.myName);
return "File uploaded successfully " + file.getUrl();
} catch (error) {
return error.toString();
}
}
My suggestion is to modify the return value from uploadFiles() to be an object, then use the URL to populate a spreadsheet HYPERLINK() formula.
return "File uploaded successfully " + file.getUrl();
Becomes:
return {
result: "File uploaded successfully",
fileURL: file.getUrl(),
fileDesc: file.getDescription() // Could be other values
};
Next, a function that sets the formula. This server-side function would be called with the values to be used in the formula, which were previously returned from uploadFiles(). I'm assuming this is from your client-side JavaScript, but that's just a guess, since you didn't include that in your question.
function setHyperlink( fileURL, fileDesc ) {
var formula = '=HYPERLINK("' + fileURL + '","' + fileDesc + '")';
SpreadsheetApp.getActiveCell()
.setFormula( formula );
return true;
}
I ended up solving this issue using the GAS Properties Service - creating 2 new User Properties to contain URL and Name data.
I also found a few issues with getActiveCell - it kept placing the link in A1. Although I had used Google's suggested method for returning the active cell, I was able to use the fix suggested here:
http://bit.ly/20Gc7l6
Here's my final script
function onOpen() {
SpreadsheetApp.getUi()
.createMenu('File Upload')
.addItem('Open', 'openDialog')
.addToUi();
}
function openDialog() {
var html = HtmlService.createHtmlOutputFromFile('form.html')
.setSandboxMode(HtmlService.SandboxMode.IFRAME);
SpreadsheetApp.getUi()
.showModalDialog(html, 'Upload A File');
}
function uploadFiles(form) {
try {
var dropbox = "Blacksand Clips";
var folder, folders = DriveApp.getFoldersByName(dropbox);
if (folders.hasNext()) {
folder = folders.next();
} else {
folder = DriveApp.createFolder(dropbox);
}
var blob = form.myFile;
var file = folder.createFile(blob);
file.setDescription(file.getDescription());
var userProperties = PropertiesService.getUserProperties();
var link = file.getUrl();
var name = file.getName();
userProperties.setProperty('link', link);
userProperties.setProperty('name', name);
setHyperlink();
return "File uploaded successfully ";
} catch (error) {
return error.toString();
}
}
function setHyperlink() {
var userProperties = PropertiesService.getUserProperties();
var link = userProperties.getProperty('link');
var displayName = userProperties.getProperty('name');
var value = 'hyperlink("' + link + '","' + displayName + '")'
var ss = SpreadsheetApp.getActiveSheet();
var cell = ss.getActiveCell().activate();
cell.setFormula( value )
return true;
}

YouTube : This video contains content from vevo?

I am trying to play a YouTube video in my application. Everything works fine. However, when I try to watch a video that contains content from Vevo, it fails.
I had also tried to pass el=vevo in get_video_info:
http://www.youtube.com/get_video_info?video_id=uuZE_IRwLNI&el=vevo&ps=default&eurl=&gl=US&hl=en
stream
{
"fallback_host" = "tc.v12.cache7.googlevideo.com";
itag = 22;
quality = hd720;
s = "8E6E5D13EB65FB653B173B94CB0BCC3A20853F5EDE8.5E2E87DF33EEDE165FEA90109D3C7D5DADA06B6BB60";
type = "video/mp4; codecs=\"avc1.64001F, mp4a.40.2\"";
url = "http://r7---sn-cvh7zn7r.googlevideo.com/videoplayback?pcm2fr=yes&sver=3&expire=1393773646&itag=22&id=bae644fc84702cd2&upn=SjZd81MudQs&sparams=gcr%2Cid%2Cip%2Cipbits%2Citag%2Cpcm2fr%2Cratebypass%2Csource%2Cupn%2Cexpire&ms=au&gcr=in&mt=1393747698&source=youtube&ratebypass=yes&ipbits=0&fexp=935620%2C919120%2C912523%2C932288%2C914084%2C916626%2C937417%2C937416%2C913434%2C932289%2C936910%2C936913%2C902907&mv=m&key=yt5&ip=103.250.162.79";
}
When I use url its not playing. Is there any solution?
get_video_info works only for the videos which are allowed to be viewed as embedded videos in other websites. I struggled a lot with get_video_info but could find any solution for vevo. however I was able to make it work by retrieving the actual video page, in actual video page you have to grab player version and hit url (specified in code) to grab the streams links and actual signatures.
youtube might change this in future but today following solutions is working great for me.
Its c# you should know how to convert it into object-C, entry point of following code is ExtractUrls function and remember to pass it html of video page.
e.g. html content of http://www.youtube.com/watch?v=J5iS3tULXMQ&nomobile=1
private static List<string> ExtractUrls(string html)
{
string Player_Version = Regex.Match(html, #"""\\/\\/s.ytimg.com\\/yts\\/jsbin\\/html5player-(.+?)\.js""").Groups[1].ToString();
string Player_Code = new WebClient().DownloadString("http://s.ytimg.com/yts/jsbin/" + "html5player-" + Player_Version + ".js");
html = Uri.UnescapeDataString( Regex.Match(html, #"""url_encoded_fmt_stream_map"":\s+""(.+?)""", RegexOptions.Singleline).Groups[1].ToString());
var Streams = Regex.Matches(html, #"(^url=|(\\u0026url=|,url=))(.+?)(\\u0026|,|$)");
var Signatures = Regex.Matches(html, #"(^s=|(\\u0026s=|,s=))(.+?)(\\u0026|,|$)");
List<string> urls = new List<string>();
for (int i = 0; i < Streams.Count - 1; i++)
{
string URL = Streams[i].Groups[3].ToString();
if (Signatures.Count > 0)
{
string Sign = Sign_Decipher(Signatures[i].Groups[3].ToString(), Player_Code);
URL += "&signature=" + Sign;
}
urls.Add(URL.Trim());
}
return urls;
}
public static string Sign_Decipher(string s, string Code)
{
string Function_Name = Regex.Match(Code, #"signature=(\w+)\(\w+\)").Groups[1].ToString();
var Function_Match = Regex.Match(Code, "function " + Function_Name + #"\((\w+)\)\{(.+?)\}",RegexOptions.Singleline);
string Var = Function_Match.Groups[1].ToString();
string Decipher = Function_Match.Groups[2].ToString();
var Lines = Decipher.Split(';');
for (int i = 0; i < Lines.Length; i++)
{
string Line = Lines[i].Trim();
if (Regex.IsMatch(Line, Var + "=" + Var + #"\.reverse\(\)"))
{
char[] charArray = s.ToCharArray();
Array.Reverse(charArray);
s = new string(charArray);
}
else if (Regex.IsMatch(Line, Var + "=" + Var + #"\.slice\(\d+\)"))
{
s = Slice(s, Convert.ToInt32(Regex.Match(Line, Var + "=" + Var + #"\.slice\((\d+)\)").Groups[1].ToString()));
}
else if (Regex.IsMatch(Line, Var + #"=\w+\(" + Var + #",\d+\)"))
{
s = Swap(s, Convert.ToInt32(Regex.Match(Line, Var + #"=\w+\(" + Var + #",(\d+)\)").Groups[1].ToString()));
}
else if (Regex.IsMatch(Line, Var + #"\[0\]=" + Var + #"\[\d+%" + Var + #"\.length\]"))
{
s = Swap(s, Convert.ToInt32(Regex.Match(Line, Var + #"\[0\]=" + Var + #"\[(\d+)%" + Var + #"\.length\]").Groups[1].ToString()));
}
}
return s;
}
private static string Slice(string Input, int Length)
{
return Input.Substring(Length, Input.Length - 1);
}
private static string Swap(string Input, int Position)
{
var Str = new StringBuilder(Input);
var SwapChar = Str[Position];
Str[Position] = Str[0];
Str[0] = SwapChar;
return Str.ToString();
}
credit goes to comments under this code project artical
Certain videos have a domain-level whitelist or blacklist applied to them. This is done at the discretion of the content owner.
If there is a whitelist or a blacklist, and the domain of the embedding site can't be determined (perhaps because of there not being a real referring domain in the case of your native application), then the default behavior is to block playback.
This blog post has a bit more detail as well: http://apiblog.youtube.com/2011/12/understanding-playback-restrictions.html
That specific video can only be played when it's embedded on a real website with a real referring URL, due to the way domain white/blacklisting works. And, we don't expose those lists via the API. It's a longstanding feature request
YouTube video URL should contain a signature (which is included in the 's' field), to use this url, you need to decrypt the signature first and add it to the URL.
The signature decryptor can be found on the web page of the video (i.e. youtube.com/watch?v=VIDEO_ID).
I can't provide more info as it would be against YouTube terms of service :).

Google Calendar not displaying correct time

I have a Google Calendar for a school website I'm working on and am using the Google API to display the next five calendar events. One problem is that the time displays on a 24 hour clock instead of AM and PM, but that's not my main problem. The main problem is that while the events display the correct time on the website, when you click on the event to view it in the calendar event view, it will only display GMT time instead of Eastern Time. While logged into the Google account, the events display the right time zone, but whenever you view it while not logged in, it defaults to GMT.
I have tried changing it to another time zone and change it back, didn't fix it.
I also made sure all settings in both the calendar and the account were set to Eastern time zone, at least everywhere I could find it.
I've seen a lot of people with similar problems on Google sites using the ical or other feeds, but I haven't seen anyone with the problem using a code similar to mine.
The website is live: http://fletcheracademy.com. And here is the main javascript code that pulls it.
There's probably some details I'm missing, let me know if there's anything else you need to know. Thanks so much!
<script type="text/javascript">
google.load("gdata", "2.x");
function init() {
google.gdata.client.init(handleGDError);
loadDeveloperCalendar();
}
function loadDeveloperCalendar() {
loadCalendarByAddress('fletcheracademycalendar#gmail.com');
}
function padNumber(num) {
if (num <= 9) {
return "0" + num;
}
return num;
}
function loadCalendarByAddress(calendarAddress) {
var calendarUrl = 'https://www.google.com/calendar/feeds/' +
calendarAddress + '/public/full';
loadCalendar(calendarUrl);
}
function loadCalendar(calendarUrl) {
var service = new
google.gdata.calendar.CalendarService('gdata-js-client-samples-simple');
var query = new google.gdata.calendar.CalendarEventQuery(calendarUrl);
query.setOrderBy('starttime');
query.setSortOrder('ascending');
query.setFutureEvents(true);
query.setSingleEvents(true);
query.setMaxResults(5);
service.getEventsFeed(query, listEvents, handleGDError);
}
function handleGDError(e) {
document.getElementById('jsSourceFinal').setAttribute('style', 'display:none');
if (e instanceof Error) {
alert('Error at line ' + e.lineNumber + ' in ' + e.fileName + '\n' + 'Message: ' + e.message);
if (e.cause) {
var status = e.cause.status;
var statusText = e.cause.statusText;
alert('Root cause: HTTP error ' + status + ' with status text of: ' + statusText);
}
} else {
alert(e.toString());
}
}
function listEvents(feedRoot) {
var entries = feedRoot.feed.getEntries();
var eventDiv = document.getElementById('events');
if (eventDiv.childNodes.length > 0) {
eventDiv.removeChild(eventDiv.childNodes[0]);
}
var ul = document.createElement('ul');
//document.getElementById('calendarTitle').innerHTML =
// "Calendar: " + feedRoot.feed.title.$t;
var len = entries.length;
for (var i = 0; i < len; i++) {
var entry = entries[i];
var title = entry.getTitle().getText();
var startDateTime = null;
var startJSDate = null;
var times = entry.getTimes();
if (times.length > 0) {
startDateTime = times[0].getStartTime();
startJSDate = startDateTime.getDate();
}
var entryLinkHref = null;
if (entry.getHtmlLink() != null) {
entryLinkHref = entry.getHtmlLink().getHref();
}
var dateString = (startJSDate.getMonth() + 1) + "/" + startJSDate.getDate();
if (!startDateTime.isDateOnly()) {
dateString += " " + startJSDate.getHours() + ":" +
padNumber(startJSDate.getMinutes());
}
var li = document.createElement('li');
if (entryLinkHref != null) {
entryLink = document.createElement('a');
entryLink.setAttribute('href', entryLinkHref);
entryLink.appendChild(document.createTextNode(title));
li.appendChild(entryLink);
li.appendChild(document.createTextNode(' - ' + dateString));
} else {
li.appendChild(document.createTextNode(title + ' - ' + dateString));
}
ul.appendChild(li);
}
eventDiv.appendChild(ul);
}
google.setOnLoadCallback(init);
</script>
Try this!
Where you have:
var calendarUrl = 'https://www.google.com/calendar/feeds/' + calendarAddress + '/public/full';
you should add something like:
&ctz=Europe/Lisbon
Check here for the correct name of your timezone.

Resources