Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Saturday, February 06, 2016

Google Drive API JavaScript App Running in App Engine

I worked through the JavaScript quickstart guide of the Google Drive V3 API and modified it slightly so I could host and run the whole thing in the Google App Engine . I suggest you work through the quickstart once (following all the steps) before you read on--unless you're already familiar with App Engine and the Google Cloud Console, it may be difficult to follow what needs to change. The quickstart is a simple one-page app that allows the user to authorize access to their Google Drive and then shows ten files and/or folders in that Drive.

Why would you want to do this? It's great to be able to create your app that uses the Google Apps APIs, but even better is to host that app within the Google App Engine so that you don't have to worry about maintaining any of the infrastructure that runs your app. You also get the benefit of sophisticated App Engine features such as performance scaling.



The steps in the Drive V3 quickstart (https://developers.google.com/drive/v3/web/quickstart/js) will get you most of the way, but with a few changes, you can serve the app from the Google App Engine, which is super powerful and a more modern mechanism for running a web application.

One thing you'll have to change is the URL that your client ID will accept. The quickstart expects you to host and run the app on your local machine, and therefore use http://localhost as the URL. You can enter that when you work through the tutorial, but you'll have to change the URL for "Authorized JavaScript origins" later to the URL for your particular app. However, you won't have that URL until you try running your app from the Google Cloud Shell.

Your client ID should look something like this (I've edited it so as not to disclose my client ID):
111111-gchjkdptqt15lp89s229jo8h25omel4e.apps.googleusercontent.com

Here are some notes to clarify how to get the quickstart running in Google App Engine from the Google Developer Console:

  • You'll likely want to create your own GitHub repo and clone it to your local machine.
  • When you get to the step in the tutorial, you can connect your app project to your new Git repo. You'll have to authorize Google App Engine to use your repo. By connecting the two, it is super easy to manage (and even edit) your code from within the Google Developer Console.
  • Create the sample file (with code provided) in the local repo and push it to master that is synching with GitHub.
  • Don't forget to change your client ID in the sample code: var CLIENT_ID = '';
  • Once you have everything ready. Make sure you have your new quickstart project selected and then open the developer cloud console. The console is a Linux based shell.
  • Change directory (CD) to the src directory for your quickstart project.

cawood@definite-destiny-999999:~$ cd src/definite-destiny-999999/master

  • Start a web server using Python to host your application: python -m SimpleHTTPServer 8080

cawood@definite-destiny-999999:~/src/definite-destiny-999999/master$ python -m SimpleHTTPServer 8080
Serving HTTP on 0.0.0.0 port 8080 ...10.240.0.145 - - [12/Jan/2016 00:11:56] "GET /?authuser=0/ HTTP/1.1" 200 -10.240.0.150 - - [12/Jan/2016 00:11:58] "GET /quickstart.html HTTP/1.1" 200 -

  • Open the Web preview from the console window in port 8080. When you navigate to the quickstart.html file, you will get the URL you need to add to your client ID credentials. The Web preview option is in the top-left corner of the cloud console. 
  • Find your client ID in the credentials section of the dev console and edit it to add the URL you just opened in web preview. (To stop the app (quit the web server), press Ctrl+C.)

That's it! You should now have the quickstart running in Google App Engine! Sweet.


Note: I do not cover deploying your app for production use. Perhaps that can be a future post.

Note: If you've made a change and pushed it from your local machine, but the file isn't updating, try closing and reopening the console. I've found that it doesn't pick up changes to the files unless I do this.

I've made the project public on GitHub: https://github.com/stephencawood/WebHelpEditor/tree/master/WebHelpEditor

Thursday, December 05, 2013

Using Wyzz Web-based HTML editing control in ASP.NET MVC

I recently had to put out a web-based single page application (SPA) on short notice. To make that happen, I knew I had to use some open-source controls. One was the jsTree treeview control (which I wrote about on this blog - Using jsTree with ASP.NET MVC) and another was the Wyzz WYSIWYG web-based editing control for HTML.
From their site, “Wyzz is an ultra-small, very light WYSIWYG (What You See Is What You Get) Editor for use in your web applications. It's written in JavaScript, and is free (as in speech and as in beer) for you to use in your web applications and/or alter to your needs (see the license conditions).
image
Naturally, the first step to add a reference to the wyzz.js script file. Once you have that, you just need to add the control to an HTML <textarea> element. Finally, it’s a simple matter of adding some JavaScript to “make_wyzz” the control.
<script language="JavaScript" type="text/javascript" src="~/Home/wyzz.js"></script>



<textarea name="textEditor" id="textEditor" rows="10" cols="40">No file loaded...</textarea><br />
<script language="javascript1.2">
    make_wyzz('textEditor');
</script> <div ng-controller="EditorCtrl"> <form novalidate class="simple-form"> <button ng-click="saveFileContent()">save</button> </form> </div>

As you can see in the example above, I’ve chosen to use an AngularJS control to define the behaviour of the save button. In the JavaScript I define a server-side controller function (ASP.NET in this case) and I send it the content of the control by accessing the HTML element that the control is using.

$scope.saveFileContent = function () { 
        $http.post('/Home/SaveFileContent', { filePath: document.getElementById("multilingualfile").innerHTML, content: document.getElementById("wysiwyg" + "textEditor").contentWindow.document.body.innerHTML, title: document.getElementById("titleHtml").value })
            .then(
            function (response) {
                alert("File Save Result: " + response.data.Result);
            },
            function (data) {
                alert("Error saving file content");
            }
        );
    }

Update: Here’s the basic format of the server-side part:


[HttpPost]
public ActionResult SaveFileContent(string filePath, string content, string title)
{
    try
    {
        ...
        
        return Json
            (
                new
                {
                    Result = "Success",
                }
            );
    }
    catch (Exception ex)
    {
       ...

        return Json
            (
                new
                {
                    Result = "Error saving content: " + ex.ToString(),
                }
            );
    }
}

To customize your Wyzz controls, you can edit the wyzz.js file. If you have any issues, refer to the Wyzz discussion forum.

Sunday, December 01, 2013

Using jsTree with ASP.NET MVC

When I wanted to use a pure JavaScript treeview control for a recent ASP.NET MVC5 project, I looked around and found jsTree; it’s a popular and rich solution, so I decided to try it. I ran into a few customization hurdles, so here are my lessons learned.

Note that this is for jsTree 1.0; at the time of writing, 3.0 has not been released.

Step 1: The HTML in the view. Pretty simple…

<div id="FileTree"></div>


Step 2: Loading the tree dynamically from the MVC controller using jQuery.

<script type="text/javascript"> 
// Begin JSTree: courtesy Ivan Bozhanov: http://www.jstree.com:

$('#FileTree').jstree({
"json_data": {
"ajax": {
"url": "/Home/GetTreeData",
"type": "POST",
"dataType": "json",
"contentType": "application/json charset=utf-8"
}
},
"themes": {
"theme": "default",
"dots": false,
"icons": true,
"url": "/jstree/themes/default/style.css"
},

"contextmenu": {
"items": {
"create": false,
"rename": false,
"remove": false,
"ccp": false,
}
},

"plugins": ["themes", "json_data", "dnd", "contextmenu", "ui", "crrm"]
})

</script>


Step 3: Server-side code to populate the tree. This code is based on desalbres’s Simple FileManager with jsTree. (The model code is below.)

// Begin JSTree (Controller code courtesy desalbres: http://www.codeproject.com/Articles/176166/Simple-FileManager-width-MVC-3-and-jsTree)
[HttpPost]
public ActionResult GetTreeData()
{
if (AlreadyPopulated == false)
{
JsTreeModel rootNode = new JsTreeModel();
rootNode.attr = new JsTreeAttribute();
rootNode.data = "Root";
string rootPath = Request.MapPath(dataPath);
rootNode.attr.id = rootPath;
PopulateTree(rootPath, rootNode);
AlreadyPopulated = true;
return Json(rootNode);
}
else
{
return null;
}
}

/// <summary>
/// Populate a TreeView with directories, subdirectories, and files
/// </summary>
/// <param name="dir">The path of the directory</param>
/// <param name="node">The "master" node, to populate</param>
public void PopulateTree(string dir, JsTreeModel node)
{
if (node.children == null)
{
node.children = new List<JsTreeModel>();
}
// get the information of the directory
DirectoryInfo directory = new DirectoryInfo(dir);
// loop through each subdirectory
foreach (DirectoryInfo d in directory.GetDirectories())
{
// create a new node
JsTreeModel t = new JsTreeModel();
t.attr = new JsTreeAttribute();
t.attr.id = d.FullName;
t.data = d.Name.ToString();
// populate the new node recursively
PopulateTree(d.FullName, t);
node.children.Add(t); // add the node to the "master" node
}
// loop through each file in the directory, and add these as nodes
foreach (FileInfo f in directory.GetFiles("*.htm"))
{
// create a new node
JsTreeModel t = new JsTreeModel();
t.attr = new JsTreeAttribute();
t.attr.id = f.FullName;
t.data = f.Name.ToString();
// add it to the "master"
node.children.Add(t);
}
}

// Don't load the jsTree treeview again if it has already been populated.
// Note: this causes a bug where the tree won't repaint on browser refresh
public bool AlreadyPopulated
{
get
{
return (Session["AlreadyPopulated"] == null ? false : (bool)Session["AlreadyPopulated"]);
}
set
{
Session["AlreadyPopulated"] = (bool)value;
}

}
// End JSTree

First I had to resolve the issue that a browser refresh would repaint the whole treeview. It’s possible that I simply missed this when I cherry picked code from the FileManager codeproject example.


public ActionResult Test(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
Session["AlreadyPopulated"] = false;
return View();
}

Next, I had to customize jsTree the way I wanted it to behave. Getting the tree to start collapsed (closed) instead of expanded (open) was the first order of business. The jsTree API took care of the problem.


$('#FileTree').bind("loaded.jstree", function (event, data) { 
$(this).jstree("close_all");
})


Next, I wanted the leaf nodes to use a different background image than the folder nodes. This required changing the server-side code to actually write the leaves (files) as leaf nodes and then add the right CSS to style the jstree-leaf class.



namespace FileEditor.Models 
{
public class JsTreeModel
{
public string data;
public JsTreeAttribute attr;
// this was "open" but changing it to “leaf” adds “jstree-leaf” to the class
public string state = "leaf";
public List<JsTreeModel> children;
}

public class JsTreeAttribute
{
public string id;
}
}


And then styling the leaf nodes with a different background image than the folders.



<style type="text/css"> 
#FileTree .jstree-leaf > a > ins {
background: url("/jstree/themes/default/d.gif");
background-position: -2px -19px !important;
}
</style>


Finally, I wanted to disable the right-click context menu options since I’m not using them. (This code appears in the code above.)

"contextmenu": {
"items": {
"create": false,
"rename": false,
"remove": false,
"ccp": false,
}
},

That’s it. jsTree is not working the way I want. I expect that version 3 will be great when it is released.

Other posts on this topic:
jsTree – Few examples with ASP.Net/C#
Simple FileManager width MVC 3 and jsTree