WebPal Cloud Server is a pretty complex platform that can be daunting to explore at first login. With this documentation, we want to make first steps in application development as easy as possible. If you have not done so already, we recommend to read the instructions for basic web site editing and document management first. The next step is to get a good understanding of how WebPal site pages are rendered, followed by some more details on XSL transformations. Last not least, become a WebPal master by learning about extending site with custom code.
WebPal Cloud Server is a Content Management and web application IDE in one. A devout WebPal developer subscribes to the philosophy that content and code can co-exist in a friendly manner.
In our world (of Pals), content editors, marketing professionals and client staff are able to collaborate on content edits while developers peacefully develop new functionality in an agile fashion.
We believe that developers should have access to advanced version control and can utilize their github accounts to synchronize code between multiple WebPal Servers, while content editors should be able to subscribe to the same mechanisms without having to learn the complexity of git commands to keep their content safe.
WebPal Cloud Server manages account and content versions behind the scene to provide a safe environment for all involved.
The first step to develop a custom application is to create a Site. Assuming you have admin access to your WebPal Server, you can create a blank application under Web Sites > New.
To create a blank application without a predefined template, select the "BasicSite" template, and enter a new name for the application.
The new site contains some placeholder content and pages that can easily be replaced. A publish destination is now required while the application is under development.
Only specific users are able to edit a site. You need to add access to the new site even for yourself, the user who just created the site. To do that, select add on the users table and select the users that should be able to access the site.
Note: at this time, there is just one level of access - all or none. We are adding different levels (edit, review, read-only, content-only, etc) in the near future.
You can now switch over to WebPal Sites in order to start development and preview the application.
Now that you have created a basic site, it's time to look a little under the hood. This chapter explains the inner workings of how pages of a WebPal Site are rendered and presented to the browser. This is very useful information if you are planning to extend the site's functionality with your own custom extensions.
WebPal uses Laravel as the model-view-controller (MVC) framework to handle all rendering, routing, and composition of pages within the site. So basically, a WebPal Site is a single, stand-alone Laravel Application. The code and resources for this application are all contained in a single extension called webpal-core, which is the cornerstone of every WebPal Site. Every site has this extension at a minimum.
Webpal-core contains a single core controller, routes, filters, resources, and a few other goodies that are explained further below. Although you can, you typically don't want to modify the webpal-core extension, as each of its functions can be extended or overridden using your own separate custom extension node. This approach will enable you to receive future updates to webpal-core without losing your own code.
In general, all content of a site is stored in hierarchical XML format, which creates a tree structure of nodes that can each be edited individually. Depending on the node's schema-type and other attributes, WebPal opens different editors to accommodate different content structures. What's important to note is that at all times, WebPal maintains nodes and content in perfectly valid XML. This enables us to always parse and transform the content using standard XSL transformations, but later more about that.
Attachments, such as images or other binary files, are stored as documents and referred to by the document file ID as generated by WebPal. This ID is generated every time a new attachment is inserted, pasted, copied or updated.
As mentioned above, all node types have an attribute edittype that determines which editor UI is presented to the user when opening a node for editing. Currently, WebPal supports the following types: html, text, code, form, sheet, data-table, image, string, attributes.
The webpal-core extension contains the schema which defines the structure of a basic WebPal Site:
<web>
<pages>
<page name="home">
<html><h1>Home Page</h1><p>some text</p></html>
</page>
</pages>
<extensions>
<extension name="webpal-core">...</extension>
</extensions>
</web>
This schema is defined by standard XML Schema Definition (XSD) language, which is stored in the extension's core-xsd node.
<element name="web" type="wpWeb"/>
<complexType name="wpWeb" edittype="sheet" icon="world.png" lockable="yes">
<attribute name="name" use="required" type="wpName"/>
<element name="settings" type="wpWebsettings" minOccurs="1" maxOccurs="1"/>
<element name="pages" type="wpPageList" maxOccurs="1"/>
<element name="extensions" type="wpExtensions" maxOccurs="1"/>
</complexType>
<complexType name="wpWebsettings" edittype="sheet" icon="wrench.png" lockable="yes">
<element name="title" type="wpStringL" minOccurs="1" />
<element name="shorttitle" type="wpShorttitle" minOccurs="1" />
<element name="author" type="wpString"/>
<element name="classification" type="wpStringL"/>
...
</complexType>
<complexType name="wpPageList" edittype="sheet" icon="folder_page.png">
<element name="page" type="wpPage"/>
</complexType>
<complexType name="wpPage" edittype="sheet" icon="folder_page.png" lockable="yes">
<attribute name="id" use="auto" type="wpString" value="chap"/>
<attribute name="name" use="required" type="wpPageName"/>
<attribute name="design" use="optional" type="wpString"/>
<attribute name="show-in-menu" use="oneof" type="wpString" value="yes,no,sub-pages-only"/>
<element name="page" type="wpPage"/>
<element name="title" type="wpStringL"/>
<element name="shorttitle" type="wpShorttitle" minOccurs="1"/>
<element name="html" type="wpPageHtml"/>
<!-- other possible child nodes here -->
</complexType>
It is thus possible to define entirely new node schemas that allow you to create virtually any content structure
Finally, webpal-core contains two nodes core-xsl and core-theme, which contain XSLT transformation stylesheets. XSL is a language which defines templates for transforming XML from one structure to another. XSL is a rule-based, not a functional language, and as such is ideally suited for the hierarchical nature of transforming XML content. It also offers convenient output switches to produce HTML and XHTML output formats.
For example, let's say we have a simple web site with this structure:
<pages name="pages">
<page name="home">
<shorttitle>Home</shorttitle>
<html>
<h1>Home</h1>
<p>Lorem ipsum dolor sit amet... </p>
</html>
</page>
<page name="offers">
<shorttitle>Our Offers</shorttitle>
<page name="offer1">
<shorttitle>Offer 1</shorttitle>
</page>
<page name="offer2">
<shorttitle>Offer 2</shorttitle>
</page>
<page name="offer3">
<shorttitle>Offer 3</shorttitle>
</page>
</page>
<page name="contactus">
<shorttitle>Contact Us</shorttitle>
</page>
<page name="common">
<shorttitle>Common</shorttitle>
</page>
</pages>
Then the following templates will render a basic navigation for this site. This template can be invoked in a number of different ways, later more about this. For now, let's assume that any XML content can be accessed and re-purposed for any page that is presented to the browser.
<xsl:template match="page" mode="navigation">
<xsl:param name="path" select="''"/>
<li>
<a href="{$path}/{@name}"><xsl:value-of select="shorttitle"/></a>
</li>
<xsl:if test="page">
<ul>
<xsl:apply-templates select="page" mode="navigation">
<xsl:with-param name="path" select="concat($path, '/', @name)"/>
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:template>
For more information on XSLT processing, we recommend you check out http://www.w3schools.com/xml/xsl_intro.asp
By default, all XSL transformations start with the root node of any input XML document as their main entry point of template matching. WebPal-core changes that by injecting any desired context node as the node to be processed. This node is located using an XPath expression.
For example, let's say we want to render the home page of the sample site above. The corresponding XPath expression for the page to be rendered would thus be:
/web/pages/page[@name='home']
Webpal-core provides a single controller that bundles a number of methods for basic page-rendering functions. Among these is the Core::render() method which can be invoked as such:
\WebpalCore\Controllers\Core::render(
array(
'xpath' => "/web/pages/page[@name='home']"
));
Generally, WebPal tries to store the entire web site in a single XML document for convenient XSLT processing. Naturally, for large web sites, this document can become rather large. This scaling concern is even larger for multi-lingual web sites which house duplicate version of the same content in different web sites or for different localities.
WebPal Sites use a multi-level caching cascade to optimize rendering performance of the final end-product, the page views. Additionally, the XML content is pre-processed, splitting the content into multiple copies for different languages. This pre-process is actually done by an XSLT transformation, too.
Lastly, the end-result of the final view computation is cacheable again by Laravel or edge-side proxy mechanisms, such as squid, redis, memcached, or mod_proxy.
Summarizing, the Core::render() method follows these steps:
WebPal Sites have a simple mechanism to extend their functionality. Extensions nodes can be inserted under the extensions node. Palomino maintains a number of publicly available extensions on github which can easily be integrated into your web application project.
To import an extension into your site, select Import/Export > Import from Repository... from the context menu of the extensions node:
To import (or clone) an extension from github into your site, all you need to supply is the SSH address of the extension's repository. You can copy this address from github using the "Clone with SSH" function:
Paste this address in the Import Node from Repository dialog. For convenience, the SSH key of your user account is supplied in this dialog. For public github repositories, this key is not needed, however it is necessary to access a private repository, such as on your own github account.
Once imported, the extension is visible as a new extension node. This node includes different components (resources, controllers, stylesheets, routes, and database), which usually do not need to be edited.
Most extensions supply additional nodes, which can be inserted on your site's pages.
WebPal stores all app content as XML, and, offers XSLT transformations to process the content into HTML. This model allows you to maintain both content and templating instructions in a single user interface.
Even moreso, since XSL is itself a language that can be written in XML, it is maintained within the tree structure of a web site. There are a number of advantages to this combined storage of content and templates, last not least being the ability to extend templates very easily.
This chapter assumes you have an basic knowledge of the XSL and the XSLT templating language. We recommend some reading at http://www.w3schools.com/xsl/
Below a visual diagram of how the UI maintains content (XML) and templates (XSL) jointly and the XSLT processor uses the XSL instructions to generate a blade template for the Laravel App to consume. So, in short, WebPal uses:
The webpal-core extension supplies a set of default XSL templates to the processor, which can be overridden by supplying additional templates with a higher priority value. This means that as a CMS developer, you can use XSLT templates to define how static content is rendered on the current page to display.
During the Transformation step of a page load, a new XSLT Processor object is created which is initialized with all XSL stylesheets and templates defined within the Site extensions. XSL stylesheets are added to an extension simply by selecting Insert new... > stylesheet for the extension node.
After initializing, the processor by default applies templates starting with the root node of the site XML document, with is the top-level web node. Since this is in most cases not the desired behaviour, the Core::render() controller method injects the desired starting node using the $NODE parameter, and then applies the best matching template in the mode page-template.
<xsl:template match="/" priority="0.1">
<xsl:if test="$NODE">
<xsl:for-each select="$NODE">
<xsl:apply-templates select="." mode="page-template"/>
</xsl:for-each>
</xsl:if>
</xsl:template>
To override the default page-template supplied by webpal-core, you can supply custom templates with a higher priority value, like so:
The template should generate the HTML for an entire page, including the html, body and head tags:
Now let's create a couple of simple templates that render the pages in a two-column layout, and adds a nested list of navigation links in the left sidebar.
<xsl:template match="/web/pages//page"
mode="page-template"
priority="0.5">
<html lang="en">
<head>
<title>Page Title</title>
</head>
<body>
<table>
<tr>
<td colspan="2">
<h1>Basic WebPal Site</h1>
</td>
</tr>
<tr>
<td style="width:300px; vertical-align: top; padding: 10px;">
<ul>
<xsl:apply-templates select="/web/pages/page" mode="navigation"/>
</ul>
</td>
<td style="width: 600px; padding: 20px;vertical-align: top; ">
<xsl:apply-templates select="*"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template match="page" mode="navigation">
<xsl:param name="path" select="''"/>
<li><a href="{$path}/{@name}"><xsl:value-of select="@name"/></a></li>
<xsl:if test="page">
<ul>
<xsl:apply-templates select="page" mode="navigation">
<xsl:with-param name="path" select="concat($path, '/', @name)"/>
</xsl:apply-templates>
</ul>
</xsl:if>
</xsl:template>
The first template defines a simple page with a table-based layout, and does two things:
The second template recursively builds a simple navigation menu with links to all pages of the site. View the result by navigation to the "home" page and tapping "Preview".
Let's say that for all pages other than the home page, you want to use a 3-column layout. To achieve this without modifying your existing templates, you can add another template with higher specificity like so:
<xsl:template match="page[@name != 'home']"
mode="page-template">
<html lang="en">
<head>
<title>Basic WebPal Site</title>
</head>
<body>
<table>
<tr>
<td colspan="3">
<h1>
Basic WebPal Site
</h1>
</td>
</tr>
<tr>
<td style="width:300px; vertical-align: top; padding: 10px;">
<ul>
<xsl:apply-templates select="/web/pages/page" mode="navigation"/>
</ul>
</td>
<td style="width: 600px; padding: 20px;vertical-align: top; ">
<xsl:call-template name="render-content"/>
</td>
<td style="width:300px; padding: 20px; vertical-align: top; ">
<xsl:apply-templates select="/web//page[@name='common']/html"/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
This template will take precedence over the default behaviour for all page nodes that don't have a @name of 'home' and results in this layout:
Obviously, the same can also be achieved in other ways. For example, the default behaviour can be defined as a 3-column layout, and only the home page uses a different template.
You can define a template priority like so:
<xsl:template match="page" mode="page-template" priority="0.75">
The priority attribute ranges from 0.0 to 1.0, with 0.5 being the default. The default priority for all templates is 0.5. The webpal-core extension uses priority 0.1 for it's default templates.
The XSL standard prescribes that named templates of the form <xsl:template name="some-template"> need to be globally unique for the entire stylesheet.
Since there are a number of templates provided by other extensions, a naming convention of pre-pending the extension name is recommended, for example my-custom-ext--my-named-template.
The webpal-core extension defines a set of default routes for rendering pages and serving resources. These are tested against the URL in order, and the first matching route is invoked. WebPal allows editing of routes with a convenient table view in order to manage larger numbers of routes easily.
You can add custom routes by inserting a routes node in an extension like so:
Routes are matched against the URL in order specified, which means that to override existing routes, your custom extension should be placed before the webpal-core extension in order for the custom routes to be matched first.
A pre-defined route can be used to call specific named templates. For example, assume you wrote a template as such:
<xsl:template name="my-custom-sitemap">
<html>
<h1>Sitemap</h1>
<xsl:apply-templates select="/web/pages/page" mode="navigation"/>
</html>
</xsl:template>
you can now invoke it with this route:
/call-template/my-custom-sitemap
Caution: this default route allows anyone to invoke your named templates. Thus, all named templates should ensure that they do not expose the entire XML or any protected pages to the browser. For example, the following named template copies the entire XML, including settings, and code nodes, to the browser.
<xsl:template name="not-a-good-template">
<xsl:copy-of select="/web" />
</xsl:template>
If you need to disable any of the default routes, simply override them with new behaviour in your custom extension. For example the following route will redirect the call-template URL to the home page:
Any site extension can supply it's own set of resources, such as images, CSS or Javascript files. The webpal-core extension only supplies a minimal set of default resources, as we attempted to make the core as light-weight as possible.
All resource files are managed under the resources node - which behaves exactly like a document folder. Files here can be edited (double-click to open), copied, deleted, and so on. Adding a file here allows you to reference it via a special resource route (which is defined in webpal-core):
<link rel="stylesheet" href="/resources/ext/CamelBackExtensionName/path/to/resource.css" />
Note the CamelBack version of the extension name. To achieve a consistent and compatible naming convention for storage of extension resources and the equivalent namespaces, extension names are converted to CamelBack - ignoring all non-alpha characters and treating them as separators instead.
In many cases, you want the page templates to includes additional resources supplied by the extensions without having to declare these each time in the page-template. To auto-include required resource links, you can declare these resources in the resource-links node, like so:
The link pathname to be supplied should not be absolute. For example, if the resources file mystyles.css is in the css folder under the resource node, then the pathname to supply is:
css/mystyles.css
Note that this also allows you to declare external resources, simply by supplying the full URL instead of the relative pathname, for example:
https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css
Once required resource links are defined by your extensions, you can generate the include statements with a single call to include-extension-styles and include-extension-scripts templates pre-defined in webpal-core:
<xsl:template match="/web/pages//page"
mode="page-template">
<html lang="en">
<head>
<title>Basic WebPal Site</title>
<!-- include all styles -->
<xsl:call-template name="include-extension-styles"/>
</head>
<body>
...
</body>
<!-- include all scripts -->
<xsl:call-template name="include-extension-scripts"/>
</html>
</xsl:template>
This template generates <link> and <script> tags for all resource links, in document order.