Fetch and display Google Doc body within html page - content-management-system

Is it possible to retrieve the content of a Google Doc and display it within a div in an html page? If so, what's the right way to implement the "MAGIC" in the stripped-down example below?
<html>
<head>
</head>
<body>
<div>
<MAGIC>
Script or link that retrieves and displays the body of a Google Doc.
</MAGIC>
</div>
</body>
</html>
In the above, you can assume
The html is served by Google Drive Hosting.
The reference to the Google Doc is static.
There is no need to edit the Doc from within the public html page (i.e it's read-only in that context).
I've read through the Apps Script documentation and it looks as though something might be possible with some combination of Document Service and Content Service. For instance, Document Service has getBody() and copy() methods, but it's not clear whether the objects returned by these calls can be rendered WYSIWYG as html for insertion into an html container.
Background: I'm trying to implement a safe easy-to-use CMS for a small nonprofit. I've prototyped a website framework that's hosted
on Google Drive. So far it looks promising, but changes require being able to edit the html. We have a number of people who can create content in a word-processor-like environment but only couple including myself
who can cope with HTML/CSS/JQuery/AppsScript.
If I could concentrate on the overall framework and let the others update the content for
events, etc., that would be a huge win. Basically, I'd be very happy if they were able to edit the Google Doc and then manually reload the web page to see the result.
I realize there are many approaches for CMS, but for now, I'm interested in exploring a pure Google Docs/Google Drive solution.

I've settled on publishing the content docs and including the iframe embed code supplied by Google to implement the "MAGIC" from my original question, e.g
<iframe class="cmsframe" src="https://docs.google.com/document/d/1rhkuAB3IIu5Hq0tEtA4E_Qy_-sJMMnb33WBMlAEqlJU/pub?embedded=true"></iframe>
The class tag is added manually so I can control the iframe size with CSS.

You can get the raw html content of a google doc with a call to the drive API using urlFetch, here is how it works
var id = 'Doc-Very-Long-ID-Here';
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/documents/Export?exportFormat=html&format=html&id='+id,
googleOAuth_('docs',url)).getContentText();
// the variable doc is the HTML content that you can use
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}
There is also a library by Romain Vialard available here it is called DocsListExtended and provides a whole bunch of nice extensions.
EDIT : Following your EDIT:
You can't use it just like that, to render an HTML content in a webapp use html service, example below with your complete code and working example:
function doGet() {
var id = '1el3DpTp1sukDjzlKXh8plf0Zj-qm0drI7KbytroVrNU';
var url = 'https://docs.google.com/feeds/';
var doc = UrlFetchApp.fetch(url+'download/documents/Export?exportFormat=html&format=html&id='+id, googleOAuth_('docs',url)).getContentText();
return HtmlService.createHtmlOutput(doc);
}
// the variable doc is the HTML content that you can use
function googleOAuth_(name,scope) {
var oAuthConfig = UrlFetchApp.addOAuthService(name);
oAuthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oAuthConfig.setAuthorizationUrl("https://www.google.com/accounts/OAuthAuthorizeToken");
oAuthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
oAuthConfig.setConsumerKey('anonymous');
oAuthConfig.setConsumerSecret('anonymous');
return {oAuthServiceName:name, oAuthUseToken:"always"};
}

Related

How to retrieve the Query string value from web word instance

