Blog

Showing 10 post.

Enabling CORS in KeystoneJS API

Posted in KeystoneJS by Chris on October 21, 2017

I'm setting up a dev server for a new open source project I'm working on: P2P VPS.

I needed to enable CORS on the server so that my dev box could communicate with it. The first thing I tried was this old article that covers it, but it didn't work. Something had changed in KeystoneJS core since I wrote that article. However these two threads lead me to  get CORS working for the regular API routes, like ConnextCMS uses.

However, I wasn't after the regular API routes. I was after the new(ish) Admin API in KeystoneJS core. This API allows one to log in remotely, which is what I would need while developing the Vue.js application on my dev box. 

As it turned out, I needed to edit the node_modules/keystone/admin/server/app/createDynamicRouter.js file in my KeystoneJS installation, and add these lines to it:


        if (keystone.get('healthchecks')) {
                router.use('/server-health', require('./createHealthchecksHandler')(keystone));
        }

router.use(function(req, res, next) { //allow cross origin requests res.setHeader("Access-Control-Allow-Methods", "POST, PUT, OPTIONS, DELETE, GET"); res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); }); // Init API request helpers router.use('/api', require('../middleware/apiError')); router.use('/api', require('../middleware/logError'));
 

Inside the keystone.js file in the root directory of my project folder, I needed to add this setting:

keystone.set('cors allow origin', true);

 

After restarting the KeystoneJS software, I could now make an AJAX call to the Admin API, like this:

$.get('http://p2pvps.net/keystone/api/session', '', function(data){debugger;})

Read more...

Building Keystone From Source - Pt 2

Posted in KeystoneJS by Chris on August 04, 2016

Shortly after publishing the first part of this article, I discovered an much easier way to generate a fresh project with a git-able version of KeystoneJS. Normally I don't have much luck with using the yeoman generator, but this time everything seems to be building great. 

 

Follow steps 1 through 7 in the first part. From there, logged in as your normal user, run the following commands:

  • npm install -g generator-keystone
  • yo keystone
    (This will install the keystone generator)

  • node keystone.js
    (After installing keystone, run this command to ensure that Keystone will actually launch.)

  • cd node_modules
  • git clone https://github.com/keystonejs/keystone
  • cd keystone
  • npm install
    (Replace the default keystone directory with a clone of the KeystoneJS repository from GitHub)

 

Again, this worked for me on node version v4.2.6 and npm v3.5.2 . The result is the full demo install with a git-able KeystoneJS repository. For reference, the latest commit to the keystone master branch is this one.

Read more...

Building Keystone From Source

Posted in KeystoneJS by Chris on July 31, 2016

This article describes how to get the basic KeystoneJS engine up and running on a Digital Ocean Droplet running Ubuntu 16.04. Following these instructions will get you a minimally running KeystoneJS with a cloned repository that you can update through git, make changes, and subit pull requests with.

 

Be sure to start with part 2 as it has more up-to-date information.

 

These instrucitons document how to install the latest version of KeystoneJS from source using a Digital Ocean droplet running Ubuntu 16.04. If you use another host or operating system, you mileage may vary.

 

 

I wanted to create this list of instructions mostly for myself, but I also plan to post a link on the KeystoneJS mailing list. Getting a KeystoneJS installation to run successfully is one of the biggest hurdles for potential new developers. Even if you get the Keystone yeoman generator to work, it does not create a repository clone that you can update with git. The install described here is minimal. It does not include the Admin UI files that come with the yeoman generator. But you can copy the output files from the yeoman generator if you successfully get the Keystone core running using these instructions.
  

1. Create a new Digital Ocean Droplet with Ubuntu 16.04. It needs to be a $10 droplet at minimum with 1GB of ram. The $5 512MB RAM Droplet is not powerful enough to compile KeystoneJS.

 

2. Install Mongo DB by following this tutorial. No need to complete step 3, as MongoDB does not need to be accessible from the internet as we'll be interacting with it through KeystoneJS.

 

3. As root, install the following packages:

• apt-get install make

• apt-get install git

• apt-get install g++

 

4. As root, add a new user with sudo privledges.

• adduser trout

• adduser trout sudo

5. As root, install Node and NPM. You can refer to various methods described here

a. apt-get install -y nodejs-legacy

b. apt-get install -y npm

As of this writing, this installed node v4.2.6 and npm 3.5.2

6. Log in as your normal user. For this tutorial, I'll use the user 'trout'. From here, you should skip to step 11 and clone the repository containing the required libraries. For posterity, I've kept steps 7 through 10 to replicate the install from scratch as much as possible.

 

7. Install the yeoman generator:

a. Sudo npm install -g yo

It's important to run as sudo and use the -g option. Otherwise you won't be able to run the 'yo' command.

 

8. Install the express generator:

a. npm install generator-node-express

 

9. Create the directory that will contain the KeystoneJS system and cd into it. In this case, I'm calling the directory 'keystone-build'.

a. mkdir ~/keystone-build

b. cd ~/keystone-build

 

10. Inside the keystone-build directory, run 'yo node-express'. I used the following options. Ultimately though, none of them matter.

a. Project name: Keystone

b. Libraries: all

c. Editors: none

d. Browser: Chrome

e. Heroku: No

The code that represents the main file of your Keystone application is app.js.

 

11. Return to the parent 'keystone-build' directory. The following packages are needed by KeystoneJS but are not readily available in NPM, so I created a GitHub repository with the node_modules directory included. You can find it here. These libraries were copied from a working version of KeystoneJS.

a. serve-static

b. serve-favicon

c. body-parser

d. cookie-parser

e. multer

My GitHub repository captures all the work done between steps 7 through 11. It contains the above libraries and an app.js file for running Keystone. It will overwrite the keystone-build directory. The above libraries can also be extracted from the yoeman keystone generator output, even if it does not build a running version of KeystoneJS.

a. cd

b. git clone https://github.com/christroutner/keystone-build

 

12. Enter the node_modules directory and clone the KeystoneJS repository with this command:

a. cd ~/keystone-build/node_modules

b. rmdir keystone

c. git clone https://github.com/keystonejs/keystone

 

13. Run npm install inside the keystone directory:

a. cd ~/keystone-build/node_modules/keystone

b. npm install

 

14. Return to the parent 'keystone-build' directory. If you cloned the keystone-build repo, the app.js file will be ready to launch Keystone. This is a modified version of the code in the KeystoneJS Wiki.

a. node app.js

 

Read more...

Setting Up KeystoneJS in a Ubuntu 14.04 Droplet

Posted in KeystoneJS by Chris on December 08, 2015

Setting Up KeystoneJS CMS in Ubuntu

I wanted to play around with the KeystoneJS CMS. This is a simple, bare-bones CMS that is natively written in JavaScript. It took quite a bit of work to get it up and running, so this page documents the steps involved to get a real, live running version.

I could have developed this on the Raspberry Pi, but instead opted to use a virtual server hosted by Digital Ocean. They only cost $5 per month and the ability to save and restore snapshots is even faster than re-flashing the SD card on the R-Pi. Either way, all the setup and configuration was done on the command line using SSH.

So without further ado, here are the steps I needed to take to get KeystoneJS up and running on a Ubuntu 14.04 Droplet:

  1. Log in as root

  2. Setup Mongo DB by executing the following code: (adapted from this Digital Ocean tutorial)
    apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10
    echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.0.list
    apt-get update && apt-get -y upgrade
    sudo apt-get install -y mongodb-org

  3. Install the following support packages:
    apt-get install make
    apt-get install git
    apt-get install g++

  4. Add a local user. In this case I’m calling him ‘trout’:
    adduser trout
    adduser trout sudo

  5. This is a great time to shutdown the VM and take a snapshot. (optional)

  6. Log in as your new user.

  7. Install NVM (to run older versions of Node and NPM).
    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.29.0/install.sh | bash

  8. Log out and log back in as your regular user (‘trout’).

  9. Install Node 0.10.40:

    Note: This will give you warnings that node and npm are outdated. Ignore these warnings. 0.10.13 is too old to work and 0.12.7 (latest as of this writing) will break KeystoneJS because it is too new. There may be other versions that work, but these are what I’ve tested.

    nvm install 0.10.40

  10. Install the Yeoman Keystone Generator:
    npm install -g generator-keystone

  11. Run the Yeoman Keystone Generator:
    yo keystone

  12. Follow the on-screen prompts. Just hit Enter if you want to accept all quickly accept all defaults and test if you can even get KeystoneJS running.

  13. Change directory and run KeystoneJS:
    By default KeystoneJS will install into a directory called ‘my-site’.

    cd my-site
    node keystone

  14. Access your new website view port 3000. If you’re using a Digital Ocean Droplet like me, you can simply type in the IP address is your web browser like this: http://555.555.555.555:3000. If you’re using a Raspberry Pi, use the IP address of your Pi.

 

These are the steps that I was able to take and get KeystoneJS up and running. I hope it helps other people from having to spend two days figuring them out, like it took me.

Read more...

Uploading Images via the API - Part 2

Posted in KeystoneJS, JavaScript by Chris on December 06, 2015

In Part 1 I provided the routing files necessary to open up the KeystoneJS API for accepting images. I also showed how to create a simple HTML form for uploading images and pointed out some of the limitations of the API. In Part 2 I'll show how to simulate a form with JavaScript so that images can be uploaded via JavaScript code rather than an HTML form. I'll also show how to use the JavaScript Caman.js library to edit images on a HTML5 canvas element, then upload them to the server.

 

Uploading Images Via JavaScript

Here is a very simple HTML form. Notice that the Submit button is of type 'button' and not 'submit'. It also has the onclick property set to the JavaScript function uploadImage(). 

<form> 
  <input type="file" id="image_upload" onchange="displayUploadImage()"> 
  <br> 
<img id="imageToUpload" src="" />
<br> <input type="button" value="Submit" onclick="uploadImage()"> </form>

 

Check out this code while I walk you through it:

function uploadImage() {
var selectedFile = $('#image_upload').get(0).files[0]; var fileName = selectedFile.name;

//Create the FormData data object and append the file to it. var newImage = new FormData(); newImage.append('image_upload', selectedFile); //This is the raw file that was selected

var opts = { url: 'http://'+serverIp+':3000/api/imageupload/create', data: newImage, cache: false, contentType: false, processData: false, type: 'POST', success: function(data){ console.log('Image upload ID: ' + data.image_upload._id); } }; //Execute the AJAX operation. jQuery.ajax(opts);
}

The simple HTML form provides the user interface for retrieving a file. The first line of the JavaScript function assigns the selected file to to the selectedFile object. The object newImage is of type FormData, which is the JavaScript equivalent of a form. The selectedFile object is appended to it with the value 'image_upload'. This is all creating the JavaScript equivalent to the HTML form in Part 1.

Just as in our HTML form, we can't 'append' any values other than 'image_upload'. They will be ignored. Only the image can be uploaded. We can however update other values in the database with a second AJAX request once the image has been successfully uploaded.

The opts object further fleshes out the settings that our FormData object needs in order to upload the selected image correctly. KeystoneJS is very picky about its settings and it took me several hours to find the right combination above.

In the opts values, you'll see a function assigned to the 'success' key. When the image is uploaded, KeystoneJS will respond with the _id GUID it generates when creating the new database entry. It will return this value in response to the AJAX post request, so I display this value in the browser console. This value will be needed in order to update the image data with a second AJAX request. 

The last line in the code above executes the AJAX request for uploading the image.

 

Displaying a Thumb Nail Image

In order to begin visually editing the image, the first step is to display the selected file in the browser. The code below for doing that is borrowed from this MDN tutorial. It's really weird and I'm not totally comfortable with the way they are doing it, but the code works in Chrome and Firefox and it's very compact, which is nice. 

function displayUploadImage() {  

var imageFile = $('#image_upload').get(0).files[0]; var imageToUpload = $('#imageToUpload')[0];
//Display the selected image file in the browser var reader = new FileReader(); //Create a new FileReader object reader.onload = (function(aImg) { //These functions are executed when the FileReader return function(e) { //finishes reading the image file. aImg.src = e.target.result; //It displays the image file. }; })(imageToUpload); reader.readAsDataURL(imageFile); //Read the selected file with the FileReader.

//Replace the image with a canvas element and control it with Caman. Caman("#imageToUpload", function () { //This is where I would do image manipulation like resizing and cropping. //For now this is just a place holder. this.brightness(5).render(); });
}

After the image has been populated with the selected image file, the Caman function gets called to replace the image with an HTML5 canvas. Check out the CamanJS website, especially the example page that displays some of the in-browser editing you can do to an image. In the code above, all I do is adjust the brightness by 5%. The point is that this is the step where you'd edit your image: crop, scale, give the user a UI. Whatever you want.

 

Converting HTML5 Canvas to a File

So we've got two awesome ingredients: A way to upload image files to KeystoneJS and a way to edit images in the browser. How do we combine them? The answer to that my friends took me several hours to figure out. HTML Canvas elements are brand new and all the specifications around them have not been fully implemented into Chrome. The most common solution I found entailed converting the canvas image to ascii, uploading the ascii string to a server running a php script, and calling base64_decode() in the script. It was literally the only solution I could find after hours and hours of research, but I managed to come up with my own JavaScript based solution that avoids the use of PHP or extra server calls.

One of the HTML5 canvas specifications that haven't been implemented in Chrome yet is the .toBlob() function. This would be the easiest way convert the canvas to a file for upload via our JavaScript FormData object. The .toDataURL() function is widely implemented, which converts the canvas to an ascii string and results in all the nonsense with PHP and the base64_decode() function. However, the .toBlob() functionality is available as an external JavaScript library via this GitHub repository. Simply download the repository and include the canvas-to-blob.js file in your html. Thank you Sebastian Tschan!

Back to the uploadImage() function, Batman!

function uploadImage() {
var selectedFile = $('#image_upload').get(0).files[0]; var fileName = selectedFile.name;

//Below I'll createa file based on the manipulatd Canvas. var canvas = $('#imageToUpload')[0]; if (canvas.toBlob) { //Ensure the toBlob library is loaded canvas.toBlob( handleCanvasBlob, selectedFile.type ); } else { console.error('Could not access toBlob library!'); return; }
}


function handleCanvasBlob(blob) { var the_file = new File([blob], fileName, {type: blob.type}); //Create the FormData data object and append the file to it. var newImage = new FormData(); newImage.append('image_upload', the_file); //This is the raw file that was selected var opts = { url: 'http://'+serverIp+':3000/api/imageupload/create', data: newImage, cache: false, contentType: false, processData: false, type: 'POST', success: function(data){ console.log('Image upload ID: ' + data.image_upload._id); } }; jQuery.ajax(opts); }

 

In the uploadImage() function, I add a call to the .toBlob() function. The handleCanvasBlob() function handles the callback when the browser finishes converting the canvas to a blob. The line new File([blob], fileName, {type: blob.type}); converts the Blob object into a File object. The code below that is just a copy of what was originally inside the uploadImage() function, which handles the file upload to KeystoneJS.

One thing to watch out for is that the filename does not survive this manipulation process. Notice that I call the object fileName in the File() constructor. This is a global variable that I used to store the file name.

 

Combining the information in Part 1 and Part 2, you can now allow users to upload an image, perform on-the-fly image editing, and then submit that image to KeystoneJS. Have fun!

Read more...

Uploading Images via the API - Part 1

Posted in KeystoneJS by Chris on November 25, 2015

This week I struggled with implementing image uploads via the API. The first part of the struggle centered on hosting images locally. The second part focused on getting the uploads to work via the API.

 

Hosting Images Locally

Out of the box, KeystoneJS uses Cloudinary for image hosting. They are a fine service, but in my experience the life expectancy of internet companies is much lower than the lifetime of many websites I build. I really wanted to figure out how to host images locally. There were a lot of false starts on this aspect of the project, but ultimately I found the LocalFiles data type mentioned in the Keystone Database Documentation. That allowed me to create a new database model for uploading images.

The magic of KeystoneJS is that once you get the model coded correctly, the interface for it automagically appears in the Admin UI. That's great if you are building a site for yourself. However my primary focus on KeystoneJS is to use it as a CMS for non-tech-savvy clients. Turning them loose in the Admin UI could lead to all sorts of issues. That's why I wrote the tutorial on opening up the API, so that I could design custom back end pages to walk clients through the update of their sites. 

 

Image Upload via the API

Uploading images via the API was not very straightforward, despite the fact that several people had resolved this issue before me. I found several historical threads on the KeystoneJS group mailing list asking for help with this same aspect. Unfortunately none of them came back to document a clear solution to their problem. Hopefully that will be resolved with this blog post. 

Long story short, here are the routes/index.js and routes/api/imageupload.js files I put together to solve this issue. The export.create subfunction in the imageupload.js file is the only subfunction that differs greatly from the front end widget file used in the API tutorial. Here is an example of a simple HTML form that can upload images via the API once those files (and the model) are in place:

<form action="http://<your ip>:3000/api/imageupload/create" method="POST" enctype='multipart/form-data'> 
  <input type="file" id="image_upload"> 
  <br> 
  <input type="submit" value="Submit"> 
</form> 

The important caveats of the code are:

  • Based on historical mailing list threads, my understanding is that both the field name in the routes/api/imageupload.js file and the name of the HTML input field need to be named <field>_upload. In my case 'image_upload'. I might be wrong about both of them needing this, but that's what worked for me.

  • The <form> enctype must be set as 'multipart/form-data'.

  • I can only upload the image. If I try to set other features like the name or alt-tag, the POST request will puke. I plan to get around this by having two steps in my front end JavaScript. The first step will upload the image. The second step will update the image data with name, alt tag, ect. Again, there may be a work around for this. I just haven't found it yet.

That's the gist of how I got image uploads to work via the API. Check out this thread and this thread where I recorded some of the historical solutions I found and that summarize the information in this post. 

Read more...

Adding Blog Comments

Posted in KeystoneJS, JavaScript by Chris on November 24, 2015

KeystoneJS does not come with comments for the blog. I wanted to add some so that I can get feedback on my tutorials. I eventually decided to roll with the Disqus comments you see below. It wasn't my first pick, but ultimately, it was the fastest and easiest way to roll.

I started by reaching out to the KeystoneJS Google Group with this thread. A reply to that thread led me to this commit in the sydjs-site repository. I started trying to impliment comments this way, but I am not a fan of server-side rendering. I realize this is a bit of a contradiction since I'm such a KeystoneJS fan and it's *big* on server side rendering. Honestly, I probably would have passed on using KeystoneJS if I hadn't found out how to open the API so that I could pull data from the CMS and do client-side rendering with it.

So true to my nature, I started steps very similar to creating a Front End Widget in order to deal with comments. Here is a pastebin of the model I created in model/PostComment.js. I created an API for accessing the model the same way I did for the Front End Widget. I began doing the front end coding for adding and removing comments to blog posts when a though occured to me: "Can I really do this better than some 'free' option?"

I paused right there and started to look for embeddedable comment options. The two big contenders were Disqus and Discourse. Discourse has the disadvantage of requiring people to navigate away from a page and go to a Discourse forum page to leave a comment. I really didn't want that. I used Disqus when they first came out and it left a bad taste in my mouth. They were really spammy, but so far, I'm pretty happy with the lack of spam. Plus, they let people log in with social network credentials like Google and Facebook, and they integrate comment voting. Both very cool features that I like in the modern web.

So for better or worse, I went with Disqus. It was an easy decision and easy to impliment. I just added their code to the bottom of my templates/views/post.hbs file. Just above their embed code snippet, I added this code: 

 

{{#if data}}
  <script type="text/javascript">
    var postUrl = 'http://christroutner.com/blog/post/{{data.post.slug}}';
    var postSlug = '{{data.post.slug}}';
  </script>
{{/if}}

 

I was then able to feed the postUrl and postSlug variables into the default Disqus embed code like this:

 

...
var disqus_config = function () { this.page.url = postUrl; // Replace PAGE_URL with your page's canonical URL variable this.page.identifier = postSlug; // Replace PAGE_IDENTIFIER with your page's unique identifier variable };
...

 

Read more...

Front End Widgets - Part 5 - Password Protecting the Artificial Back End

Posted in KeystoneJS, JavaScript by Chris on November 13, 2015

Table of Contents:

 

The front end side of things is pretty straightforward. The code in part 3 can be copy and pasted into any of the *.hbs Handlebar template files in the templates/views/ directory. These are the front end templates that KeystoneJS serves up. For instance, templates/views/index.hbs is the template for the home page.

You could do the same thing with the Artificial Back End, but then anyone with the URL will be able to maliciously update your widgets. I haven't figured out a way to password protect the API yet, so hopefully someone in the KeystoneJS group will chime in with a good way to do that. However, you can at least protect the Artificial Back End.

As an aside, here is what I've tried to password protect the API, but it didn't work. I have tried to change this line in routes/index.js:

app.all('/api/frontendwidget/:id/update', keystone.middleware.api, routes.api.frontendwidget.update);

to this:

app.all('/api/frontendwidget/:id/update', middleware.requireUser, routes.api.frontendwidget.update);

which seems to be the obvious way to password protect the API, but it errors out. We can however, do the same thing to password protect the page. In order to do that, add this line to routes/index.js:

// Setup Route Bindings
exports = module.exports = function(app) {
...
app.get('/updatefrontend', middleware.requireUser, routes.views.updatefrontend); };

 

That will create a password protected route to our new page. Now, create the file routes/views/updatefrontend.js and paste this code into it:

var keystone = require('keystone');

exports = module.exports = function(req, res) {

        var view = new keystone.View(req, res);
        var locals = res.locals;

        // Set locals
        locals.section = 'updatefrontend';

        // Load the galleries by sortOrder
        view.query('frontendcollections', keystone.list('FrontendWidget').model.find().sort('sortOrder'));

        // Render the view
        view.render('updatefrontend');

};

 

Finally, paste the html from Part 4 into the file templates/views/updatefrontend.hbs to complete the process. This hbs file is the html that KeystoneJS will serve. The page can be accessed from http://<your ip>:3000/updatefrontend and the page will require that you be loggined in as an Admin user.

Read more...

Front End Widgets - Part 4 - Editing the Widget on an Artificial Back End

Posted in KeystoneJS, JavaScript by Chris on November 10, 2015

Table of Contents:

 

Updating widget data through the KeystoneJS API is very similar to retrieving it. Copy and paste the below code into an HTML file, update the server IP, and open it in a browser. It will generate a simple form that will allow you to update the URL of the first image in the first widget.

<!DOCTYPE html>

<!--
Note: You'll need the bootstrap css and js files and change the paths to them below.
-->

<html lang="en">
<head>
  <title>KeystoneJS API Example</title>

  <link href="css/bootstrap.min.css" media="all" rel="stylesheet" />
</head>
<body>


  <section>
    <div class='container'>
      <div class="row well well-lg">
        <div class="col-md-12">
          <p>Image from first widget:</p>
          <img id="image1" src="" alt="" />
          <br><br>
        </div>
      </div>
      
      <form action='' method="post" name='updateImgForm' id='updateImgForm' enctype='multipart/form-data'>
        <div class='form-group' >
          <div class='col-md-12'>
            <label for='widgetUrl'>URL:</label>
            <input class='form-control' type='text' id='widgetUrl' size="36">
          </div>
        </div>
        <div class='form-group' >
          <div class='col-md-4'>
              <button class="btn btn-default" type='button' id='oldUrlBtn' onclick='updateWidget()'>Update URL</button>
          </div>
          <div class='col-md-4'>
              <span id="checkMsg1">Click button to update widget URL.</span>
          </div>
          <div class='col-md-4'>

          </div>
        </div>
      </form>
      
    </div>
  </section>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="js/bootstrap.min.js?body=1"></script>

  <script type="text/javascript">
    //Global Variables
    var serverIP = '<your ip here>';
    var JSONData;
    
    //First function that is called when the document finishes loading.
    $(document).ready( function() {
    
      //Call server to retrieve JSON Gallery data.
      $.getJSON('http://'+serverIP+':3000/api/frontendwidget/list', '', processJSON);
    });
    
    //Callback function executed when JSON data is returned.
    //Sorts the items in data.collections into different categories.
    function processJSON(data) {
      debugger;
      //Fill in the images with data from the KeystoneJS API
      $('#image1').attr('src', data.collections[0].url1);
      $('#image1').attr('alt', data.collections[0].alt1);
      $('#widgetUrl').val(data.collections[0].url1);
      
      JSONData = data; //Copy to global varible.
    }
    
    //This function is called when the 'Update URL' button is clicked.
    function updateWidget() {
      var collectionId = JSONData.collections[0]._id; //GUID of the first widget
      
      //Update the JSON data with the data from our form.
      JSONData.collections[0].url1 = $('#widgetUrl').val();
      
      //Send the JSON string to the server and log a copy on the console.
      console.log('JSON data sent: '+JSON.stringify(JSONData.collections[0])); //Used for debugging.
      $.getJSON('http://'+serverIP+':3000/api/frontendwidget/'+collectionId+'/update', JSONData.collections[0], validateChange);
    }
    
    //This function handles the AJAX callback from the server after a POST request has been sent to update the widget.
    //The purpose of this function is to validate that the data returned from the server matches the changes made.
    function validateChange(data) {
      
      //If the returned data matches the data in our form, then report a success.
      if( data.collection.url1 == $('#widgetUrl').val() ) {
        $('#checkMsg1').text('Widget successfully updated!');
      } else {
        $('#checkMsg1').text('Update failed!');
      }
    }
    
  </script>
  
</body>
</html>
 

 

Here's what the HTML page looks like:

To really study the code, I suggest you insert debugger statements at the top of each function and then run through the code line by line in Chrome Dev Tools. That way you can see the ways the API URL and other variables are getting manipulated.

The API is very picky about the format of the JSON, so it's best to simply manipulate the JSON returned by the server and then feed it back. If it doesn't like something, it won't complain, it simply won't update the widget. You can test if your changes were rejected by testing the returned JSON data against the manipulated JSON data. 

Read more...

Front End Widgets - Part 3 - Displaying the Widget on the Front End

Posted in JavaScript, KeystoneJS by Chris on November 08, 2015

Table of Contents:

 

The first step in displaying data on the front end is to open up Cross Origin Resouce-Sharing (CORS). Add the following lines to the file node_modules/keystone/lib/middleware/api.js

exports = module.exports = function(keystone) {
        return function initAPI(req, res, next) {

                //Add CORS
                res.header("Access-Control-Allow-Origin", "*");
                res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");


                res.apiResponse = function(data) {
...

 

There is a cors.js file in that directory that you could point the route to, but I had trouble in trying to execute it. In reality you only need to add the above two lines. Please note that there are security issues surrounding the concept of CORS. You should Google it and have a thorough understanding of the risks involved. 

Once again, you'll need to reboot KeystoneJS to make the changes go into affect.

 

Manipulating Front End Objects Through The API

Paste the following HTML into a file and open it in your browser. Note that you'll need to download the bootstrap css and js files to make it work correctly. This gives you an example using jQuery and the API to dynamically manipulate front end objects with data retrieved from the API.

 

<!DOCTYPE html>

<!--
Note: You'll need the bootstrap css and js files and change the paths to them below.
-->

<html lang="en">
<head>
  <title>KeystoneJS API Example</title>

  <link href="css/bootstrap.min.css" media="all" rel="stylesheet" />
</head>
<body>


  <section>
    <div class='container'>
      <div class="row well well-lg">
        <div class="col-md-12">
          <img id="image1" src="" alt="" width="600px" />
          <br><br>
          <img id="image2" src="" alt="" width="600px" />
          <br><br>
          <img id="image3" src="" alt="" width="600px" />
        </div>
      </div>
    </div>
  </section>

  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="js/bootstrap.min.js?body=1"></script>

  <script type="text/javascript">
    //Global Variables
    var serverIP = '<your ip address>';
    
    //First function that is called when the document finishes loading.
    $(document).ready( function() {
    
      //Call server to retrieve JSON Gallery data.
      $.getJSON('http://'+serverIP+':3000/api/frontendwidget/list', '', processJSON);
    });
    
    //Callback function executed when JSON data is returned.
    //Sorts the items in data.collections into different categories.
    function processJSON(data) {
      //Fill in the images with data from the KeystoneJS API
      $('#image1').attr('src', data.collections[0].url1);
      $('#image1').attr('alt', data.collections[0].alt1);
      $('#image2').attr('src', data.collections[1].url1);
      $('#image2').attr('alt', data.collections[1].alt1);
      $('#image3').attr('src', data.collections[2].url1);
      $('#image3').attr('alt', data.collections[2].alt1);
    }
    
  </script>
  
</body>
</html>
 

Read more...

blog data loaded.