Back to top

Sources

Create a custom Storify source to allow you to more easily search for content from your selected service then drag and drop it into a Storify story.


Source definition

The source definition defines all properties for the source, including:

  • the different tabs,
  • the form for each tab (if any),
  • the API endpoint to call for each tab once the form is submitted with some values, and
  • a mapping table to normalize the results provided by the API endpoint.

Once the object is dropped onto the story, another optional API endpoint is called to retrieve extra information about the object.

The following is the source definition for Google, which may be used as a template and adapted to your needs.

{
    "name": "google",
    "label": "Google",
    "icon": "http://storify.com/public/img/sources/google-24px.png",
    "href": "http://www.google.com",
    "defaults": {
      "results": {
        "loadMore": {
          "attribute":"start",
          "inc":8
        },
        "array": "json.responseData.results",
        "mapping": {
            "type": "link",
            "permalink": "{{result.unescapedUrl}}",
            "posted_at": "{{result.publishedDate}}",
            "data": {
                "link": {
                    "href": "{{result.unescapedUrl}}",
                    "title": "{{result.titleNoFormatting}}",
                    "description": "{{result.content}}",
                    "thumbnail": "{{result.thumbnail}}"
                }
            },
            "attribution": {
                "name": "{{result.visibleUrl}}"
              , "href": "{{result.unescapedUrl}}"
            },
            "source": {
                "name": "{{result.visibleUrl}}"
              , "href": "{{result.unescapedUrl}}"
            },
            "meta": "{{result}}",
            "details_endpoint": "embedly"
        }
      },
      "details": {
        "mapping": "oembed"
      }
    },
    "tabs": [
        {
            "name": "search",
            "label": "Web",
            "api_endpoint": "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&rsz=8&q={{formdata.query}}"
        },
        {
            "name": "news",
            "label": "News",
            "api_endpoint": "http://ajax.googleapis.com/ajax/services/search/news?v=1.0&rsz=8&q={{formdata.query}}"
        },
        {
            "name": "images",
            "label": "Images",
            "api_endpoint": "http://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&q={{formdata.query}}",
            "results": {
              "mapping": {
                "type":"image",
                "data":{
                  "image": {
                    "src":"{{result.url}}",
                    "href":"{{result.originalContextUrl}}"
                  }
                }
              }
            }
        }
    ]
}


Source properties

Sources are divided into different tabs. Use the 'defaults' property to define properties common to all tabs. For example: source.tabs[0].results = source.tabs[0].results || source.defaults; (Note: If you specify a property both in the defaults and in a tab definition, the defaults will take precedence.)

Note: The name, label, href and icon properties are self-explanatory, and will not be discussed here.


Tab properties

source.tabs = [ {tabObject} ];

The tabs property is an array of tab objects. A tab is an object with the following properties: name, label, api_endpoint, form, results, details.

tab.form