I'm creating a web add-in for Word targeting Word Online. When a document is opened using Word Online, the URL will contain a file name. I am trying to retrieve that file name.
To retrieve this URL I tried using window.location href, but it is fetching my hosted addin web application URL. Is it possible to get the Word Online URL?
Sample Code :
function getParameterByName(name, url) {
if (!url) url = window.location.search;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
What you should use instead?
You should use Office.js API that's defined here
https://learn.microsoft.com/en-us/javascript/api/office/office.document
Office.context.document.url this should return you something that you can use.
Why didn't your solution work?
Unfortunately this is not possible the way you're trying to implement it due to security requirements around iFrames. Word Online is using an iFrame to host your add-in and accessing the parent window from an iFrame is generally very restricted unless you're specifically allowed.
You can however get the base host url of the parent window by looking into the referrer header, but that's pretty much it. I understand that this is not sufficient for you, which is why I'm recommending to use the Word API.
You can see a bit more documentation below about iFrames:
iFrame spec documentation: https://www.w3.org/TR/2011/WD-html5-20110525/the-iframe-element.html
On getting the url that's loading the frame (the parent URL): https://humanwhocodes.com/blog/2013/04/16/getting-the-url-of-an-iframes-parent/
Some more information on how to communicate between parent and the iFrame using postMessage API if you were whitelisted (which you aren't): https://medium.com/#Farzad_YZ/cross-domain-iframe-parent-communication-403912fff692

Tracking Clicks in Emails to 3rd Party Websites

A hopefully simple GA newbie question.
I programmatically (in Java) send emails to my clients and I'm trying to track what links in the email they click.
The links in the emails are to third party websites.
Is there a way to setup the href links so that I can track what uid's are clicking and treat clicking those links as an event?
An example link would be to a piece of legislation:
https://www.cga.ct.gov/asp/cgabillstatus/cgabillstatus.asp?selBillType=Bill&which_year=2017&bill_num=5210
how would I add that link in the email HTML?
That does not work, at least not with additional resources.
Google Analytics tracks links by sending a request to the Google servers before the user is redirected to the link href. In web tracking this is usually done via a javascript event handler that is attached to the link. In e-mail javascript is not really an option, so that does not work.
What is usually done instead is that in your emails you link to your own server; there is a script that collects the data from the link, and only then it redirects to the originally requested url. Most commercial newsletter packages integrate a solution like that, but you can set up something yourself with some basic programming skills and the Google Analytics measurement protocol (which would allow you to send tracking calls from a server-side redirection script).
Setup the Redirects Tracking Engine
The Redirects Tracking Engine makes it easy to collect event data where traditional tracking methods may not have been possible. This tracking engine utilizes Google Tag Manager and Google Analytics to capture events and is run on the client side. All events can be seen in Google Analytics (Behavior > Events > Top Events).
Getting Started
Installation
Add the latest JQuery and Redirects Tracking Engine scripts to the header of your site.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script><script type="text/javascript">// Fetches redirects.txt and stores information in the variable data
$.get('redirects.txt', function(data) {
// Fetches the parameters of the query string and stores the path in urlParams
var urlParams = [];
// Sifts through the string and removes unwanted characters
(function () {
var match,
pl = /\+/g, // Regex for replacing addition symbol with a space
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) { return decodeURIComponent(s.replace(pl, '')); },
query = window.location.search.substring(1);
while (match = search.exec(query))
urlParams[decode(match[1])] = decode(match[2]);
})();
// Pulls properties from urlParams and stores them in destination array
var destination = Object.keys(urlParams).map(function(path){return urlParams[path]});
// Assigns the redirects.txt data to the userData array
var userData = data.split('\n');
// Multidimensional array declaration
var redirects = [];
// Fetches the total number of objects in the userData array
var total = userData.length;
// Counter variable
var i = 0;
// Runs through the redirects array to check to see if there is a string match
while (i < total) {
// Places userData into the multidimensional redirects array
redirects[i] = userData[i].split(' => ');
// Checks for a path match in the redirects array
if (redirects[i][0] == destination[1]) {
window.location.href = redirects[i][1];
return;
}
i++;
}
// Redirects to safe page if no match is found
window.location.href = 'https://example.com';
});
Setup Google Tag Manager
Paste this code as high in the <head> of the page as possible. Make sure to substitute the filler GTM-XXXX with your Google Tag Manager Account ID.
<!-- Google Tag Manager --><script>(function(w,d,s,l,i){w[l]=w[l]|| [];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js? id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXX');</script>
<!-- End Google Tag Manager -->
Additionally, paste this code immediately after the opening <body> tag. Make sure to substitute the filler GTM-XXXX with your Google Tag Manager Account ID here as well.
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXX"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->
NOTE: For more information about installing the Google Tag Manager snippet, check out their Quick Start Guide.
Create Tags/Triggers/Variables
Create a variable — You will need to first create a User-Defined Variable. Go to the Variables page of your Google Tag Manager and in the User-Defined Variables section create a new variable named Tracking ID. Give this variable your Google Analytics Tracking ID in the Value field and SAVE.
Create a tag — You will need to go to the Tags page in Google Tag Manager and click the NEW button. Fill in the fields and click SAVE when finished.
Create a trigger — Finally, you will need to create a trigger. Go to the Triggers page in Google Tag Manager. Once there click on the NEW button and fill in the fields and click SAVE when finished.
After these configurations have been setup in Google Tag Manager you are ready to add your redirects to the redirects.txt file.
Redirects
To add a redirect open the redirects.txt file. The text linkedin is telling the engine what path it needs to look for in the URL. Immediately following is the separator => which shows the engine where the assigned path should direct the user. New redirects can be added on a new line and there are no limits to the number of redirects that can be added to this file.
// Example Redirect
linkedin => https://example.com
Failsafe
In the off-chance that one of the redirects is not working it is good to have a page that the user can be directed to. You can add in your failsafe page by modifying the window.location.href location that comes immediately after the loop.
// Redirects to safe page if no match is found
window.location.href = 'https://example.com';
Browser Support
Supported Browsers : Chrome, Firefox, Safari, Opera, Edge, IE7+.
The source code as well as an example can be found on GitHub.

Rendering pictures in email's template in Meteor

I have and nice email template stored in /private folder and I have some pictures in /public/images folder. I have img tags with links to my template using full path (http://localhost:3000/images/image1) or external links.
I render my template using
SSR.compileTemplate('myTemplate',Assets.getText('myTemplate.html');
renderedTemplate = SSR.render('myTemplate',emailData);
var dataContext = {
htmlHead: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">',
htmlFoot: '</html>'
};
and then I use
finalHtml = dataContext.htmlHead + renderedHtml + dataContext.htmlFoot;
Email.send({
from: smtp.login,
to: email,
subject: 'News',
html: finalHtml
});
Then when I get my lovely mail everything is nice and perfect but my attached images which are not loaded;is it a problem with SSR.compileTemplate? Usually static pictures should be attached at the bottom of email (if I check the "show original" in my gmail) but they are not there..
What am I doing wrong and how I should solve it?
Using the Meteor.absoluteUrl method is the proper way to reference an image source. However, I believe you will still face the same problem even after using it.
This is because you are trying to reference an image from a non-publicly accessible url. Which in your case localhost will be the host in your full path to your image. Your html template needs to reference the images to a specific uri. In this case, it won't be able because it's been hosted on your local machine.
If you deploy your app on a hosted environment, your host url will be served up. Hope that makes sense.
Try using Meteor.absoluteUrl, see docs here.
Maybe something like:
emailData.absoluteUrl = Meteor.absoluteUrl('/');
Then in your template:
<img src="{{absoluteUrl}}/path-to-your-image.jpg"/>
Haven't tried it myself, so this is untested. I tend to use assets direct from S3.

derbyjs data sending understanding

I want to build a website using derbyjs. The website has articles.
Every article has title (short text) and text (a lot of text) fields.
Page /articles is a page where I can see titles of all articles.
Clicking on article opens /articles/<article_id> where I can see the text of the clicked article.
In usual server-side framework the client would get only html. It's nice and simple.
But in derby, as I understand, we get data and html separately and then pushing data to html on client.
So, my questions:
1) How to make /articles load only titles, but not texts of articles? (in other words, load only data we need for the current page and no more)
2) When I click on some article, html changes immediately, right? But text of the clicked article loads not immediately (because it was not loaded before). So what should client see? Blank page which will be filled with text, when data for the article will be loaded?
1) Because of implementation of ShareJS at the moment there is only possible to subscribe(fetch) at minimum to the whole document. It means that you can not get from server just title of the article.
Workarounds:
You can make method in Express router, which would return list of id + title. And you can make XMLHttpRequest request from client (as usual) to get it and put to client model.
You can split collections. First for titles, second for texts
Maybe there are more
2) In this example html will start render only after article is loaded to client:
app.get('/articles/:id', function(page, model, params, next) {
// let's load the article
model.subscribe('/articles/' + params.id, function(err) {
// article is loaded now let's start to render html
page.render('article');
});
});
In this example html will start render before article is loaded, and after article is loaded html will be filled with data (if you use {} and not {{}} in template):
app.get('/articles/:id', function(page, model, params, next) {
// let's load the article
model.subscribe('/articles/' + params.id, function(err) {
// article is loaded and html is filled with data
});
// Still no article, let's render page without data
page.render('article');
});

Google Search autocomplete API?

Does Google provide API access to autocomplete for search like on the actual site? I have not been able to find anything.
I would like to use Google's autocomplete logic for web search on my own site which relies on Google's search API.
The new url is:
http://suggestqueries.google.com/complete/search?client=firefox&q=YOURQUERY
the client part is required; I did't test other clients.
[EDIT]
If you want the callback use this:
http://suggestqueries.google.com/complete/search?client=chrome&q=YOURQUERY&callback=callback
As #Quandary found out; the callback does not work with client "firefox".
[EDIT2]
As indicated by # user2067021 this api will stop working as of 10-08-2015: Update on the Autocomplete API
First, go to google, click Settings (bottom right corner), change Search Settings to "never show instant results. That way, you'll get regular autocomplete instead of a full page of instant results.
After your settings are saved, go back to the Google main home page. Open your browser's developer tools and go to the Network tab. If you're in Firefox, you might have to reload the page.
Type a letter in the search box. A new line should appear in the Network window you just opened. That line is showing where the autocomplete data came from. Copy that url. It should look something like this:
https://www.google.com/complete/search?client=hp&hl=en&sugexp=msedr&gs_rn=62&gs_ri=hp&cp=1&gs_id=9c&q=a&xhr=t&callback=hello
You'll notice your search term right after the part that says q=.
Add &callback=myAmazingFunction to the end of the url. You may replace myAmazingFunction with whatever you want to name your function that will handle the data.
Here's an example of the code required to show the autocomplete data for the search term "a".
<div id="output"></div>
<script>
/* this function shows the raw data */
function myAmazingFunction(data){
document.getElementById('output').innerHTML = data;
}
</script>
<script src="https://www.google.com/complete/search?client=hp&hl=en&sugexp=msedr&gs_rn=62&gs_ri=hp&cp=1&gs_id=9c&q=a&xhr=t&callback=hello&callback=myAmazingFunction"></script>
Now that you know how to get the data, the next step is to automatically change that last script (the one with the autocomplete url). The basic procedure is: each time the user types something in the search box (onkeyup) replace the search term (q=whatever) in the url, and then append to the body a script with that url. Remove the previous script so that the body doesn't get cluttered.
For more info, see http://simplestepscode.com/autocomplete-data-tutorial/
Most of the above mentioned methods works for me, specifically the following serves my purpose.
http://suggestqueries.google.com/complete/search?client=firefox&q=YOURQUERY
Being a newbie in web programming, I'm not much aware of the "Callback" functionality and the format of the file returned by query. I'm little aware of AJAX and JSON.
Could someone provide more details about the format of file returned by the query.
Thanks.
Hi I don't know if this answer is relevant for you anymore or not but google returns JSON data through following get request (although this isn't an official API but many toolbars are using this API so there's no reason why google might discontinue it):
http://google.com/complete/search?q=<Your keywords here>&hl=en
You should use AutocompleteService and pass that text box value into the service.getPlacePredictions function. It send the data in callback function.
let service = new google.maps.places.AutocompleteService();
let displaySuggestions = function(predictions, status) {
}
service.getPlacePredictions({
input: value
}, displaySuggestions);
Base: https://developers.google.com/maps/documentation/javascript/reference/places-autocomplete-service#AutocompleteService.getPlacePredictions
example: https://dzone.com/articles/implement-and-optimize-autocomplete-with-google-pl
I'm using (( Edrra.com )) API that have google search and suggestions that works with both GET & POST:
Google suggestions:
https://edrra.com/v1/api.php?c=google&f=suggest&k=YOUR_API_KEY&v=YOUR_SEARCH
Google search:
https://edrra.com/v1/api.php?c=google&f=search&k=YOUR_API_KEY&v=YOUR_SEARCH
and more...
What are you trying to use an auto-complete for? More information would help narrow it down.
As far as I know, google does not provide one, but they do exist like jQuery UI's auto-complete.
EDIT:
If you are using their custom search API view here for autocomplete.