development.md
This guide is intended to teach you the basics of developing a Privly injectable application.
You should start with the [developer guide](https://priv.ly/pages/develop) before
starting with this guide.
# Quick Start #
If you are developing these applications, the easiest way to begin is to clone
the [Google Chrome Extension](https://github.com/privly/privly-chrome) or
[Mozilla Firefox Extension](https://github.com/privly/privly-firefox) and hack
on the privly-applications directory. That directory has the privly-applications
repository included as a git module. You can also setup a development
environment for the
[Rails-based content server](https://github.com/privly/privly-web) if you
like, but it would be faster if you asked for a development account on dev.privly.org.
For experimentation, we recommend changing the code found in the
PlainPost directory. Please note that PlainPost is intentionally left simple
to make hacking easier.
For more information on how injectable apps are structured, please read below.
# Templating #
When developing Privly applications, you can either edit one of the existing
applications directly, or modify their .subtemplate files. If you use the
templating system, you should look at the `build.py` script for details.
# Directory Structure #
The applications are all in their own directory. The directory is determined by
the name of the application, for instance, the PlainPost app is in the PlainPost
directory. The required directory structure is:
ApplicationName/
show.html
new.html
shared/
css/
common.css
tooltip.css
injected/injected.css
top/top.css
images/ajax-loader.gif
javascripts/
parameters.js
host_page_integration.js
extension_integration.js
network_service.js
tooltip.js
meta_loader.js
vendor/
jquery.min.js
markdown.js
jasmine/
jquery.dataTables.min.js
bootstrap/
The pages may reference any contentApps must expose at least the following two
pages within their application's directory:
* show: The application expects to be associated with pre-existing data found in
the URL referencing it.
* new: The application is being used to generate a new URL that will likely be
shared across the web.
Several JavaScripts are required to ease integration issues and are guaranteed to
be present in the shared directory. Other scripts are optional and are provided
to make certain tasks easier.
* `parameters.js`: Grabs the parameters found on both the query string and the
anchor of the link.
* `host_page_integration.js`: (required) Interfaces the application with the host
page. This is primarily used to resize the height of the application when it is
viewed in the context of other web applications.
* `network_service.js`: Facilitates same-origin requests when served from an
extension or application that permits it. This is generally not used when the
application is hosted by the user's content server.
* `tooltip.js`: (required) Defines a tooltip that gives application metadata when
it is injected into a host page. For the tooltip JavaScript to work, you must
also include the `tooltip.css` in every injectable page.
* `extension_integration.js`: (required) Defines functions for integrating with
extension platforms. This is primarily used for sending hyperlinks to extensions
for their posting to host pages.
* `meta_loader.js`: "show.html" may be shown injected into a page, or as a normal
top level web page. The meta loader checks for special meta tags defining CSS for
specific contexts, which will be loaded dynamically. For more information, see
the JavaScript file's source.
* `jquery.min.js`: Jquery gives better cross-browser support for a number of
operations. Try to live without it if you can, since it can create more overhead
for your application than is necessary. If you don't know what jquery is, then
you probably are going to have difficulty developing an application.
* `markdown.js`: Converts markdown text to HTML. This is primarily used for
previewing content typed in markdown.
* `jasmine/`: Jasmine is a JavaScript testing library. See
[testing](https://github.com/privly/privly-organization/wiki/Testing).
* `jquery.dataTables.min.js`: Datatables provides a table view rendered in JavaScript
that is searchable.
* `bootstrap/`: Bootstrap provides mobile-responsive layout.
The shared CSS folder also has some recommended CSS for apps. The `top` and
`injected` folders contain stylesheets which should only be applied when the page
is viewed as the top application or an injected application, respectably.
# Connecting to Data Storage #
While the application can have a URL that points to a particular server, the
application might not be served from the domain in the URL. Platforms serving the
applications must allow the application to make appropriate same-origin requests.
You can assume same-origin access for all "get" requests when creating a new
link, but the way requests are handled when reading existing content is a bit
more complicated.
* "Get" requests are universally permitted
* All other request types should make an effort to verify that the content
server's data will not be mangled by interacting with your application. This can
be most easily accomplished by storing a string in JSON data that specifies the
list of supported applications.
You should use the `network_service.js` JavaScript library to make all requests
to the remote server since it simplifies requests on several architectures.
Several of the cases are discussed below:
**"Hosted" Apps**
If a user does not have a local version of the application in a browser extension
or on a mobile app, then they will click on the link and be taken to the
content's storage provider. If the application does not support hosted operation
(maybe it doesn't trust the server), then it should display an error message.
Otherwise, the hosted content server should provide fallback operation, which is
possible with clever use of cross-domain requests.
**Extension Apps**
Many platforms allow the developer to make same-origin requests to a content
server. Your application should put in place proper precautions to prevent a URL
attack. For instance, when an extension injects an application into the context
of a social network, the URL may be tied to a server which does not expect your
application's requests. This is a potentially potent attack similar to cross site
scripting. You can mitigate it by requiring the content server to affirm its
support for your injectable application in the initial get request. The best way
to do this is to have data in the get request's response that affirms app
support.
**Mobile Apps**
Mobile applications use an authentication token (essentially an API secret token)
to gain access to the user's account. This is all handled by the
`network_service.js` file without you needing to worry about it. Keep in mind
that if you need an authentication scheme, you will need to support the
auth_token or implement the auth scheme inside the injectable application.
# Integration #
The applications must communicate with a variety of platforms and must support
both posting new content (link creation) and reading existing content (injected
into a host page and served from a content server).
## Posting a New Link ##
An application that is creating a new link should display the link in the UI when
the posting process is complete, as well as fire the event found in
[this specification](https://github.com/privly/privly-organization/wiki/Posting-Application).
Firing the event will cause browser extensions to close the application and
automatically paste the link into a host page.
Since the application may be served from a location other than the server hosting
its data, extensions and mobile apps give the application a parameter specifying
the target domain. This is all handled by `network_service.js` for you.
Make sure the html file is named "new.html." All other functionality for this
posting application is up to the developer.
## Reading a Link Discovered on the Web ##
When viewing an existing application (ie after it has been posted to a host page
like a webmail provider) then it could be viewed in several different contexts.
We are making efforts to abstract away the differences in platforms using the
shared JavaScript files, but you should take note of a few unique aspects of the
different platforms.
Make sure the html file is named "show.html." All other functionality for this
posting application is up to the developer.
### Injected Viewing ###
**Determining whether the App was Injected**
The injected view is triggered whenever the application sees that it is not the
top window. This can be evaluated with this statement:
`window.self === window.top`
When this statement evaluates to true, then the current window (self) is the top
window and it is not in an iframe. Conversely, if it evaluates to false, the
application is being displayed in an iframe. There is a helper in
`host_page_integration.js` that makes this easier.
**Setting the Height of the iframe**
The iframe is protected by what is known as the
[same origin policy](http://en.wikipedia.org/wiki/Same_origin_policy).
This means the host page only knows what your application tells it. Since the
host page controls the height of your application, you must message it the height
of the app. There are helpers for doing this in `host_page_integration.js`. Call
either:
`privlyHostPage.dispatchResize(height)` to resize to a specified height, or
`privlyHostPage.resizeToWrapper()` to resize to the current height of the
application. (note: for this function to work, you must have a div surrounding
the content with the id `privlyHeightWrapper`, this allows JavaScript to get an
accurate representation of the height of the content)
**Layout**
Now that you know your app is injected, you should take steps to ensure that the
layout is non-intrusive to the host page in which it is contained. This currently
means that you should not display much formatting, because there is no way to
know what your surrounding context looks like.
### Top View ###
Either an extension or a hosted server could serve the application as the top
window when reading the content. This is where you have full control of the
application's environment, and need not worry about the strange integration cases
outlined above.
# Platform Specific README #
Several platforms have their own readme with platform specific details. Make sure
you check those out if you are developing on the corresponding platform.