Cloud Connected

Thoughts and Ideas from the Gitana Development Team

* Showing search results for tag: Preview

Setup a Next.JS Static Site using Cloud CMS

The web application ecosystem has improved leaps and bounds over the years, and its never been easier to make high performance static/hybrid sites quickly and using good development practices. And its just as easy to get these sites going using Cloud CMS as a datasource! Below you'll find a quick guide to get you started using Next.JS with Cloud CMS to setup a simple bookstore website, and some highlights from the code.

Getting Started

First, you'll need to have a Cloud CMS account and Sample project setup to use as your site's datasource. If you don't have an account, sign up for a trial! Then, navigate to your cloudcms platform by going to https://{subdomain}.cloudcms.net. When you get there, click create project, and in the popup menu select the sample project.

create_project.png

Once the project is created, feel free to click around to get a sense of the content model. The main types that we are interested in are store:book and store:author, which you can check out from the Content menu.

content_cloudcms.png

Credentials

In order to connect Cloud CMS to an external application, we will need to get a set of API keys that this application can use to secure its communication. Click on Manage Project at the top of the screen, and then find API Keys from the left hand menu.

manage_project.png

api_keys_nav.png

The sample project includes a default set of API keys that we can use here, click on gitana.json under Files on the right and copy the JSON that pops up.

api_keys.png

Copy this to your clipboard, we will need to paste this into a gitana.json file at the top level of our Next.js site's source code.

Pulling the SDK

Now that we are setup with some content in Cloud CMS, we can start setting up the site where this content will live. We've already got this ready for you to check out as part of our SDK: clone this with your Git client of preference go to the nextjs/sample directory. Here, create a gitana.json file and paste in the credentials we copied from Cloud CMS

next_files.png

Ensure that you are using the latest Node.js LTS (at the time of writing, this is version 16.13.1). From here, install the dependencies by running npm install

Trying it Out

You are now ready to test the site! Run npm run dev to start the Next development server, and go to http://localhost:3000 to see the sample project on a page.

site.png

Since you are running this on a development server, any content changes you make in Cloud CMS will also change the content in your site, feel free to try it out!

You can also build an optimized static version of your site by running npm run build, which will download all content needed to a single directory. Note that this version will not update with any content changes until your next build.

Exploring the Code

The Next.JS sample project contains three main directories of interest:

  • lib - Contains cloudcms.js, which contains functions to connect and interface with cloudcms using some customizations to the Cloud CMS Javascript Driver. This file is a good starting point to copy into your own project when building out data queries.
  • pages - Next.JS pages which contain React code as well as calls to getStaticProps which run queries to Cloud CMS serverside
  • components - React JS components used to display the content - these don't contain anything that interact with Cloud CMS directly and receive data from the pages they reside in

Custom Session

lib/cloudcms.js makes a number of modifications to the default Cloud CMS Javascript Driver functions by means of a custom session. It adds the following:

  • createAttachmentLink function which downloads or streams attachments from Cloud CMS into Next, and returns a URL which references it
  • Extensions to various node querying/read functions to allow default attachments to be available as defaultAttachmentUrl
  • getCurrentBranch function which takes the Next JS context and returns the branch that is being viewed based on preconfigured defaults or a branch being previewed.

This session gets automatically used by the exported helper functions:

  • connect - returns a session object that can make any calls against your Cloud CMS tenant.
  • getCurrentBranch returns a branch object, which binds functions from the session with information about the branch relevant to the current context.

Use these functions in your pages to quickly get connected to Cloud CMS and start requesting data.

Querying Data in Pages

Let's look at pages/index.js, the code for the home page of our site. In it we use getStaticProps to fetch the first four books and authors:

import { getCurrentBranch } from '../lib/cloudcms';
// ...

export async function getStaticProps(context)
{
    const branch = await getCurrentBranch(context);
    let books = (await branch.queryNodes({ _type: "store:book" }, { limit: 4 })).rows;
    let authors = (await branch.queryNodes({ _type: "store:author" }, { limit: 4 })).rows;

    return {
        props: {
            books: books,
            authors: authors
        }
    }
}

A resulting book, for example, will look like:

{
    _doc: '4e6ab409f6c50f2ef9f8',
    _features: {
      'f:audit': {},
      'f:titled': {},
      'f:filename': [Object],
      'f:geolocation': {},
      'f:indexable': {},
      'f:taggable': [Object],
      'f:deployment-config': [Object],
      'f:knowledge-recipient': [Object],
      'f:related': {}
    },
    _qname: 'o:4e6ab409f6c50f2ef9f8',
    _type: 'store:book',
    author: {
      id: '02369a026d5b5d69b36c',
      ref: 'node://92290c964a6ed8a0d29f/4ba62478c5658b735e0c/f5a325600af0468c021b/02369a026d5b5d69b36c',
      qname: 'o:02369a026d5b5d69b36c',
      typeQName: 'store:author',
      title: 'William Shakespeare'
    },
    description: "Macbeth (the Scottish Play) is among the best-known of William Shakespeare's plays, and is his shortest tragedy, believed to have been written between 1603 and 1606.",
    recommendations: [ [Object], [Object] ],
    slug: 'macbeth',
    summary: "Macbeth is among the best-known of William Shakespeare's plays, and is his shortest tragedy, believed to have been written between 1603 and 1606. It is frequently performed at both amateur and professional levels, and has been adapted for opera, film, books, stage and screen. Often regarded as archetypal, the play tells of the dangers of the lust for power and the betrayal of friends. For the plot Shakespeare drew loosely on the historical account of King Macbeth of Scotland by Raphael Holinshed and that by the Scottish philosopher Hector Boece. There are many superstitions centred on the belief the play is somehow 'cursed', and many actors will not mention the name of the play aloud, referring to it instead as 'The Scottish play'.",
    tags: [ 'tragedy', 'macbeth', 'shakespeare', 'war', 'royalty' ],
    title: 'Macbeth',
    _is_association: false,
    defaultAttachmentUrl: '/_next/static/cloudcms/4ba62478c5658b735e0c/f5a325600af0468c021b/4e6ab409f6c50f2ef9f8/default.jpeg'
}

This will be made available as a property in the page that can be passed to components.

The call to getCurrentBranch will give you the master branch of your project by default, and this will allow you to query, search, and read nodes, and even make updates to your content if needed. Feel free to take a look at some of the other pages to see other example reads and queries.

Previewing

One of the cool aspects of Cloud CMS is its branch based versioning system - which allows you to work on different versions of the same content, and the Next.JS sample allows you to preview different branch versions in context, without any deploying!

In pages/api/preview.js, the endpoint /api/preview is defined which takes query parameters url, branch, and repository. The endpoint redirects to url after setting branch and repository (both are optional) as preview variables and will turn on preview mode. When in preview mode, the preview variables for branch and repository will be used with priority over the defaults configured for your application.

For example, this link will preview the homepage (route /) for content on branch myBranchId:

http://localhost:3000/api/preview?branch=myBranchId&url=/

We can set Cloud CMS to provide preview links that automatically inject these variables into the paramters base on what branch a user is editing in. To do so, we need to configure a preview server. Go to Manage Project -> Preview Servers, and select Add Preview Server. From here, provide and ID and title, set the type to URL, and provide the following URL:

http://localhost:3000/api/preview?branch={{branch.id}}&url=/

preview_server.png

In order to see links in the editor, you will also have to ensure publishing is enabled. In Manage Project, go to Publishing, and ensure Enable Publishing for this Project and Show Preview Buttons are checked.

publishing_settings.png

When you go to a content item, you will see the option to view that content in your preview environment. This will set a Next preview cookie in your browser, so all subsequent navigations in this preview browser will contain content from the branch you are previewing.

instant_preview.png

Deploying to Vercel

Next.JS provides a great deal of vertsatility with where and how it can be deployed in production. One of the great options for this is provided by Next's managing company, Vercel.

For this part, you will need a Vercel account and for it to be connected to a remote Git service like Github or Gitlab. You will also need to setup a new git repository for your Vercel project to link to. For this example, I will be pushing a version of the Next.js app to a github repository.

github_files.png

Go to Vercel and create a new project. If you do not see your new repository listed as an option for creating the project, you may need to adjust your Github permissions so that Vercel has access to your repository.