By default, every tab gets a search box that can be accessed in any property with {{formdata.query}} (In the example above: tab.api_endpoint = "http://ajax.googleapis.com/ajax/services/search/images?v=1.0&rsz=8&q={{formdata.query}}".

To disable forms, use the following value: tab.form = { "_default": [] };. If tab.form is disabled, the api_endpoint will be directly called as soon as the tab becomes active.

Forms may also be customized. For example:

"form": {"_default": [{"type": "searchquery", "name":"username", "placeholder": "Enter a App.net username"}]}

In which case, the value of username can be retrieved later on using {{formdata.username}}.

Or you can add advanced options to your form (like we do for the YouTube source):

"form": {
  "advanced":[
            {"label":"Order by","type":"select", "name":"orderby", "options":[
              {"relevance":"relevance"},
              {"published":"published"},
              {"viewCount":"views"},
              {"rating":"rating"}
              ]
            }
          ]},

This will generate the following form: YouTube advanced form

tab.api_endpoint

This endpoint is called when your form is submitted with some values. Construct the URL using the data submitted in the form using a moustache-like syntax: {{formdata.fieldname}}.

For example:

"api_endpoint": "http://gdata.youtube.com/feeds/api/videos?v=2&max-results=20&alt=jsonc&orderby={{formdata.orderby}}&q={{formdata.query}}"

tab.results

This is an important property, and defines how Storify should process the results sent by your api_endpoint. It requires 3 attributes: array, loadMore and mapping.

  • array: specifies where in the JSON the array with the results will be located. For example: json.data.items. Use this attribute to refer to the current result with the result object. For example: {{result.url}}.
  • loadMore: defines the paging system to load more results.
  • mapping: defines the mapping between the result object and a normalized Storify element.

tab.results.loadMore

We support two types of pagination: page based and cursor based.

Page based:

"loadMore": {
    "attribute": "start-index",
    "inc": 20,
    "start": 1 // optional, default: 0
  },

This will append the start-index parameter to the api_endpoint. For example: api_endpoint?start-index=1, then api_endpoint?start-index=21, …

Cursor based:

"loadMore": {
  "cursor": "{{json.cursor.next}}",
  "attribute":"cursor"
},

This will append the cursor parameter to the api_endpoint, with the value defined in the result JSON by {{json.cursor.next}}. Sometimes your api_endpoint does not return the cursor value and you must use a value out of the latest result. You can do that by referring to result instead of json. For example: {{result.timestamp}}.

tab.results.mapping

This is the most important piece in your source definition. You must define the mapping between the data returned by your API and the normalized Storify element.

Common attributes for all element types:

type: "link" | "quote" | "image" | "video",
permalink: "Unique URL to that object on the source website. For example: {{result.url}}",
posted_at: "JavaScript parseable timestamp of the date the object has been created on the source service. For example: {{result.created_at}}",
attribution: {
  "name" : "Name displayed for attribution. For example: {{result.author.name}}",
  "href" : "URL the attribution will link to. For example: {{result.author.profilepage}}"
},
details_endpoint: "api_endpoint to get the details of that object"

The only attribute that depends on the element.type is data:

link

data: {
  link: {
         title: "link title",
         description: "link description",
         thumbnail: "absolute URL to thumbnail",
         href:  "link URL"
  }
}

quote

data: {
  quote: {
         text: "text of the quote"
  }
}

image

data: {
  image: {
         src: "image absolute URL. For example: {{result.src}}",
         href:  "image link"
  }
}

video

data: {
  video: {
         title: "video title",
         description: "video description",
         thumbnail: "absolute url to thumbnail",
         src:  "video URL to be loaded in an iframe"
  }
}

tab.details (optional)

Define a details attribute for your tabs to automatically trigger a new API call to fetch detailed information about the object each time a results object is dropped into a story by a user. This attribute may be defined for your defaults for all tabs, or defined for specific tabs only.

Note: This attribute can also be used as a mechanism to track the number of times one of your objects is added to a story.

details works the same way as the results attribute. You must specify an api_endpoint and an array attribute to specify where in the returned JSON the details of the object will be located. (For example: json.data)

You must also specify the mapping table if different from the one used to map results. Alternatively, you can just specify the string "oembed" in which case we will use the standard oEmbed format to deduce the mapping.

If you omit details, we will fall back on embed.ly to get the maximum amount of information about the object. That's how for example, we can easily turn a SoundCloud permalink into a SoundCloud embed.


Testing your source

There are two ways you can test your source before submitting it to Storify: by URL, or by JSON:

  1. Open the Storify editor.
  2. Open the Javascript console.
  3. Enter s.editor.sourceManager.loadSourceFromURL(AbsoluteURLToYourSourceDefinitionInJSONP); or s.editor.sourceManager.loadSourceFromJSON(YourSourceDefinitionInJSON);.

If everything passed, your new source will appear in the editor.


Submitting your source

Just email [email protected] with your source attached and your api_key. (Request an api_key.)