Now we are prompted to configure the project. If you uploaded the entire SDK rather than just the Next.js sample to github, be sure to set the root path to something like ./nextjs/sample. Instead of authenticating using keys from gitana.json, we will use environment variables to pass this information securely in the serverless environment. Be sure to provide values for CLOUDCMS_CLIENT_KEY, CLOUDCMS_CLIENT_SECRET, CLOUDCMS_USERNAME, CLOUDCMS_PASSWORD, CLOUDCMS_BASE_URL, and CLOUDCMS_APPLICATION.

vercel_config.png

After setting these, you should be ready to click deploy, and within a few minutes your site will be ready to view.

vercel_deployed.png

Wrapping Up

By now you should have a good understanding of how can start with an editor being input into Cloud CMS, and end up as text and images in a modern web application, and how simple the process can be! From here you can continue to explore the sample project by adding/editing content, adding branches and seeing how they differ using the preview system, or even edit the content model to add fields to add more displayed information to the sample site. Then, take what you've learned and start building your own awesome Next.JS applications with Cloud CMS.

Content Previews and Thumbnails with Cloud CMS

Cloud CMS lets you generate preview images (often called thumbnails) for any content item stored in your repository. This generation can be performed ahead of time via content modeling or it can be done in real-time using a simple URL call.

Content Nodes

In Cloud CMS, everyone content item you create is referred to as a node. A node is a JSON document that can have any structure you’d like.

That is to say, you can drop any valid JSON document you’d like into Cloud CMS and the product will automatically capture it, version it, index it and so forth. It’ll also give your node a unique ID (provided by the _doc field).

Every node lives on a repository branch. You might be working on repository repo1 and branch master. You might use the Cloud CMS Console to create a simple JSON node:

image

When we create this node, Cloud CMS automatically gives us a Node ID.  

image

In this case, it gives us d9cfc0491de1a63b6e8e. It is available in our JSON document and we can use this ID to reference our node anytime we want to. 

Note: Fans of the 80’s will no doubt identify the absurdity of a document promoting a movie that should not be made.  Still, we’re here to have some fun, so let’s go with it!

REST API

The Cloud CMS REST API lets us work with Cloud CMS content via HTTP commands. If we’re authenticated against our Cloud CMS platform or if our platform offers guest-mode access, we can reference our node like this:

This will give us back our JSON document. It’ll also give us a few additional and interesting properties.

{
   "_doc":"d9cfc0491de1a63b6e8e",
   "_features":{
      "f:audit":{},
      "f:titled":{},
      "f:filename":{
         "filename":"Back to the Future part IV"
      },
      "f:geolocation":{},
      "f:indexable":{}
   },
   "_qname":"o:d9cfc0491de1a63b6e8e",
   "_type":"n:node",
   "description":"Yes, they're back.  In Time.",
   "destination":1985,
   "title":"Back to the Future part IV",
   "year":2015
}

We won’t delve into this too deeply. But it suffices to say that our new node has automatically been given a dictionary type n:node and a definition qname o:d9cfc0491de1a63b6e8e. This allows us to use Cloud CMS’ content modeling facilities (if we choose to) to provide validation logic and behaviors on top of our content.

We also see that it has been outfitted with a number of features such as f:audit, f:geolocation and f:indexable. These features inform the behavior of our content and tell Cloud CMS that:

  • access to this content object should be recorded in the audit logs for our platform
  • a geolocation index should be maintained for this content so we can look it up by latitude and longitude (if desired)
  • this content should be indexed for search

Each feature can be configured. By default, no configuration is provided which allows Cloud CMS to fall back to its default way of handling each feature.

Attachments

Now on to preview images.

Every node in Cloud CMS supports zero or more attachments. An attachment is a binary object that is associated with the node.

You can think of attachments kind of like you think of attachments for an email. Every email can have many attachments. And the same is true for nodes. A node might have zero attachments. If so, we think of this as “content-less” content. Nothing wrong with that. However, other nodes might have a single attachment.

For example, a Word document would be stored in a node (with some JSON) and have a single default attachment which contains the Word document itself.

Suppose we upload a Word document as an attachment for our node. The word document might provide a movie description along with some images.

image

We can then reference this Word document via HTTP:

This will stream the Word document back to us.  Very useful!

Previews

Now let’s suppose that we have a web site. And on our web site, we’d like to provide a link to this Word document along with a thumbnail for purposes of preview. It’s easy. We just let Cloud CMS do the thumbnail generation for us.

This will generate a thumbnail image of the default attachment with a max width of 320 in image/jpeg format. Cloud CMS will produce a preview image of the first page of the Word document.

The preview image will be saved as a new attachment on the node. The new attachment will be called thumb320. The thumbnail will only be generated on the first call. Subsequent calls will re-use the previously generated attachment.

Options

The URL-based preview generator provides all kinds of neat options. Using request parameters, you can adjust how your preview attachments are generated and maintained. Here are some of the options:

  • mimetype - specifies the preview mimetype to be generated. If not specified, assumed to be image/jpeg. However, you could specify other image formats or even other document formats such as application/pdf.
  • size - specifies the maximum width of the resulting image (when mimetype is an image format).
  • attachment - specifies the attachment on the node to be previewed. If not specified, assumed to be default.
  • force - specifies whether the preview attachment should be re-generated in the event it already exists. If not specified, assumed to be false.
  • save - specifies whether the generated preview attachment should be saved as a new attachment on the node. If not specified, assumed to be true. If you set this to false, each request will generate the preview anew.
  • fallback - specifies a fallback URL that should be 301 redirected to in case the preview does not exist or cannot be generated.

Examples

Cloud CMS uses preview generation in many places. Fundamentally, we use this quite extensively in all of our HTML5 front-end applications as a means for generating intuitive user interfaces. Here are a couple of really neat places where it shows up: 

Real-time Document Transformations

Suppose we wanted to offer the default attachment (Word document) as a PDF. After all, PDF is a much preferred format for consumption. Businesses tend to prefer Word for the back-office and PDF for the consumer. We can do this by using a link like this:

Multi-Device / Multiple Form Factors

Suppose we want to support lots of form factors. We might want to have a hi-res preview for large form factors (like a kiosk) and much smaller images for devices like an iPhone. We may even want different mimetypes based on bandwidth. We can adjust our URLs accordingly.

Here are a few examples:

imageimageimageimage

Fallback

Sometimes we may not know whether a preview can/could be generated for content. Suppose, for example, that we want to produce a list of nodes. For content-less nodes, we might provide a stock image that use as a placeholder. The preview generation code lets you fallback to a URL of your choosing. You can do it like this:

Conclusion

As Cloud CMS grows and evolves, we’re pushing further into services and capabilities that support live applications.  The platform’s document management and transformation services are powerful capabilities that stand ready to service your web and mobile apps!

Are you new to Cloud CMS?  If so, consider giving us a try by signing up for a free trial today.

Introduction to Changeset Versioning

Cloud CMS provides you with content repositories that are powered by a “changeset” versioning model.  This a powerful versioning model that you won’t find in most conventional CMS products.  It’s one of the reasons why Cloud CMS is such a great platform for collaboration!

Document-level Versioning

A lot of legacy CMS products feature document-level versioning.  With document-level versioning, when you make a change to a document, the system simply increments a version counter.  You end up with multiple versions of your document.

It might look something like the following:

We all have or had an awesome grandparent who knew how to cook something good. For a recipe stored in a Microsoft Word file, the document-versioning model works pretty well!

Problems with Document-level Versioning

That said, there are some major drawbacks.

  1. Desktop Documents Only.  Document-level versioning is really only good for desktop documents (like Microsoft Office files) where everything (all of your nested images, fonts, etc) are contained within a single file.

    That’s why Dropbox uses file-level versioning.  It makes sense for people who work almost exclusively with desktop documents.
     
  2. No way to handle Sets of Changes.  If you’re working on mobile applications, web sites, or just about any non back-office projects, your content will be spread over multiple files.

    Think about a web site.  A web site might have hundreds or thousands of files - things like HTML, CSS, JS, image files and much more.  When you publish a web site, you really want to version the full set of files all at once so that you can push, pull and roll back updates to your web site.
     
  3. Bottlenecks.  If you’ve ever worked with Microsoft Sharepoint or any document-versioning CMS, then you’re aware of the bottlenecks that get introduced when two people want to work on something at the same time.  Either they both make changes (and you have to manually merge them together) or one person locks the file and the other person is sits on their hands.

    Most products that feature document-level versioning do so simply because it’s easy to implement.  However, it leaves your business users with the extremely limited tools for collaboration.  This makes collaboration frustrating as it cuts off people’s initiative, creativity and productivity.
     
  4. No ability to scale.  Okay, so let’s suppose now that you want to scale your content ingestion and production capabilities out to the broader world.  You might want to pull in content from Twitter, Facebook or Quora in real-time.  And let a broad community collaborate together…

    Nah, forget it.  With document-level versioning, that’d be like give everyone a phone and telling them to call each other.

    And then only giving them one phone line.

Changeset Versioning

Fortunately, this problem has been solved.  The solution comes out of the source control world and it is known as distributed “changeset versioning”.

If you’ve ever used Git, Mercurial or any modern source control software, then you’re already familiar with the concept.  It’s been around for awhile and has become extremely popular since it enables folks to work unimpeded, fully distributed and without any of the headaches of file locking and so forth.

It should be noted.  Cloud CMS is the only Content Management System to offer changeset versioning.  We’re it.  Why?  I suppose because it is hard to implement.  

And maybe because everyone else is busy chasing the desktop document problem.  However, if you’ve ever try to build a web or mobile app or tried consuming social content from Twitter, Facebook, LinkedIn, etc… well, then you know it’s all about JSON, XML, object relationships, lots of composite documents, highly concurrent writes and reads and so on!

Only your sales person will believe that a document-versioning system could be used for that purpose!

Changeset Versioning: The Basics

This article by no means intends to provide a Masters thesis on how changeset versioning works.  However, lets delve into the basics!

Let’s start with writing, editing and deleting content.  

When you write content into the Cloud CMS repository, your content gets stored on a “changeset”.  A changeset is a lot like a transparency (from the old transparency projector days).  This is a see-through sheet of plastic that you write on with one of those Sharpie pens.  The projector projects whatever you write up onto the screen.

The cool thing about transparencies is that you can layer them, one on top of the other.  What ends up getting projected is the composite of everything layered together.

So when you write content, the repository basically gets a new transparency and puts your content onto it.

If you make a change, it gets out another transparency, writes your change and layers it on top.

It also does this if you delete something.  It gets out a new transparency, masks (or covers up) your content so that it appears deleted.  

However, your content isn’t really deleted.  It is safe and tucked away somewhere in the stack of transparencies.  It’s just been hidden by the top-most transparency!

You can write as many things onto a changeset (transparency) as you want.  Cloud CMS manages the changesets for you, keeps them in a nice stack and lets you roll back changes if you make a mistake anywhere along the way.

Changeset Versioning: Branches and Merges

As noted, Cloud CMS manages your changesets for you.  The “stack” of changesets is known as a Branch.  As you add more changesets to the branch, the length of the branch gets longer (just like the stack of transparencies gets thicker).

A read operation simple pulls information out of the repository.  A write or a delete adds a new changeset.  Consider the branch shown below.  The reading operation just peeks at the branch looking down from the top.  The writing operation adds a new changeset.

With just a single branch, you can still get into the situation where two people want to change the same file at the same time.  Cloud CMS lets you lock the object and all that kind of thing if you want.  Or, you can create new branches so that everyone can work together at the same time and on the same things.

It kind of looks like this:

Here we have two workspaces.  Each workspace has its own branch which was stemmed off of the Master Branch at changeset V5.  The first user works on Branch A and the second user works on Branch B.  Both Branch A and Branch B have a common ancestor (changeset V5 in the Master Branch).

This allows both users to do whatever they want without stepping on each other’s toes. They can update documents, delete things and create new content.  At any time, they can push and pull changes between their workspace and any other workspace.  This gives them a way to preview what other people are working on and merge their work into their own branches.  They can also merge back to the Master Branch.

Cloud CMS provides an elegant merge algorithm that walks the changeset history tree from the common ancestor on up.  It uses a JSON differencing algorithm to allow for JSON property-level conflicts (as opposed to document level conflicts).  And it provides content model and scriptable policy validation for the merged result.

The result is a highly collaborative experience that encourages your users to experiment and take a shot at contributing without the worry of blocking others or screwing up the master content.

In a future blog, we’ll cover the details of how branching and merging works.  Our approach is one that did not seek to reinvent the wheel but rather ride on top of the wonderful innovation that has already occurred over the last decade within source control tools like Mercurial, Git and Bazaar.